The problem with this is the memory layout where the tuple slice/sub tuple may not have the same memory layout as it's parent so creating a new tuple may not be what's wanted here.
I think that it can be solved by marking which tuple element/offset remains accessible through a variable. Not sure if such metadata can exist in the compiler (if it's possible I definetely will write an rfc).
If marking which offsets/members are accessible/not accessible (by move/ref/mut) this can also work with structs
Pseudo code to illustrate this
fn takes_refs(string: &mut String, nb: &mut i32);
fn takes_tuple(t: (&mut String, &mut i32));
let (_, rest @ .., _) = &mut (1, String::new("hello"), 12, 13);
takes_refs(rest); // rest is an alias to a mutable string and a mutable i32
takes_tuple((..rest)); // tuple created with members of rest
// only 2 fields of the original tuple can be used by rest
let (a, b): (&mut String, &mut i32) = rest;
// or
let { 0: a, 1: b } = rest;
Similar functionality for structs, member names can be used
(this could be an evolution after full tuple bindings)
struct Foo { field1: u8, field2: u8, field3: String }
struct Bar<'a> { field2: &'a u8, field3: &'a String }
fn takes_refs(field2: &u8, field3: &String);
fn takes_bar<'a>(bar: Bar<'a>);
let Foo {_field1, rest.. } = &Foo { ... };
takes_refs(rest);
takes_bar(Bar{..rest})
// only field2 and field3 are marked as accessible through rest
let { field2, field3 } = rest;
I understand that it has a different memory layout, but I fail to see how it is a problem when it produces a tuple with references to the individual elements.
You can think of my example above as being syntactic sugar for
That seems pretty intuitive to me, and is much easier than introducing a kind of "tuple slice". And it will likely be more performant, since a bunch of references is easier to optimize than a special metadata section. It is also trivial for borrowcheck to reason about. It doesn't require any new, complicated concepts that will make the language harder to learn.
I suggested to make it a tuple of references instead, so rest == (&3u8, &3i32). This is possible. Whether it is a good idea is another question, because it isn't entirely consistent with how patterns work today.
This is, btw, similar to how currently mut doesnât distribute over tuple patterns so you canât write mut (a, b) (which I at least try to do every once in a while) but have to write (mut a, mut b).
If one wants to bind a tuple of non-references to a tuple of references, itâs currently possible via tup @ (ref _0, ref _1) (unfortunately you can't use _ here for some reason). So, should people be against changing the semantics of ref, maybe a good syntax to bind a rest-tuple to a tuple of res would be
(a, rest @ ref.., b)
and the same should work for mut and ref mut, too. Note that
(a, rest @ (ref..), b)
would then automatically also work, and bind a tuple within a tuple rather than a rest-tuple.
Ough, thanks. I thought I had double-checked it on the playground, but I had a brain fart in my test program⌠And yeah, it wouldn't even make much sense if ref _0 somehow also modified the binding mode of rest