Why is it possible to pass mutable reference to a temporary object / rvalue / literal?

struct Foo {
    field: bool,
}

fn foo(i: &mut u32) {
    *i = 421; // what exactly was changed here?
}

fn bar(f: &mut Foo) {
    f.field = false;
}

fn main() {
    bar(&mut Foo { field: true }); // Passing temporary object
    foo(&mut 42); // even worse!
}

Such things are explicitly banned in C++. However, Rust compiles them. Why so?

In most cases, any unbound values are dropped at the end of the statement.

bar(&mut Foo { field: true });
//       Foo is dropped here ^

Rust doesn't let you use the value after that, for example returning &mut Foo from bar and trying to bind it to a variable would be an error.

Some related things are temporary lifetime extension and for shared references, constant promotion but these aren't happening in this specific situation.

3 Likes

Thank you.

So, the idea is Rust moves this object (be it struct or a fresh variable created from 42) to this function much like && does in C++, and this is why it works.

The behavior is as you describe, but the object is not moved to the function, it is owned by the function call expression, i.e., it is a temporary. A mutable reference to that temporary is moved to the function parameter.

It is possible to move an object to a function parameter, but this is done by omitting the & or &mut in the parameter type. When the parameter type is not a reference, the object is moved or copied, depending on whether its type implements the Copy trait. All this is also true for assignment to a field or local variable.

1 Like

thank you.

I now finally got it. The object lives to the end of the line (as any temporary object except the one prolonged with constant ref) and function borrows it.

In C++ that might lead to problem since function might return reference to the object, but since Rust guarantees that this reference doesn't outlive this object it is not problem at all. Code is safe. Hence, it is compiled.

1 Like

Good point!