Could anyone tell me what's the difference between the 2 ways for returning the &str value for name of User? Both compile but I'm not sure if there's a preferred one.
pub struct User {
name: String,
}
impl User {
pub fn name(&self) -> &str {
// way1
&self.name
// way2
// self.name.as_str()
}
}
Using & on a String gives a &String and relies on deref coercion to arrive at the desired &str type, but as_str unambiguously gives a &str (while using deref coercion internally). Without deref coercion one would have to write &s[..] to turn a String s into a &str.
Deref coercion converts a reference to a type that implements the Deref trait into a reference to another type. For example, deref
coercion can convert &String to &str because String implements
the Deref trait such that it returns &str. Deref coercion is a
convenience Rust performs on arguments to functions and methods, and
works only on types that implement the Deref trait. It happens
automatically when we pass a reference to a particular type’s value as
an argument to a function or method that doesn’t match the parameter
type in the function or method definition. A sequence of calls to the
deref method converts the type we provided into the type the
parameter needs. -- The Rust Programming Language (Chapter 15)
Some programmers may prefer one for its brevity, while others may prefer the other one for its clarity.
Related
This question already has answers here:
What are Rust's exact auto-dereferencing rules?
(4 answers)
Confusion over auto-dereferencing rules when using function
(1 answer)
Closed 4 years ago.
The following program works fine:
pub fn foo(_v: &str) -> bool {
false
}
fn main() {
let f = "hello world";
println!("{}", foo(&&&&f)); // note the number of & here
}
In fact it works on passing any number of &. How should I interpret what is going on ?
My rust version:
$ rustc --version
rustc 1.32.0-nightly (13dab66a6 2018-11-05)
From the Rust book:
Deref coercion is a convenience that Rust performs on arguments to
functions and methods. Deref coercion converts a reference to a type
that implements Deref into a reference to a type that Deref can
convert the original type into. Deref coercion happens automatically
when we pass a reference to a particular type’s value as an argument
to a function or method that doesn’t match the parameter type in the
function or method definition. A sequence of calls to the deref method
converts the type we provided into the type the parameter needs.
So basically, in function arguments the compiler will automatically remove any & written or implied until it gets to a type that can be passed to the function.
Because the compiler automatically dereferences the chain of references, you can imagine that it inserts as many * as necessary to get the right type:
foo(&&&f)
is converted to:
foo(&****(&&&f))
that leads to the right invocation:
foo(f)
The insertions of as many * as needed is actually performed by this blanket implementation of Deref trait:
impl<'a, T: ?Sized> Deref for &'a T {
type Target = T;
fn deref(&self) -> &T { *self }
}
Note: I've update my answer because in the original I used the term autoderef in the wrong way, see this post for details.
Consider this example:
fn main() {
let string: String = "A string".to_string();
let string_ref: &String = &string;
let str_ref_a: &str = string_ref; // A
let str_ref_b: &str = &string; // B
}
How exactly are lines A and B different? string_ref is of type &String, so my understanding is that in line A we have an example of Deref coercion. What about line B though? Is it correct to say that it has nothing to do with Deref coercion and we simply have "a direct borrowing of a String as str", due to this:
impl Borrow<str> for String {
#[inline]
fn borrow(&self) -> &str {
&self[..]
}
}
Both are essentially equivalent and involve deref coercion:
let str_ref_a: &str = string_ref; // A
let str_ref_b: &str = &string; // B
The value string above is of type String, so the expression &string is of type &String which coerces into &str due to deref coercion as String implements Deref<Target=str>.
Regarding your question about Borrow: No, you aren't calling borrow() on the string value. Instead, that would be:
let str_ref_b: &str = string.borrow(); // Borrow::borrow() on String
That is, unlike deref(), the call to borrow() isn't inserted automatically.
No. Both lines involve deref coercion.
The Borrow trait is not special in any way - it is not known to the compiler (not a lang item). The Deref trait is.
The difference between Deref and Borrow (and also AsRef) is that Deref can only have one implementation for a type (since Target is an associated type and not a generic parameter) while AsRef (and Borrow) take a generic parameter and thus can be implemented multiple times. This is because Deref is for smart pointers: you should implement Deref<Target = T> if I am a T (note that the accurate definition of "smart pointer" is in flux). String is a str (plus additional features), and Vec is a slice. Box<T> is T.
On the other hand, AsRef and Borrow are conversion traits. I should implement AsRef<T> if I can be viewed as T. String can be viewed as str. But take e.g. str and OsStr. str is not an OsStr. But it can be treated like it if it was. So it implements AsRef<OsStr>. Borrow is the same as AsRef except it has additional requirements (namely, it should have the same Eq, Hash and Ord as the original value).
An iterator can be transformed to a collection using the Iterator trait's collect method if the collection implements FromIterator.
let vec = (0..10).collect::<Vec<_>>();
let devec = (0..10).collect::<VecDeque<_>>();
Vec and VecDeque implement FromIterator trait.
The implementation of Iterator::collect method is:
fn collect<B: FromIterator<Self::Item>>(self) -> B
where
Self: Sized,
{
FromIterator::from_iter(self)
}
How does Rust understand to call from_iter method of Vec or VecDeque from FromIterator::from_iter(self)?
When the FromIterator trait has an implementor, it has the form:
impl<T> FromIterator for Vec<T> {
fn from_iter<I>(iter: I) -> Vec<T> { /* ... */ }
}
It returns the concrete type Self; that is the generic parameter B in the collect code.
When you type collect::<Vec<_>>(), you say that B is Vec<_> and therefore, that Vec<_>::from_iter() must be called.
The implementation of collect could have been written: <B as FromIterator>::from_iter(self), but it is more verbose.
Let's look at the definition of the FromIterator trait:
pub trait FromIterator<A>: Sized {
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self;
}
From how FromIterator::from_iter() is used in the implementation of the collect() method, Rust knows that the call to FromIterator::from_iter() takes the iterator self as parameter, and returns an object of type B. This information is matched against the definition of the from_iter() method, so Rust can conclude that the Self type of the FromIterator trait implementation must be B, and that the type A must be the item type of the iterator. The Self type of the FromIterator trait is precisely the type Rust needs to call from_iter() for.
The details of how type inference works are complex. Rust uses a variant of the Hindley-Milner type inference algorithm. You can find some further information in this document from the rustc guide.
Roughly, Rust uses all information it has about unknown types as constraints and iteratively refines its knowledge about generic types, until only a single type for each unknown type remains, or some type remains ambiguous. In the former case, type inference was successful and all types have been inferred, and in the latter case, Rust will throw an error stating that it needs further type annotations.
This question already has answers here:
What are Rust's exact auto-dereferencing rules?
(4 answers)
Confusion over auto-dereferencing rules when using function
(1 answer)
Closed 4 years ago.
The following program works fine:
pub fn foo(_v: &str) -> bool {
false
}
fn main() {
let f = "hello world";
println!("{}", foo(&&&&f)); // note the number of & here
}
In fact it works on passing any number of &. How should I interpret what is going on ?
My rust version:
$ rustc --version
rustc 1.32.0-nightly (13dab66a6 2018-11-05)
From the Rust book:
Deref coercion is a convenience that Rust performs on arguments to
functions and methods. Deref coercion converts a reference to a type
that implements Deref into a reference to a type that Deref can
convert the original type into. Deref coercion happens automatically
when we pass a reference to a particular type’s value as an argument
to a function or method that doesn’t match the parameter type in the
function or method definition. A sequence of calls to the deref method
converts the type we provided into the type the parameter needs.
So basically, in function arguments the compiler will automatically remove any & written or implied until it gets to a type that can be passed to the function.
Because the compiler automatically dereferences the chain of references, you can imagine that it inserts as many * as necessary to get the right type:
foo(&&&f)
is converted to:
foo(&****(&&&f))
that leads to the right invocation:
foo(f)
The insertions of as many * as needed is actually performed by this blanket implementation of Deref trait:
impl<'a, T: ?Sized> Deref for &'a T {
type Target = T;
fn deref(&self) -> &T { *self }
}
Note: I've update my answer because in the original I used the term autoderef in the wrong way, see this post for details.
In this example code from the Rust documentation:
fn takes_str(s: &str) { }
let s = String::from("Hello");
takes_str(&s);
What exactly is going on behind the scenes that causes &s to become a &str instead of a &String? The documentation seems to suggest that there's some dereferencing going on, but I thought * was for dereferencing, not &?
What's going on here is called deref coercions. These allow references to types that implement the Deref trait to be used in place of references to other types. As your example shows, &String can be used anywhere a &str is required, because String implements Deref to str.