Consider the following three functions, which differ only in the second line:
fn foo() {
let mut a = 1;
let ref b = if true { a } else { 0 };
let c = &mut a;
*c += 1;
dbg!(b);
}
fn bar() {
let mut a = 1;
let b = if true { &a } else { &0 };
let c = &mut a;
*c += 1;
dbg!(b);
}
fn baz() {
let mut a = 1;
let ref b = a;
let c = &mut a;
*c += 1;
dbg!(b);
}
These all appear to do the same thing: create a reference, create and use a mutable reference to the same data, then use the original reference, which of course should be a compile time error. However, bar() and baz() give a compiler error as expected, but foo() instead makes b point to an implicit copy of a, so the program runs and prints b = 1. Seeing how foo() is halfway between bar() and baz(), this behavior took me by surprise and lead to a difficult to find bug.
I can kind of understand why this is happening: in foo(), b is a reference to the result of the if expression, and the if expression is taking the arguments a and 0 by copy. So I guess the behavior is logical. My initial reaction was that this was a mistake in Rust that should be fixed. It would have been nice if there was an easier way to find this, but I guess I understand it now. What are your thoughts?
The thing between let and = is a pattern and this is just a normal part of pattern syntax.
Usually you'll see it in code which takes part of an object by reference (e.g. a String field) to avoid moving, while others can safely be bound by value because they are Copy.
let Person { ref name, age } = ...;
// or
let result = expensive_operation();
if let Ok(Person { ref name, age }) = result {
println!("{} is {} years old", name, age);
}
return result;
While I'm not surprised by it, I do wish we more aggressively discouraged its use in that form. There are a ton of beginner questions about it, and it's an irrelevant distraction.
(Personally I try to just never use ref, but I know that's a controversial opinion. But AFAIK everyone agrees that let x = &a; should be used over let ref x = a; for those specific non-nested forms.)
I wouldn't go as far as actively telling people to avoid it, but I'd definitely nudge people towards more common forms like let x = &y, throwing around words like "more readable" and "idiomatic".
With match ergonomics you don't really need ref apart from a handful of niche cases.