Search code examples
rustreferencetuplestype-coercion

Rust Reference of Tuple and Tuple of Reference


What is the relationship between reference of tuple and tuple of reference as types?
Why does the first works but the second doesn't?

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;

To make my point of question more clear. It is more about the relation of type (&'a A, &'a B) with (A, B).

Thought the memory layout of tuple is not guaranteed, it is clear that can't make &(A, B) out of &A and &B without cloning, since there is no one memory address holding A and B.

However, making (&A, &B) out of (A, B) makes some sense, since we have not only the address of tuple, (namely, &(A, B)), but also at the addresses of its elements, (namely, &A and &B, as @etchesketch mentioned). And this seems work in the first case of the example above, but not in the second.

Actually the second is what I want. Is there anyway to (&A, &B, ..) out of owned (A, B, ..) in general? Or is there any good way to express these 'matchability' in trait bound?

Following question: Tuple of Reference and Reference of Tuple for type level operation


Solution

  • If you do that a lot, you can make it easier with a trait:

    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:?}");
    }
    

    Playground

    The downside is that you first need to implement the trait for all possible tuple sizes (although it can be simplified with a macro):

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

    Playground