I tried this code:
mod module {
#[derive(Default)]
pub struct S {
pub public_field: u32,
_private_field: u32,
}
}
use module::S;
pub fn f() -> S {
S {
public_field: 42,
..S::default()
}
}
I expected to see this happen: code compiles successfully.
Instead, this happened: rustc throws an error
error[E0451]: field `_private_field` of struct `module::S` is private
--> <source>:14:11
|
14 | ..S::default()
| ^^^^^^^^^^^^ field `_private_field` is private
As a user, I field this confusing, because I'm not trying to access or set the private field explicitly anywhere in the literal.
Applies to all known rustc versions.
I understand why this happens in principle, if the literal is desugared literally (no pun intended) to:
let s_rest = S::default();
let s = S {
public_field: 42,
_private_field: s_rest.private_field,
};
However, it seems that this code could be equally expanded to:
let mut s = S::default();
s.public_field = 42;
let s = s;
This way an immutable literal could be used with many more kinds of structures without users manually resorting to the temporarily-mutable variable as in the 2nd example.
Do I miss some other difference between the two that would break some existing usecases if the implementation is changed?
Do I miss some other difference between the two that would break some existing usecases if the implementation is changed?
The desugaring change would break existing use cases, yes.
See https://internals.rust-lang.org/t/proto-rfc-expanded-functional-record-update/3077/2.
@petrochenkov Interesting. It's hard to understand from the branch code, but is desugaring in your experiment same as I have shown above? I don't really understand why Drop would be an issue here, since I'm not trying to move any fields out.
I don't really understand why Drop would be an issue here, since I'm not trying to move any fields out.
Tried to add Drop to my S and the desugaring still works as expected. Could you please provide an example code that would break?
This discussion also recently came up on IRLO again: https://internals.rust-lang.org/t/pre-rfc-relaxed-non-exhaustive-structs/11977
Tried to add Drop to my S and the desugaring still works as expected. Could you please provide an example code that would break?
As the linked comment says: "FRU from borrowed contexts, you can鈥檛 move the whole value, but can copy individual Copy fields"
So, try a situation where the thing after the .. is borrowed, not owned. This commit has some examples.
@RalfJung Ah, I see, haven't realised it's possible. It could be interesting to enable this at least for owned case, but I suppose that's what makes feature request more nuanced (which explains previous forum discussions).
I'll just add to this that if we could use the alternative desugaring, this would also make functional record update syntax work with #[non_exhaustive] types as well, which would be _really_ nice.
Would it be feasible to make this work without fundamentally changing the desugaring? I.e. somehow "ignore" the non-exhaustiveness of the struct (and privacy of implicitly-copied / -moved fields) when it is used with FRU?
Also either this or #63538 should probably be closed as a duplicate.
Most helpful comment
I'll just add to this that if we could use the alternative desugaring, this would also make functional record update syntax work with
#[non_exhaustive]types as well, which would be _really_ nice.