Skip to content

borrow/ref: Not clear what's the difference between ref and & #390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kornelski opened this issue Jan 11, 2015 · 24 comments
Closed

borrow/ref: Not clear what's the difference between ref and & #390

kornelski opened this issue Jan 11, 2015 · 24 comments

Comments

@kornelski
Copy link
Contributor

In other languages & is called a reference and borrowing looks very much like references, so to me & and ref seem like the same thing.

I'd appreciate an example that contrasts the two — why let Point { x: ref ref_to_x, y: _ } is OK, but let Point { x: & ref_to_x, y: _ } isn't?

@strega-nil
Copy link

ref is used in pattern matching, so its used when you want to borrow
something in, for example, a match statement.

let a = Some("Hello!".to_string());
match a {
    Some(ref s) => { //s is an &String here
    }
    ...
}

match a {
    Some(s) => {
        // s is a String here, and therefore
        // is owned by the match
    }
    ...
}

@kornelski
Copy link
Contributor Author

Why can't you use & to borrow in the match?

match a {
    Some(&s) => {}
}

?

e.g. in methods I can use &self instead of ref self.

@strega-nil
Copy link

That, I don't know. It's not really a decision I agree with. I like that
style better, personally.

On Sun, Jan 11, 2015 at 11:27 AM, Kornel notifications@github.com wrote:

Why can't you use & to borrow in the match?

match a {Some(&s) => {}
}

?


Reply to this email directly or view it on GitHub
#390 (comment)
.

"Calling all. This is our last cry before our eternal silence."
"The lone and level sands stretch far and away"

@kornelski
Copy link
Contributor Author

I'm not trying to question Rust design decision here, just understand difference between ref and & — and I'm reporting that the example about references doesn't elaborate on that.

Whether that's just arbitrary, or quirky syntax to avoid ambiguity, or it has slightly different meaning, I'd like to know that, because I'm not sure when to use which and why rustc complains about &_-ptr or such when I use & instead of ref.

@strega-nil
Copy link

I think the idea is that you destruct as you construct, like in a

let op = Some(a);

match op {
    Some(x) => x,
    None => ...
}

So if you type

match op {
    Some(&x) => // here you are dereferencing a pointer
        ...
    None => ...
}

On Jan 11, 2015 11:44 AM, "Kornel" notifications@github.com wrote:

I'm not trying to question Rust design decision here, just understand
difference between ref and & — and I'm reporting that the example about
references doesn't elaborate on that.

Whether that's just arbitrary, or quirky syntax to avoid ambiguity, or it
has slightly different meaning, I'd like to know that, because I'm not sure
when to use which and why rustc complains about &_-ptr or such when I use
& instead of ref.


Reply to this email directly or view it on GitHub
#390 (comment)
.

@kornelski
Copy link
Contributor Author

Sorry, I don't understand your last answer. &x is dereferencing a pointer? I thought *x is dereferencing.

Perhaps I'm asking stupid questions, but the mental model I have is from C:

object  ----  reference operator &  ---> pointer
object  <--- dereference operator * ---- pointer

and there's no room in it for ref. If it's like this:

object  ---- ref operator ---> pointer

then in my mind it's identical with &, but clearly in Rust there must be a difference.

When I use & where Rust expects ref I get error message that I don't understand:

mismatched types: expected isize, found &_ (expected isize, found &-ptr)

I'm not sure what &_ and &-ptr are, but I'm guessing it means reference to an unknown type. And it's weird, because the type should be known from the context.

@strega-nil
Copy link

So where are you seeing this error? In Rust, you basically have this:

let a = &i;
let r = *a;
// These are exactly like in C (i.e. `&i` gets the address of i,
// `*a` dereferences a)

And in pattern matching, you do this:

