I'm confused about adding '&' when defining variables in Rust

I don't understand it all the time.

For example, when defining a variable, it is usually done like this:
let x = 3;
struct A();
let a = A();

I understand this kind of syntax.

But when it comes to:
let a = &A()

I don't understand what happens in this case, and there are many other questions.

For example, in a method, there are parameters like &self, self, mut self, &mut self.
And then when defining a variable:
let a = A()

All of the above parameters can be used, but I don't understand why self can be assigned to &self. It is not borrowing.
And when it comes to let a = &A(), there are some functions that have parameters as self and cannot be assigned to &self, so they cannot be called.

Not to mention when defining a variable like let &&a = &&A(), I'm completely confused. So how can I understand them? Are there any relevant rules?

1 Like

It's static promotion. 1414-rvalue_static_promotion - The Rust RFC Book


It's about methods call desugaring and ownership.

when you call a method with object.something() , Rust automatically adds in & , &mut , or * so object matches the signature of the method.

src: Method Syntax - The Rust Programming Language

When looking up a method call, the receiver may be automatically dereferenced or borrowed in order to call a method. This requires a more complex lookup process than for other functions, since there may be a number of possible methods to call. The following procedure is used:

The first step is to build a list of candidate receiver types. Obtain these by repeatedly dereferencing the receiver expression's type, adding each type encountered to the list, then finally attempting an unsized coercion at the end, and adding the result type if that is successful. Then, for each candidate T, add &T and &mut T to the list immediately after T.

src: Method call expressions - The Rust Reference


It's a reference pattern in let statements.

fn f4(&mut self) {
    println!("f4");
}

look at this .

I call this function like this .

let a = A(2);
let b = a;
b.f4();

I see the regular is add & &mut *

the b is self ,why i call the function , the occur error . why it don't add &mut and call sucess ???

Whenever a function has a parameter called self, we are talking about a method; that is, the function must be defined within an impl block, it cannot be defined within a module like you did with f4.

struct A(usize);

impl A {
    fn f5(&mut self) {
        println!("f5");
    }
}

fn main() {
    let a = A(2);
    let mut b = a;
    b.f5();
}

In Rust, variables are immutable by default. The mut keyword is used to signal that you explicitly want mutable access to the value that that variable is binding to.

2 Likes

Let’s look at the error here

struct A(i32);
impl A {
    fn f4(&mut self) {
        println!("f4");
    }
}

fn main() {
    let a = A(2);
    let b = a;
    b.f4();
}
error[E0596]: cannot borrow `b` as mutable, as it is not declared as mutable
  --> src/main.rs:11:5
   |
11 |     b.f4();
   |     ^ cannot borrow as mutable
   |
help: consider changing this to be mutable
   |
10 |     let mut b = a;
   |         +++

For more information about this error, try `rustc --explain E0596`.

(and here’s context for rustc --explain E0596)

First off, the compiler suggests the fix that help out here

fn main() {
    let a = A(2);
    let mut b = a;
    b.f4();
}

(this runs successfully now)


Second off, let’s try to break down things a bit. Maybe it’s a good first step to skip method syntax here. Method syntax is super useful; you can call methods like something.method_name(arguments…), it works without importing the methods manually, and has an often useful infix style, but it’s also a feature packed full of special convenience features, and I can underst that it could lead to some confusion.

What does this code look like if it isn’t a method? Let’s make it a non-method. First the definition, then the call site.

The definition

impl A {
    fn f4(&mut self) {
        println!("f4");
    }
}

features &mut self syntax. This is a special syntax, an abbreviation, and it abbreviates self: &mut Self

impl A {
    fn f4(self: &mut Self) {
        println!("f4");
    }
}

This code is still equivalent to the original. Next up: Self, a type available in impl blocks (including trait implementation) and it’s a shorthand for the type that follows the word impl, in this case A. So we can re-write it:

impl A {
    fn f4(self: &mut A) {
        println!("f4");
    }
}

Now, this is still a method, and fully equivalent to the original (&mut self). It features the self keyword as the first argument’s name, which makes it a method. If we re-name it to anything else, it’s no longer a method.

Compare

struct A(i32);
impl A {
    fn f4(self: &mut A) {
        println!("f4");
    }
}

fn main() {
    let a = A(2);
    let mut b = a;
    b.f4();
}

output:

f4

with

