元组和引用元组的 Rust 参考

问题描述 投票:0回答:3

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 中表达这些“匹配性”?

rust reference tuples type-coercion
3个回答
4
投票

简单的答案是

&(i32, i32)
(&i32, &i32)
不是同一类型,但这可能不是您要寻找的详细程度。


如果从内存布局的角度来想,或许更容易理解why。 cheats.rs 有一些优秀的插图,我会在这里无耻地盗用:

这里重要的是,无论您在元组中存储什么,元素都是总是连续的*请参阅内存中的评论。对于拥有的类型和引用也是如此,只是当元素是引用时,它指向的内存不一定是连续的。

这就是为什么

&(T, U)
(&T, &U)
之间的转换是不平凡的。在前一种情况下,您有一个
T
U
“捆绑”(意味着它们在内存中物理上在一起),并且引用指向“捆绑”。在后一种情况下,您拥有一个
&T
&U
的“捆绑”(这意味着引用在内存中物理地在一起,但它们可能各自指向他们想要的任何地方)。


修正案:

是的,您可以从

(&T, &U)
创建一个
&(T, U)
,因为您当然可以从
&T
获得
&U
&(T, U)
。然而,我还不知道有任何流行的 crate 提供这种方便的方法。也许我可以写一个。


2
投票

虽然

&(a, b)
解构为两个都是
&i32
的变量,但这与具有
(&i32, &i32)
类型的元组不同。

您可以使用

let f: TupleOfRef = ( &e.0, &e.1 );

使第二个示例工作

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);

游乐场

© www.soinside.com 2019 - 2024. All rights reserved.