Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.6k views
in Technique[技术] by (71.8m points)

rust - How to convert a tuple of references to a reference of a tuple?

I'd like to convert a tuple of references (which are all references to members of the same struct) to a reference of a tuple.

I've tried to coerce them in various ways, however I wasn't able to do it without cloning.

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

I expect b to be of type &(Bar, Bar) given a is of type &Foo.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This is impossible.

A reference refers to a value. You wish to have a &(Bar, Bar) but there is nowhere in memory that has a 2-tuple of (Bar, Bar). You cannot refer to something that does not exist.

The memory layouts of &(A, B) and (&A, &B) are fundamentally incompatible, so you cannot use unsafe Rust techniques either.


In this particular case, you might be able to use unsafe Rust to convert your &Foo directly to a &(Bar, Bar), but...

  • it requires that the layout of a tuple struct and a tuple be the same; I don't know that's guaranteed1
  • it requires that the layout of a tuple struct be tightly packed such that you can offset by the member size to get to the next one; I don't know that's guaranteed1
  • it requires that the layout of the tuple struct places the members in the same order they are defined; I don't know that's guaranteed1
  • you can only do it for sequential pieces; no getting the first and third item
// 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 — In fact, the reference explicitly states:

Tuples do not have any guarantees about their layout.

The exception to this is the unit tuple (()) which is guaranteed as a zero-sized type to have a size of 0 and an alignment of 1.

This means that while this code may print out what you expect and Miri does not complain, it's undefined behavior.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...