Towards even smaller structs

I mentioned this earlier in the thread: Towards even smaller structs - #9 by comex

Stated more clearly, the expression foo.bar, if bar is a #[flat] field, would copy bar out of the object and produce an rvalue of whatever type it is. Then Rust's existing rules for taking references to rvalues would apply. This can be simulated in current Rust by adding braces – {foo.bar} – which forces the expression to be an rvalue.

struct Foo { bar: i32 }
let foo = Foo { bar: 10 };

&{foo.bar};

  ^ OK, copies to the stack and takes a reference to that

std::ptr::addr_of!({foo.bar});

  ^ error[E0745]: cannot take address of a temporary

&mut {foo.bar};

  ^ Currently no error or warning, but probably should be an error when using flat fields without braces (heck, it should probably be a warning in all cases). It copies to the stack and only mutates the copy, not the original.

So the limitations are:

  • The field type has to be Copy for this to make sense.
  • Since &foo.bar points to a stack copy, its lifetime is not the same as the lifetime of foo. Any function like
fn get_bar(foo: &Foo) -> &i32 { &foo.bar }

         will obviously not work.

  • You can't usefully take a mutable reference. Though, I suppose the compiler could allow mutable references by making a stack copy, then automatically writing the modified value back to the original at the point the stack copy goes out of scope. But that would be going beyond "just make it an rvalue", it would require disabling NLL for those references, and I'm not sure if it's useful enough to justify the special case.

Still, it means that simple use cases, like calling &self methods on #[flat] fields, would 'just work'.

3 Likes