let a = Some(1i); // This is how we construct an Option
match a {
    Some(x) => println!("{}', x), // This prints '1'
    // Notice we "deconstruct" in a match statement the same way we
    // normally construct
    ...
}

What about when we have to get a reference in a match statement where there was
none before? In normal code, getting a reference to a variable is easy.

struct BigFoo {
    // Lots of fields
}

fn bar(bf: &BigFoo) {
    // Do stuff with bf
}

fn main() {
    let bf = BigFoo {
        // lots of fields
    }

    bar(&bf);
}

However, what if you have, say, a match statement?

fn possibly_print(x: &Option<BigFoo>) {
    match *x {
        // BAD: It's impossible to move out of an &-reference (the Option)
        //Some(bf) => println!("{:?}", &s),

        // GOOD: Instead, we take a reference into the `Option`s memory
        Some(ref bf) => println!("{:?}", *bf),
        None => println("No BigFoo!"),
    }
}

(Note: this is taken directly from the rust docs book)

Does that make sense?

@kornelski
Copy link
Contributor Author

The error I was getting was from

match a {
  Some(&x) => 
}

So I think I'm starting to get why it's illegal syntax:

fn main() {
    let x = 123;
    let &x_ref_1 = &x;
    let ref x_ref_2 = &x;
    //let & x_ref_3 = x; // same error as when matching &x
    let ref x_ref_4 = x;
}

It would be great if the tutorial explained exactly what happens in the cases above (they all print the same value, but I suppose Rust's automagic dereferencing hides the differences).

but "matching uses same syntax as constructing" doesn't seem right to me:

    let s1 = Some(&x); // legal
    let s2 = Some(ref x); // illegal

@strega-nil
Copy link

There are two things going on here:

let x = 3i;
let s1 = Some(&x);
match s1 {
    Some(&y) => {}, // y = x here (it's destructured the same way it's constructed)
    ...
}

ref is only useful for match statements and the like; it's literally only for

let s2 = Some(x);
match s2 {
    Some(ref y) => {}, // y = &x here
    ...
}

@kornelski
Copy link
Contributor Author

Oh, that's interesting! Are these two statements equivalent?

let x = &y;
let ref x = y;

@strega-nil
Copy link

Yes, exactly!

On Mon, Jan 12, 2015 at 2:23 PM, Kornel notifications@github.com wrote:

Oh, that's interesting! Are these two statements equivalent?

let x = &y;let ref x = y;


Reply to this email directly or view it on GitHub
#390 (comment)
.

"Calling all. This is our last cry before our eternal silence."
"The lone and level sands stretch far and away"

@kornelski
Copy link
Contributor Author

Ok, so now it makes sense to me! Thanks!

I suggest adding such simple example to the site:

let y = 'y';

// `ref` on the left side of an assignment is like adding `&` on the right side
let ref x1 = y;
let x2 = &y;

println!("{}", x1 == x2);

The full example is useful as well, but with destructuring at the same time there's much more going on.

@strega-nil
Copy link

Thank you very much for bringing this incredibly confusing thing to our
attention; I hadn't even thought about it before you said it :)
On Jan 12, 2015 2:37 PM, "Kornel" notifications@github.com wrote:

Ok, so now it makes sense to me! Thanks!

I suggest adding such simple example to the site:

let y = 'y';
// ref on the left side of an assignment is like adding & on the right sidelet ref x1 = y;let x2 = &y;

println!("{}", x1 == x2);

The full example is useful as well, but with destructuring at the same
time there's much more going on.


Reply to this email directly or view it on GitHub
#390 (comment)
.

@mdinger
Copy link
Contributor

mdinger commented Jan 20, 2015

@pornel I just saw this thread and modified PR #421 to try to elaborate on this.

The specific file was pointers.rs

By the way, your critiques are very helpful. It's really hard to anticipate learning questions. Generally I focus on trying to fix things I find confusing but having others do the same is really useful.

@kornelski
Copy link
Contributor Author

@mdinger thanks. It's much clearer.

And I didn't know if let existed :)

@mdinger
Copy link
Contributor

mdinger commented Jan 26, 2015

ref and & are discussed here on reddit. let mut ref mut x = y I hadn't seen before. If my PR is generally accepted, I may try to work that in or refer to somewhere it can be discussed more thoroughly.

These explanations are missing in general and really need to be added.

@mdinger
Copy link
Contributor

mdinger commented Jan 30, 2015

Destructuring and pattern matching confusion discussed here.

I have a feeling pattern matching may need to be discussed as a separate topic from match. match uses pattern matching but it's also typically where it's explained.

@mdinger
Copy link
Contributor

mdinger commented Aug 9, 2015

Is this still a problem? Is there an actionable response that can be taken?

@kornelski
Copy link
Contributor Author

Yes, I think it still can be improved.

The big example on that page focuses on a case where ref is necessary, but it doesn't make it clear how ref relates to &. There's too much going on for me to understand it.

For me it "clicked" when I saw the simplest case:

// `ref` on the left side of an assignment is like adding `&` on the right side
let ref x1 = y;
let x2 = &y;

So I suggest adding exactly that to the page, before showing a complex case.

@LukeSkyw
Copy link

Just spent two hours trying to make sense of this:

struct Foo {
    value: u32,
}

fn double(v: &Vec<(Foo, u32)>) -> Vec<u32> {
    v.iter().map(|&(&f, v)| f.value + v).collect()
  |                ^^ expected struct `Foo`, found reference
  |
  = note: expected type `Foo`
  = note:    found type `&_`
}

fn main() {
    let vector = vec!(
        (Foo { value: 1 }, 2),
        (Foo { value: 2 }, 3),
        (Foo { value: 3 }, 4));
    let doubled = double(&vector);
    println!("{:?}", doubled);
}

So, as explained above, it should have been:

struct Foo {
    value: u32,
}

fn double(v: &Vec<(Foo, u32)>) -> Vec<u32> {
    v.iter().map(|&(ref f, v)| f.value + v).collect()
}

fn main() {
    let vector = vec!(
        (Foo { value: 1 }, 2),
        (Foo { value: 2 }, 3),
        (Foo { value: 3 }, 4));
    let doubled = double(&vector);
    println!("{:?}", doubled);
}

@AlecZadikian9001
Copy link

AlecZadikian9001 commented May 6, 2017

I find it strange that ref is only used on variables, but & is used on both types and variables. Adding in all the other type keywords creates other issues. * and & are both used for pointer types, raw and safe respectively, but with variables, * is used for dereferencing while & is used for enreferencing, for both raw and safe pointers. The "mut" keyword is inconsistently used too when you involve &, for example: mutable argument with fn foo(mut var: MyStruct) (mut comes before) and mutable pointer argument with fn foo(var: &mut MyStruct) (mut comes after).

@abbshr
Copy link

abbshr commented Aug 30, 2017

fn main() {
    if let Some(&pat) = Some(&Box::new(1)) {
        println!("{:?}", pat);
    }
}

The rust compiler explained as follow:

error[E0507]: cannot move out of borrowed content
 --> ref.rs:2:17
  |
2 |     if let Some(&pat) = Some(&Box::new(1)) {
  |                 ^---
  |                 ||
  |                 |hint: to prevent move, use `ref pat` or `ref mut pat`
  |                 cannot move out of borrowed content

It says "cannot move out of borrowed content, to prevent move, use ref pat or ref mut pat".

Besides, I think &Type is used to declare a reference type, &var is used to get the reference of the value (like a right-value), but with keyword ref, the ref var is used in pattern match to take out the matched value from a struct and bind its reference to var. Because &var can't be a left-value, so, using ref just in pattern match is undoubted~

@kaiwk
Copy link

kaiwk commented Jun 27, 2019

Hello, sorry for comment on old issue, but I found it's an interesting topic.

I know that pattern match is a way to match programming language syntax itself, but I still feel confused about keyword ref. Why can't we just use let *x_ref = x. In that case, x is a value, and x_ref is a reference.

For example:

if let Some(* x_ref) = Some(x) {
}

It's more consistent with Rust syntax.

In my opinion, getting a reference to a value should be an opposite behavior in pattern match, which is dereference. Maybe deref is better than ref.

if let Some(deref x_ref) = Some(x) {
}

@vbauerster
Copy link

vbauerster commented Jun 16, 2020

e.g. in methods I can use &self instead of ref self

Looks like nobody gave an answer regarding &self in methods.
Actually &self is a shorthand for a type one implements method on.

Shorthand impl for MyType
self self: MyType
&self self: &MyType
&mut self self: &mut MyType

So, technically &self is not a reference pattern and there is nothing to destructure on it. That's why saying ref self in methods is equally not appropriate as saying let x = ref MyType.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants