如何将引用的元组转换为元组的引用?

10

我想将一个引用元组(所有引用都是同一个结构体成员的引用)转换为元组的引用。

我尝试了各种方式来强制转换它们,但是我无法做到不克隆就完成转换。

struct Bar();

struct Foo(Bar, Bar, Bar);

fn main() {
    let a: &Foo = &Foo(Bar(), Bar(), Bar());
    let b: &(Bar, Bar) = &(a.0, a.1);
}

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:7:28
  |
7 |     let b: &(Bar, Bar) = &(a.0, a.1);
  |                            ^^^ cannot move out of borrowed content

error[E0507]: cannot move out of borrowed content
 --> src/main.rs:7:33
  |
7 |     let b: &(Bar, Bar) = &(a.0, a.1);
  |                                 ^^^ cannot move out of borrowed content

假设 a 的类型是 &Foo,我期望 b 的类型为 &(Bar, Bar)


1
你很可能正在经历一个XY问题。几乎肯定有更好的方法来解决导致你提出这个问题的根本问题。 - Shepmaster
1个回答

17

这是不可能的。

一个引用指向一个值。你想要一个&(Bar, Bar),但是内存中没有一个2元组(Bar, Bar)。你不能引用不存在的东西。

&(A, B)(&A, &B)的内存布局根本不兼容,因此您也无法使用不安全的Rust技术。


在这种特殊情况下,您也许可以使用不安全的Rust直接将&Foo转换为&(Bar, Bar),但是...

  • 它要求元组结构体和元组的布局相同;我不知道是否能保证1
  • 它要求元组结构体的布局紧密打包,以便您可以通过成员大小偏移来到达下一个成员;我不知道是否能保证1
  • 它要求元组结构体的布局按照定义的顺序放置成员;我不知道是否能保证1
  • 您只能对连续的片段进行操作;不能获取第一个和第三个项
// I copied this unsafe block from Stack Overflow
// without properly documenting why I think this code is safe.
let b: &(Bar, Bar) = unsafe { &*(a as *const Foo as *const (Bar, Bar)) };
println!("{:?}", b);
// I copied this unsafe block from Stack Overflow
// without properly documenting why I think this code is safe.
let c: &(Bar, Bar) = unsafe {
    let p = a as *const Foo as *const Bar;
    let p = p.offset(1);
    &*(p as *const (Bar, Bar))
};
println!("{:?}", c);

1 — 实际上,文档明确说明

元组没有其布局的任何保证。

唯一的例外是单元素元组 (()),它保证作为零大小类型具有大小为 0 和对齐度为 1。

这意味着,虽然这段代码可能会打印出你期望的结果并且Miri不会抱怨,但这是未定义的行为。


如果元组的元素是Copy,例如(&usize, &bool),那么它应该可以被复制为(*tup.0, *tup.1)。当在HashMap上进行迭代时可能会出现这种情况,其中键值对被生成为(&K, &V),即使KVCopy,也没有好的方法将其转换为(K, V) - BallpointBen
@BallpointBen 是的,但这并不能回答问题“如何将引用元组转换为元组引用?” 这将创建一个拥有的元组。然后,您可以对新创建的元组取一个引用,但在您提到的迭代器链情况下,它不起作用,因为没有任何东西拥有该元组。我同意拥有一个函数,例如(&A,&B)->(A,B)其中A:Copy,B:Copy或等效于Clone,但我敢打赌像这样的东西会等到可变元组之类的东西存在。 - Shepmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接