9

I was reading through the book section about Strings 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?

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
D. Ataro
  • 1,242
  • 9
  • 31

2 Answers2

18

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!
Lukas Kalbertodt
  • 55,166
  • 13
  • 173
  • 236
10

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 Strings 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);

Community
  • 1
  • 1
Shepmaster
  • 274,917
  • 47
  • 731
  • 969