I was reading through the book section about String
s and found they were using &*
combined together to convert a piece of text. The following is what it says:
use std::net::TcpStream;
TcpStream::connect("192.168.0.1:3000"); // Parameter is of type &str.
let addr_string = "192.168.0.1:3000".to_string();
TcpStream::connect(&*addr_string); // Convert `addr_string` to &str.
In other words, they are saying they are converting a String
to a &str
. But why is that conversion done using both of the aforementioned signs? Should this not be done using some other method? Does not the &
mean we are taking its reference, then using the *
to dereference it?
In short: the *
triggers an explicit deref, which can be overloaded via ops::Deref
.
More Detail
Look at this code:
let s = "hi".to_string(); // : String
let a = &s;
What's the type of a
? It's simply &String
! This shouldn't be very surprising, since we take the reference of a String
. Ok, but what about this?
let s = "hi".to_string(); // : String
let b = &*s; // equivalent to `&(*s)`
What's the type of b
? It's &str
! Wow, what happened?
Note that *s
is executed first. As most operators, the dereference operator *
is also overloadable and the usage of the operator can be considered syntax sugar for *std::ops::Deref::deref(&s)
(note that we recursively dereferencing here!). String
does overload this operator:
impl Deref for String {
type Target = str;
fn deref(&self) -> &str { ... }
}
So, *s
is actually *std::ops::Deref::deref(&s)
, in which the deref()
function has the return type &str
which is then dereferenced again. Thus, *s
has the type str
(note the lack of &
).
Since str
is unsized and not very handy on its own, we'd like to have a reference to it instead, namely &str
. We can do this by adding a &
in front of the expression! Tada, now we reached the type &str
!
&*s
is rather the manual and explicit form. Often, the Deref
-overload is used via automatic deref coercion. When the target type is fixed, the compiler will deref for you:
fn takes_string_slice(_: &str) {}
let s = "hi".to_string(); // : String
takes_string_slice(&s); // this works!
In general, &*
means to first dereference (*
) and then reference (&
) a value. In many cases, this would be silly, as we'd end up at the same thing.
However, Rust has deref coercions. Combined with the Deref
and DerefMut
traits, a type can dereference to a different type!
This is useful for String
s as that means that they can get all the methods from str
, it's useful for Vec<T>
as it gains the methods from [T]
, and it's super useful for all the smart pointers, like Box<T>
, which will have all the methods of the contained T
!
Following the chain for String
:
String --deref--> str --ref--> &str
Does not the &
mean we are taking its reference, then using the *
to dereference it?
No, your order of operations is backwards. *
and &
associate to the right. In this example, dereferencing is first, then referencing.
I think now you can do this &addr_string
(from a comment)
Sometimes, this will do the same thing. See What are Rust's exact auto-dereferencing rules? for the full details, but yes, a &String
can be passed to a function that requires a &str
. There are still times where you need to do this little dance by hand. The most common I can think of is:
let name: Option<String> = Some("Hello".to_string());
let name2: Option<&str> = name.as_ref().map(|s| &**s);
You'll note that we actually dereference twice:
&String -->deref--> String --deref--> str --ref--> &str
Although this case can now be done with name.as_ref().map(String::as_str);