Hei!
I need to use a default value in case the optional inputs to my function are not provided.
I came up with this snippet, but it feels a bit magical, especially given the use of ref.
const DEFAULT_VALUES: &[i32] = &[1, 2, 3];
fn magic(inputs: Option<Vec<i32>>) {
let values = match inputs {
Some(ref vals) => vals,
None => DEFAULT_VALUES,
};
// data processing...
}
I guess my question is why does that snippet work, but if instead I use Some(vals) => &vals it doesn't? And also is there a better way to achieve what I'm trying to do?
a working alternative is (note the & operator before inputs):
let values = match &inputs {
Some(vals) => valus,
None => DEFAULT_VALUES;
};
or, better, use Option::as_deref():
let values = inputs.as_deref().unwrap_or(DEFAULT_VALUES);
the problem with your approach is the scrutinee is being moved into a inner scoped variable, and then you tried to borrow the (temp) variable of the inner scope (as opposed to borrowing the original value from the outer scope).
That's an understandable feeling; however once you get comfortable with lifetimes and move semantics, I'd argue "match ergonomics" is the much more "magical" thing at play. ref and ref mut are certainly "exotic" when you're first learning Rust, but understanding them requires a much simpler mental model. In fact in earlier Rust, you had no recourse but to use ref/ref mut. Now many don't need to have a deep understanding of the "magic" involved with references (e.g., reborrows, autoref, autoderef, etc.), so there is nothing wrong with trying to avoid ref/ref mut—one can even #![deny(clippy::ref_patterns)] if you really want to avoid them.
Personally, I prefer to use ref/ref mut—in fact I #![deny(clippy::pattern_type_mismatch)]—but I would say it's more common to rely on match ergonomics. match ergonomics can also be more powerful (e.g., see this issue); nonetheless I do recommend familiarizing yourself with ref/ref mut if for no other reason than to understand code not written by you.