Reputation: 7096
I could not find any reference to this in Constructors - The Rustonomicon. Is it guaranteed that the following code…
struct Mutates {
n: usize,
}
impl Mutates {
fn side_effects(&mut self) -> usize {
self.n += 1;
self.n
}
}
#[derive(Debug)]
struct Struct {
a: usize,
b: usize,
}
fn main() {
let mut m = Mutates { n: 0 };
// note the order of the fields
dbg!(Struct {
a: m.side_effects(),
b: m.side_effects(),
});
dbg!(Struct {
b: m.side_effects(),
a: m.side_effects(),
});
}
…will always print the following?
[src/main.rs:22] Struct{a: m.side_effects(), b: m.side_effects(),} = Struct {
a: 1,
b: 2,
}
[src/main.rs:26] Struct{b: m.side_effects(), a: m.side_effects(),} = Struct {
a: 4,
b: 3,
}
Or is it possible for the compiler to assign different values?
Note that the question is about the order in which fields are initialized, not declared.
Note that this question is specifically asking about structs and not tuples, which is answered by What is the evaluation order of tuples in Rust?.
Upvotes: 15
Views: 2046
Reputation: 2359
It's documented in The Rust Reference here that:
Expressions taking multiple operands are evaluated left to right as written in the source code.
This explicitly includes struct expressions. The documentation PR was https://github.com/rust-lang/reference/pull/888, which closed issue https://github.com/rust-lang/reference/issues/248 mentioned in Lonami's answer.
Upvotes: 3
Reputation: 7096
After lurking through Rust's issues, I came across rust-lang/reference - Document expression evaluation order, which links to an IRLO thread on Rust expression order of evaluation where user fweimer posts:
What’s the current state regarding order of evaluation? It is very tempting to write this:
struct Item { a: u32, b: u32, } impl Item { fn receive_word(&mut self) -> Result<u32, Error> { … } fn receive(&mut self) -> Result<Item, Error> { Ok(Item { a: self.receive_word()?, b: self.receive_word()?, }) } }
The expectation is that first the value a is received, then the value b. But with a non-determinate evaluation order, one has to introduce temporaries.
To which nikomatsakis responds:
That code is correct and there is no chance it will change. In fact, I’m more or less of the opinion that the ship has sailed with respect to making changes to order of evaluation, period.
And soon after adds:
More to the point, in a struct literal, the fields are evaluated in the order you write them; if a panic occurs before the struct is completely build, the intermediate values are dropped in the reverse order (once the struct is fully built, the fields are dropped in the order they are written in the struct declaration, iirc).
Which adds to the "official statement" I was looking for, similar to Heptamerical's answer.
Upvotes: 1
Reputation: 106
Yes, it's guaranteed. Ralf Jung, a compiler team contributor confirms it on Zulip:
Is the order in which struct fields are initialized guaranteed?
RalfJ:
yes -- it's always the order in which you write the fields in the initializer
the order of the fields in the struct definition is irrelevant
Upvotes: 9
Reputation: 4288
Yes, because changing it would be a breaking change:
struct Foo(usize);
impl Foo {
fn make_val(&mut self) -> usize {
self.0 + 20
}
}
struct Bar {
a: Foo,
b: usize,
}
let mut foo = Foo(10); // Not copy or clone.
// let bar = Bar { //Wouldn't work since `foo` is moved into a.
// a: foo,
// b: foo.make_val(),
// };
let bar = Bar {
b: foo.make_val(),
a: foo,
}
And we can also observe that order of fields on the instantiation side changes the order in which the values are semantically built. Playground.
#![allow(dead_code)]
struct Bar;
impl Bar {
pub fn new(val: usize) -> Self {
println!("Making {}", val);
Bar
}
}
struct Foo {
a: Bar,
b: Bar,
c: Bar,
}
fn main() {
Foo {
a: Bar::new(0),
c: Bar::new(1),
b: Bar::new(2),
};
}
Which prints
Making 0
Making 1
Making 2
Upvotes: 2