struct A(i32);
impl A {
    fn f4(other_name: &mut A) {
        println!("f4");
    }
}

fn main() {
    let a = A(2);
    let mut b = a;
    b.f4();
}

error:

error[E0599]: no method named `f4` found for struct `A` in the current scope
  --> src/main.rs:11:7
   |
1  | struct A(i32);
   | -------- method `f4` not found for this struct
...
11 |     b.f4();
   |     --^^--
   |     | |
   |     | this is an associated function, not a method
   |     help: use associated function syntax instead: `A::f4()`
   |
   = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
note: the candidate is defined in an impl for the type `A`
  --> src/main.rs:3:5
   |
3  |     fn f4(other_name: &mut A) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0599`.

(and here’s context for rustc --explain E0599)


Now, how can we call a function in impl, like f4, without it being a method? The compile already gives us a hint, we write A::f4(…).

Let’s follow compiler suggestions for a while… first up, the one from above

fn main() {
    let a = A(2);
    let mut b = a;
    b.f4();
}
11 |     b.f4();
   |     --^^--
   |     | |
   |     | this is an associated function, not a method
   |     help: use associated function syntax instead: `A::f4()`

Okay… let’t try that:

fn main() {
    let a = A(2);
    let mut b = a;
    A::f4();
}
error[E0061]: this function takes 1 argument but 0 arguments were supplied
  --> src/main.rs:11:5
   |
11 |     A::f4();
   |     ^^^^^-- an argument of type `&mut A` is missing
   |
note: associated function defined here
  --> src/main.rs:3:8
   |
3  |     fn f4(other_name: &mut A) {
   |        ^^ ------------------
help: provide the argument
   |
11 |     A::f4(/* &mut A */);
   |          ~~~~~~~~~~~~~~

For more information about this error, try `rustc --explain E0061`.

(rustc --explain E0061)

Okay, we need

fn main() {
    let a = A(2);
    let mut b = a;
    A::f4(/* SOMETHING HERE */);
}

and that something clearly is b, right? When replacing b.f4() to A::f4(), the b usage was lost anyways… so:

fn main() {
    let a = A(2);
    let mut b = a;
    A::f4(b);
}

Not quite yet, next suggestion from the compiler

error[E0308]: mismatched types
  --> src/main.rs:11:11
   |
11 |     A::f4(b);
   |     ----- ^ expected `&mut A`, found `A`
   |     |
   |     arguments to this function are incorrect
   |
note: associated function defined here
  --> src/main.rs:3:8
   |
3  |     fn f4(other_name: &mut A) {
   |        ^^ ------------------
help: consider mutably borrowing here
   |
11 |     A::f4(&mut b);
   |           ++++

For more information about this error, try `rustc --explain E0308`.

(rustc --explain E0308)

So this is getting somewhere! Because the final suggestion is to write

fn main() {
    let a = A(2);
    let mut b = a;
    A::f4(&mut b);
}

and that works! Also it’s showing something interesting: &mut self, which translates into a &mut A argument, is about types, the last error was a type mismatch error after all. The function f4 wants a value of type &mut A which is the type of “mutable references to instances of type A”. Mutable references are created explicitly using the syntax &mut …target… where the target of the reference (that is, the variable that is borrowed by the reference) here is the variable b.

The method syntax has a long list of rules that determine how it would translate b.f4() into a call of this form A::f4(&mut b) automatically and implicitly, but that can mean you might not fully understand what all the “mut” and such actually mean.

In this case, A is a different type from &mut A, and that’s also a different type from &A.


Let’s look at a few more things. Looking back, if we turn it into a method again, we can notice that the non-method-syntax call still works. This works fine:

struct A(i32);
impl A {
    fn f4(self: &mut A) {
        println!("f4");
    }
}

fn main() {
    let a = A(2);
    let mut b = a;
    A::f4(&mut b);
}

as does (unsurprisingly, if you believe my claim that &mut self was fully equivalent) the following

struct A(i32);
impl A {
    fn f4(&mut self) {
        println!("f4");
    }
}

fn main() {
    let a = A(2);
    let mut b = a;
    A::f4(&mut b);
}

Also, we can eliminate the “associated” nature of the function in the impl block if we like. It could become a free-standing function, no longer connected with the A:: type and prefix:

Compare this

struct A(i32);
impl A {
    fn f4(other_name: &mut A) {
        println!("f4");
    }
}

fn main() {
    let a = A(2);
    let mut b = a;
    A::f4(&mut b);
}

with this:

struct A(i32);
fn f4(other_name: &mut A) {
    println!("f4");
}


fn main() {
    let a = A(2);
    let mut b = a;
    f4(&mut b);
}

Finally, let’s re-visit your original error message. If we don’t write let mut b anymore, we get that error again, but now in the simplified context without all the fancy and implicit method syntax stuff…

struct A(i32);
fn f4(other_name: &mut A) {
    println!("f4");
}


fn main() {
    let a = A(2);
    let b = a;
    f4(&mut b);
}
error[E0596]: cannot borrow `b` as mutable, as it is not declared as mutable
  --> src/main.rs:10:8
   |
10 |     f4(&mut b);
   |        ^^^^^^ cannot borrow as mutable
   |
help: consider changing this to be mutable
   |
9  |     let mut b = a;
   |         +++

For more information about this error, try `rustc --explain E0596`.

You can see now, this is an error message that comes up when trying to create a mutable reference for a variable not marked as mutable. The &mut b triggers this error; and the implicit creation of the &mut A with method syntax did the same thing. Marking a variable mutable doesn’t affect its type, or change program behavior. Either way it’s a variable b of type A. But the let mut b marker in Rust serves as a very useful tool so that you don’t accidentally mutate any variable you didn’t want to; it highlights mutable variables, since mutating variables will often lead to harder to reason about code, and this way at least you can focus better on those explicitly mutable variables when trying to reason about code someone else wrote.

11 Likes

look at this . my question is from here ,i see the course of rust . the link has a sentence is "Rust 会自动为 object 添加 &&mut* 以便使 object 与方法签名匹配" link is 方法 Method - Rust语言圣经(Rust Course)

it say auto add & ,& mut ,and * until successfully matching.
so if my variable is self , i call the funtion is &mut self . Rust can auto add &mut , so it shoud be called successfully . because it auto add &mut . but actually is not like that. it fail.

so i make a table like this

    /* 
 
    mut self ,                           can convert any 
        self ,                 &mut self  can't  
  & mut self , mut self  self             can't
  &     self , mut self  self  &mut self  can't

    */

look at my four function of first parameter . I try lot of test. when mut self , call the four function . When self , i call four funtion ,........

your konw? I found regular , but i dont't how to explain . Sometime i add #[derive(Copy,Clone)] at the A struct ,some fail test can be successfully to run . how confusing !

so,can you explain ? and begin ,i was want to test the deref trait . because rust can auto add * . I implement the trait . and use &self call the self function .and compiler occur error . so the rust can't add * , let the &self to self ? . so if can be and it has condition . it should be mention the condition . and some case , &type can call the type . I found the deref is not called by rust .I guess is rust not call the deref trait . but i forget what the some case .

I find that the language sometimes follows certain rules and sometimes does not. I don't know when it follows the rules and when it doesn't, or if there are any prerequisites. Sometimes it feels like the compiler is doing a lot of things,we don't konw .

use std::ops::{Deref, DerefMut};

#[test]
fn main() {

    

    /* 
 
    mut self ,                           can convert any 
        self ,                 &mut self  can't  
  & mut self , mut self  self             can't
  &     self , mut self  self  &mut self  can't

    */

    let mut a = A(2);
    let b= &mut a;
    b.f1();
    // b.f2();
    // b.f3();
    // b.f4();
    
 
}

// #[derive(Clone,Copy)]
struct A(i32);

impl DerefMut for A{

    fn deref_mut(&mut self) -> &mut Self::Target {
        println!("ttt");
        &mut self.0
    }
}
impl Deref for A{
    type Target = i32;

    fn deref(&self) -> &Self::Target {
        println!("test");
        &self.0
    }
}
impl A {
    fn f1(self) {
        println!("f1");
    }
    fn f2(&self) {
        println!("f2");
    }
    fn f3(mut self) {
        println!("f3");
    }
    fn f4(&mut self) {
        println!("f4");
    }
}

It sounds like you have a misunderstanding about ownership and borrowing, because you are expecting the automatic referencing and dereferencing on method calls to be able to ignore those rules. I will try to explain what's going on, but I would suggest you review the information on ownership and borrowing to have a better understanding. I'm not familiar with the course you linked, but it looks like Section 2.3 is the ownership and borrowing section. The usual resource for learning Rust is the book (a Chinese translation is available), where chapter 4 covers ownership and borrowing.

When a method is called, Rust does add &, &mut and * in an attempt to find an implementation it can call (the actual algorithm is a little quirky, but you shouldn't need to worry about it at this point). However, these additions are treated the same as if you wrote them normally in your code, so & takes a shared reference (and errors if a unique reference to the same thing exists), &mut takes a unique reference (and errors if any other references to the same thing exist), * derefences but references still can't be moved out of (so it will error if you call a method that takes ownership on a non-Copy type behind a reference).

Looking at your example, A is a struct that is not Copy (ignoring the commented out derive for the moment), so you can't take ownership of it behind a &mut reference, so the call of f1() errors. For f2() it tries dereferencing and then referencing as shared (so it does &*b), which is called a reborrow and is allowed, so the call of f2() succeeds. The f3() case is the same as the f1() case, because the mut there isn't part of the function signature, it is just an annotation on the local variable binding (local to f3()), so the call of f3() errors. The f4() case perfectly matches the type being passed in, so the call to f4() succeeds. (It actually inserts an implicit reborrow when calling functions that take &mut references, since giving up ownership of such references is not usually desired; this allows reusing the reference to call another function. A similar issue doesn't occur for & references, since they are Copy.)

If A is made Copy by uncommenting the derive (Clone is irrelevant here since you never call clone()), then it is copied rather than moved into functions that take ownership, so f1() and f3() succeed (since you are just copying out of a reference now). The f2() and f4() cases work the same, because they never did any moving.

Your Deref and DerefMut implementations are unused, because a method is found in each case without needing to use it. Calling an i32 method on A would make use of it.

2 Likes

can you explain the auto add &, &mut , * ,i have a idea.
i see the Method call expressions - The Rust Reference page. has a word is
For instance, if the receiver has type Box<[i32;2]> , then the candidate types will be Box<[i32;2]> , &Box<[i32;2]> , &mut Box<[i32;2]> , [i32; 2] (by dereferencing), &[i32; 2] , &mut [i32; 2] , [i32] (by unsized coercion), &[i32] , and finally &mut [i32] .

My idea is to write a lot of functions whose parameters are the possible candidates that Rust might resolve to, and then manually call them to see if Rust really will succeed in calling the function by adding &, * and &mut. For example, one function might have a parameter of &[i32], and another function might have a parameter of &mut [i32].

And so on for the other functions.

simple test like this.

#[test]
fn t(){
    let mut x1 = Box::new([3; 2]);
    display2( &mut x1); // success 
    display3(x1);// fail , how to call ?
}
fn display2(arr:&mut [i32]){
   println!("{:?}",arr)
}
fn display3(arr: [i32;2]){
   println!("{:?}",arr)
}

So how do we understand what Rust resolves &mut [i32;2] , [i32] , and &mut [i32] to? This is not explained clearly.

This is rather getting into the specifics of the algorithm for method dispatch, but the list checked for the x1 : Box<[i32; 2]> in your example is as follows:

x1: Box<[i32; 2]>
&x1: &Box<[i32; 2]>
&mut x1: &mut Box<[i32; 2]>
*x1: [i32; 2]
&*x1: &[i32; 2]
&mut *x1: &mut [i32; 2]
*x1 as [i32]: [i32]
&*x1 as &[i32]: &[i32]
&mut *x1 as &mut [i32]: &mut [i32]

Basically, it first tries it on its own, then tries adding & and &mut. After that, it dereferences a level and does the same again. After all the possible dereferences are exhausted, it performs an unsizing coercion, specifically coercing to a slice (I have a longstanding issue to get that clarified in the reference), and tries the three possibilities again. The coercion can be performed implicitly where there is enough type information (specifically, at a designated coercion site), but I've written it here as an explicit cast.

For the display2() function, the only the last item in the list would match the type (the lookup isn't actually performed because it isn't a method and isn't called with method call syntax). The expression needed is thus &mut *x1 as &mut [i32], or just &mut *x1 (since a function argument with a non-generic type is a coercion site).

For display3(), the fourth item in the list matches, so *x1 will work. The taking of ownership is allowed because arrays are Copy whenever their contents are, so it just copies out from behind the pointer.

Those aren't method calls -- they're not after a variable. and they don't have self receivers. They're call expressions. It's still possible for coercion to happen to the arguments, but it's not the same as a method call resolution/coercion .

but it's not the same as a method call resolution/coercion .
What are the differences?

Associated functions vs methods: Associated Items - The Rust Reference

They're pretty different.

Call expressions have

  • No autoref (no leading & or &mut is added)
  • Different, more-limited autoderef (only behind reference types)
    • e.g. &T to &U and &mut T to &mut U
  • Different unsizing circumstances
    • e.g. not after an autoderef

Call expressions of trait methods can also be annotated, in which case they have access to more types of coercion

  • E.g. dyn Trait unsizing

And other things like differences between "first matching receiver wins" and what available coercions are considered (either which take precedence or which are considered ambiguous).

The playground is currently busted but here are some examples you could play with when it's back or in your own project.

trait Co {
    fn t(self) where Self: Sized {}
    fn ref_t(&self) {}
    fn ref_mut_t(&mut self) {}
}

trait Derefed {
    fn deref_t(self) where Self: Sized {}
    fn ref_deref_t(&self) {}
    fn ref_mut_deref_t(&mut self) {}
}

trait AfterSliceUnsize {
    fn ref_unsize(&self) {}
    fn ref_mut_unsize(&mut self) {}
}

impl Co for Box<[i32; 2]> {}
impl Derefed for [i32; 2] {}
impl AfterSliceUnsize for [i32] {}

fn foo(mut bx: Box<[i32; 2]>) {
    bx.clone().t();
    bx.ref_t();
    bx.ref_mut_t();
    bx.clone().deref_t();
    bx.ref_deref_t();
    bx.ref_mut_deref_t();
    bx.ref_unsize();
    bx.ref_mut_unsize();
    
    Co::t(bx.clone());
    // Co::ref_t(bx);
    // <Box<[i32; 2]> as Co>::ref_t(bx);
    Co::ref_t(&bx);
    // Co::ref_mut_t(bx);
    // <Box<[i32; 2]> as Co>::ref_mut_t(bx);
    Co::ref_mut_t(&mut bx);
    // Derefed::deref_t(bx);
    // <[i32; 2] as Derefed>::deref_t(bx);
    Derefed::deref_t(*bx);
    // Derefed::ref_deref_t(bx);
    // <[i32; 2] as Derefed>::ref_deref_t(bx);
    Derefed::ref_deref_t(&*bx);
    <[i32; 2] as Derefed>::ref_deref_t(&bx);
    // Derefed::ref_mut_deref_t(bx);
    // <[i32; 2] as Derefed>::ref_mut_deref_t(bx);
    Derefed::ref_mut_deref_t(&mut *bx);
    <[i32; 2] as Derefed>::ref_mut_deref_t(&mut bx);
    // AfterSliceUnsize::ref_unsize(bx);
    // AfterSliceUnsize::ref_unsize(*bx);
    // AfterSliceUnsize::ref_unsize(&*bx);
    // <[i32] as AfterSliceUnsize>::ref_unsize(bx);
    // <[i32] as AfterSliceUnsize>::ref_unsize(&bx);
    // <[i32] as AfterSliceUnsize>::ref_unsize(*bx);
    AfterSliceUnsize::ref_unsize(&bx[..]);
    <[i32] as AfterSliceUnsize>::ref_unsize(&*bx);
    // AfterSliceUnsize::ref_mut_unsize(bx);
    // AfterSliceUnsize::ref_mut_unsize(*bx);
    // AfterSliceUnsize::ref_mut_unsize(&mut *bx);
    // <[i32] as AfterSliceUnsize>::ref_mut_unsize(bx);
    // <[i32] as AfterSliceUnsize>::ref_mut_unsize(&mut bx);
    // <[i32] as AfterSliceUnsize>::ref_mut_unsize(*bx);
    AfterSliceUnsize::ref_mut_unsize(&mut bx[..]);
    <[i32] as AfterSliceUnsize>::ref_mut_unsize(&mut *bx);
}

trait Trait {}
impl Co for dyn Trait + '_ {}
impl Trait for f32 {}

fn bar(f: f32) {
    // f.ref_t();
    // (&f as _).ref_t();
    // Co::ref_t(&f);
    <dyn Trait>::ref_t(&f);
}
3 Likes

There's an error in the third line of the list. You forgot the brackets for the array in the Box

1 Like

Ah, so I did. Thanks for catching that! I probably shouldn't type these things out so late at night :sweat_smile:

1 Like

I reviewed it and didn't find any differences, except that the first parameter of a method must represent the instance itself.

Methods are indeed distinguished from other associated functions by having a self parameter (which must be of type Self, or Self behind one of a small set of allowed pointer types), but the point is that methods can be called with method call expressions (of the form x.method(args)), which differ from ordinary function call expressions. These method call expressions perform the adding of &, &mut and * to the self argument and look for a method which matches in order to determine which function to call. There are also limitations on what coercions can be applied as part of this lookup.

By contrast, ordinary function call expressions specify a particular function to call, and that function's signature determines the types of arguments it takes. The types passed in need to match those types, except that there are some implicit coercions supported. These implicit coercions overlap with the referencing/dereferencing used in method call lookup, but there are differences. For example, method lookup can add & and &mut, which the coercions cannot do. On the other hand, coercions can convert to trait objects, which method lookup cannot do.

1 Like

I'm really convinced by Rust, I still can't summarize this problem. But I can explain it. I can understand why mut self can call &self and &mut self. That's it.

I've listened to many replies to the post. I feel dizzy after listening to them. I've seen many people say that the rules of methods and functions are different, and that there is automatic dereferencing when calling. After reading it, I still don't understand anything.

Now I want to know clearly. When Rust methods and functions are called, what are the conversion rules for parameters? To be more specific, how are &, &mut, and * added automatically? Does the compiler add them directly and then run them, or does it add them and then optimize the code (such as adding * to the variable x and then running the entire x, or does it calculate the result of x and then stop being x and then run it)? Or does the compiler just add this and not care about anything else? Just run it. There is also an implicit type conversion between parameters. My current understanding of candidates is that the compiler first adds & and then &mut, and then adds it again, and then performs another addition of &, &mut, and so on until it fails. All the successful ones are considered as candidates, and type conversion is performed when calling functions or methods. If the conversion is successful, it is executed. By the way, there is also a conversion without size type. I'm going crazy.

In particular, my current understanding of the compiler adding &, &mut, and * is that the compiler directly manipulates the code and only adds &, &mut, and *, but I don't know if it's just that simple to add code? Will it further parse the code, such as adding * to a variable, and then calculating the result and replacing it during compilation?

Ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh. I'm going crazy.

Well, it's all about desugaring in Rust:

  • method call expressions are mere syntactic sugars for function calls, remember this and you'll never lose mind
  • the purpose of automatically adding & and *prefixes is to write them less by yourself: but remember this behavior only works in method calls, since function calls only accept accurate types without automatically (de)referencing
  • coercion happens everywhere and very implicitly
2 Likes

The good thing about these automatic conversions is that you don’t have to learn about them all at once. In almost all cases, it’s just a case of “compiler inserts a useful conversion in some cases where it’s necessary”. Necessary, as in: The types don’t match. This means that you don’t have to rely on all possible types of conversions the compiler supports.

If you pass a value of the correct type already, then it just works, and no extra “magic” is involved.

If you pass a value of an incorrect type, either it will result in compiler error, or there will be an implicit conversion applicable. Rust’s implicit conversions are designed such that they are always cheap and non-problematic, another reason why knowing the precise Rules is not too important.

The resulting compiler error might often even contain a suggestion how to manually insert a relevant conversion.


So I’d say, it’s more important to learn what the different types mean, not what specific rules apply to Rust’s method resolution or implicit coercions. It’s an important first step to learn what differentiates types like String from &mut String, or &str; what differentiates [T; N] and &[T; N] and &[T].

With that, it’ll become possible for you to deduce whether the thing you have can be made to fit the thing the function expects, or not. For example, if you have a &mut String, you can use it to call a function expecting &str – whether or not that’s working implicitly, or by doing something like &x[…] or x.as_str() is largely irrelevant.

In particular, the rules for implicit conversions that are considered don’t even match the rules for what conversions are allowed. The method resolution heuristics will have the effect that with your owned but immutable variable let my_vector = vec![1, 2, 3];, or with a reference let y: &Vec<i32> = &x;, calling x.foo() or y.foo() will consider for example &mut [i32] methods, too; but such methods cannot possibly be called because you have no mutable access. You’d need to mark x as mut and/or make sure your reference y is a &mut Vec<i32> reference, respectively.


If you want to read up the precise rules anyways, there’s the Reference to look at.

6 Likes