0

I was reading the doc from rust lang website and in chapter 4 they did the following example:

let s = String::from("hello world");

let hello = &s[0..5];
let world = &s[6..11];

hello is of type &str that I created from a variable s of type String.

Some rows below they define the following function:

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

This time s is of type &String but still &s[0..i] gave me a &str slice. How is it possible? I thought that the correct way to achieve this would be something like &((*str)[0..i]).

Am I missing something? Maybe during the [0..i] operation Rust auto deference the variable?

Thanks

Taz
  • 11
  • 1
  • 6
  • See https://stackoverflow.com/questions/28519997/what-are-rusts-exact-auto-dereferencing-rules – Jmb Aug 07 '20 at 06:45

2 Answers2

0

Maybe during the [0..i] operation Rust auto deference the variable?

This is exactly what happens. When you call methods/index a reference, it automatically dereferences before applying the method. This behavior can also be manually implemented with the Deref trait. String implements the Deref with a target of str, which means when you call str methods on String. Read more about deref coercion here.

Aplet123
  • 27,379
  • 1
  • 13
  • 35
0

It's important to realize what happens with &s[1..5], and that it's &(s[1..5]), namely, s[1..5] is first first evaluated, this returns a value of type str, and a reference to that value is taken. In fact, there's even more indirection: x[y] in rust is actually syntactic sugar for *std::ops::Index::index(x,y). Note the dereference, as this function always returns a reference, which is then dereferenced by the sugar, and then it is referenced again by the & in our code — naturally, the compiler will optimize this and ensure we are not pointlessly taking references to only dereference them again.

It so happens that the String type does support the Index<Range<usize>> trait and it's Index::output type is str.

It also happens that the str type supports the same, and that it's output type is also str, viā a blanket implementation of SliceIndex.

On your question of auto-dereferencing, it is true that Rust has a Deref trait defined on String as well so that in many contexts, such as this one, &String is automatically cast to &str — any context that accepts a &str also accepts a &String, meaning that the implementation on Index<usize> on String is actually for optimization to avoid this indirection. If it not were there, the code would still work, and perhaps the compiler could even optimize the indirection away.

But that automatic casting is not why it works — it simply works because indexing is defined on many different types.

Finally:

I thought that the correct way to achieve this would be something like &((*str)[0..i]).

This would not work regardless, a &str is not the same as a &String and cannot be dereferenced to a String like a &String. In fact, a &str in many ways is closer to a String than it is to a &String. a &str is really just a fat pointer to a sequence of unicode bytes, also containing the length of said sequence in the second word; a String is, if one will, an extra-fat pointer that also contains the current capacity of the buffer with it, and owns the buffer it points to, so it can delete and resize it.

trentcl
  • 17,886
  • 5
  • 37
  • 60
Zorf
  • 5,650
  • 1
  • 25
  • 24