reference of tuple 和 tuple of reference as types 之间有什么关系?
为什么第一个有效而第二个无效?
let a = 1;
let b = 2;
// This works, c: &i32, d:&i32
let (c, d) = &(a, b);
type TupleOfRef<'a> = (&'a i32, &'a i32);
let e = (a, b);
// This doesn't
let f: TupleOfRef = &e;
为了让我的问题更清楚。它更多是关于类型
(&'a A, &'a B)
与(A, B)
的关系。
以为元组的内存布局是不能保证的,很明显,如果不克隆,就不能从
&(A, B)
和&A
中得到&B
,因为没有一个内存地址保存A
和B
。
但是,从
(&A, &B)
中生成 (A, B)
是有道理的,因为我们不仅有元组的地址(即 &(A, B)
),而且还有其元素的地址(即 &A
和&B
,正如 @etchesketch 提到的那样)。这似乎适用于上面示例的第一种情况,但不适用于第二种情况。
其实第二个才是我想要的。总的来说有
(&A, &B, ..)
出拥有(A, B, ..)
吗?或者有没有什么好的方法可以在 trait bound 中表达这些“匹配性”?
简单的答案是
&(i32, i32)
和(&i32, &i32)
不是同一类型,但这可能不是您要寻找的详细程度。
如果从内存布局的角度来想,或许更容易理解why。 cheats.rs 有一些优秀的插图,我会在这里无耻地盗用:
这里重要的是,无论您在元组中存储什么,元素都是总是连续的*请参阅内存中的评论。对于拥有的类型和引用也是如此,只是当元素是引用时,它指向的内存不一定是连续的。
这就是为什么
&(T, U)
和 (&T, &U)
之间的转换是不平凡的。在前一种情况下,您有一个 T
和 U
“捆绑”(意味着它们在内存中物理上在一起),并且引用指向“捆绑”。在后一种情况下,您拥有一个 &T
和 &U
的“捆绑”(这意味着引用在内存中物理地在一起,但它们可能各自指向他们想要的任何地方)。
修正案:
是的,您可以从
(&T, &U)
创建一个&(T, U)
,因为您当然可以从&T
获得&U
和&(T, U)
。然而,我还不知道有任何流行的 crate 提供这种方便的方法。也许我可以写一个。
虽然
&(a, b)
解构为两个都是 &i32
的变量,但这与具有 (&i32, &i32)
类型的元组不同。
您可以使用
let f: TupleOfRef = ( &e.0, &e.1 );
使第二个示例工作
如果你经常这样做,你可以通过一个特质让它变得更容易:
trait TupleOfRefs<'a> {
type Output: 'a;
fn as_tuple_of_refs (&'a self) -> Self::Output;
}
impl<'a, A: 'a> TupleOfRefs<'a> for (A,) {
type Output = (&'a A,);
fn as_tuple_of_refs (&'a self) -> Self::Output {
(&self.0,)
}
}
impl<'a, A: 'a, B: 'a> TupleOfRefs<'a> for (A, B) {
type Output = (&'a A, &'a B);
fn as_tuple_of_refs (&'a self) -> Self::Output {
(&self.0, &self.1)
}
}
fn main() {
let a = 1;
let b = 2;
let t = (a, b);
let r = t.as_tuple_of_refs();
println!("{r:?}");
}
缺点是你首先需要为所有可能的元组大小实现特征(尽管它可以用宏简化):
macro_rules! make_tuple_of_refs {
($($t:ident $i:tt),*) => {
impl <'a, $($t: 'a),*> TupleOfRefs<'a> for ($($t,)*) {
type Output = ($(&'a $t,)*);
fn as_tuple_of_refs (&'a self) -> Self::Output {
($(&self.$i,)*)
}
}
}
}
trait TupleOfRefs<'a> {
type Output: 'a;
fn as_tuple_of_refs (&'a self) -> Self::Output;
}
make_tuple_of_refs!(A 0);
make_tuple_of_refs!(A 0, B 1);