vbstb
vbstb

Reputation: 1451

Why is `ref` used instead of an asterisk in pattern matching?

I am having trouble trying to understand pattern matching rules in Rust. I originally thought that the idea behind patterns are to match the left-hand side and right-hand side like so:

struct S {
    x: i32,
    y: (i32, i32)
}
let S { x: a, y: (b, c) } = S { x: 1, y: (2, 3) }; 
// `a` matches `1`, `(b, c)` matches `(2, 3)`

However, when we want to bind a reference to a value on the right-hand side, we need to use the ref keyword.

let &(ref a, ref b) = &(3, 4);

This feels rather inconsistent.

Why can't we use the dereferencing operator * to match the left-hand side and right-hand side like this?

let &(*a, *b) = &(3, 4);
// `*a` matches `3`, `*b` matches `4`

Why isn't this the way patterns work in Rust? Is there a reason why this isn't the case, or have I totally misunderstood something?

Upvotes: 1

Views: 895

Answers (2)

Shepmaster
Shepmaster

Reputation: 430614

Using the dereferencing operator would be very confusing in this case. ref effectively takes a reference to the value. These are more-or-less equivalent:

let bar1 = &42;
let ref bar2 = 42;

Note that in let &(ref a, ref b) = &(3, 4), a and b both have the type &i32 — they are references. Also note that since match ergonomics, let (a, b) = &(3, 4) is the same and shorter.

Furthermore, the ampersand (&) and asterisk (*) symbols are used for types. As you mention, pattern matching wants to "line up" the value with the pattern. The ampersand is already used to match and remove one layer of references in patterns:

let foo: &i32 = &42;

match foo {
    &v => println!("{}", v),
}

By analogy, it's possible that some variant of this syntax might be supported in the future for raw pointers:

let foo: *const i32 = std::ptr::null();

match foo {
    *v => println!("{}", v),
}

Since both ampersand and asterisk could be used to remove one layer of reference/pointer, they cannot be used to add one layer. Thus some new keyword was needed and ref was chosen.

See also:

Upvotes: 2

edwardw
edwardw

Reputation: 13942

In this specific case, you can achieve the same with neither ref nor asterisk:

fn main() {
    let (a, b) = &(3, 4);

    show_type_name(a);
    show_type_name(b);
}

fn show_type_name<T>(_: T) {
    println!("{}", std::any::type_name::<T>()); // rust 1.38.0 and above
}

It shows both a and b to be of type &i32. This ergonomics feature is called binding modes.

But it still doesn't answer the question of why ref pattern in the first place. I don't think there is a definite answer to that. The syntax simply settled on what it is now regarding identifier patterns.

Upvotes: 0

Related Questions