What's the difference between ref and & when assigning a variable from a reference?

前端 未结 3 1539
一生所求
一生所求 2020-12-19 07:32

What is wrong with this code?

fn example() {
    let vec = vec![1, 2, 3];
    let &_y = &vec;
}


        
相关标签:
3条回答
  • 2020-12-19 08:11

    First of all, you do not need the & in your working example. If you use it, you end up with a &&Vec<_>, which you don't really need.

    let vec = vec![1, 2, 3];
    let ref y = vec;
    

    The problem with your first code is that you are doing two things at once. Lets split it up into two:

    The first part creates a reference to vec

    let y1 = &vec;
    

    The second part (the & before the variable binding), destructures.

    let &y2 = y1;
    

    This means you are trying to move out of the reference, which only works if the type is Copy, because then any attempt to move will copy the object instead.

    0 讨论(0)
  • 2020-12-19 08:27

    The same symbol (&) is doing two different things when used on the right-end and left-end side of a binding. The left-hand side works like a pattern matching, so:

    let x = (y, z); // x contains a tuple with value (y, z)
    let (a, b) = x  // x is destructured into (a, b), so now
                    // a has value y and b has value z
    

    In the same way

    let x = &y; // x is a reference to y
    let &z = x; // this is like let &z= &y, so we want z to be y
                // this is equivalent to let z = *x
    

    A ref binding on the left side is saying "pattern match by reference, not by value". So these two statements are equivalent:

    let ref y = vec;
    let y = &vec;
    

    although in a let, the second one is more idiomatic.

    You can see more examples on the pointers/ref chapter on rust by example

    0 讨论(0)
  • 2020-12-19 08:32

    Pattern binding can get some using to ;)

    In order to understand what the compiler does, you can use the let _: () = ...; trick. By assigning to a variable of type (), you force the compiler to print an error message giving you the type it inferred for your variable.


    In the first example:

    let vec = vec![1, 2, 3];
    let &y = &vec;
    let _: () = y;
    

    we get:

    error[E0308]: mismatched types
     --> src/lib.rs:4:13
      |
    4 | let _: () = y;
      |             ^ expected (), found struct `std::vec::Vec`
      |
      = note: expected type `()`
                 found type `std::vec::Vec<{integer}>`
    

    the type of y is Vec<i32>.

    What it means is that you are:

    1. Borrowing vec into a temporary
    2. Attempting to move vec into y, which is forbidden because vec is already borrowed.

    The equivalent correct code would be:

    let vec = vec![1, 2, 3];
    let y = vec;
    

    In the second example:

    let vec = vec![1, 2, 3];
    let ref y = &vec;
    let _: () = y;
    

    we get:

    error[E0308]: mismatched types
     --> src/lib.rs:4:17
      |
    4 |     let _: () = y;
      |                 ^ expected (), found reference
      |
      = note: expected type `()`
                 found type `&&std::vec::Vec<{integer}>`
    

    Thus y is &&Vec<i32>.

    This let us see that let ref a = b; is generally equivalent to let a = &b;, and therefore in this case: let y = &&vec;.

    ref is made for destructuring; for example, if you had:

    let vec = Some(vec![1, 2, 3]);
    if let Some(ref y) = vec {
        // use `y` here
    }
    

    you can use ref here to be able to bind y to &Vec<i32> without moving even though vec here has type Option<Vec<i32>>. Indeed, the purpose of ref is to take a reference inside an existing object during destructuring.

    In general, in a let statement, you will not use ref.

    And since Rust 1.26, ref is inferred in pattern matching; see the stabilization of match ergonomics.

    0 讨论(0)
提交回复
热议问题