Is there a difference between slicing and an explicit reborrow when converting Strings to &strs?

后端 未结 2 1189
悲哀的现实
悲哀的现实 2021-01-17 14:29

Are the following two examples equivalent?

Example 1:

let x = String::new();
let y = &x[..];

Example 2:

let x =         


        
相关标签:
2条回答
  • 2021-01-17 14:58

    They are completely the same for String and Vec.

    The [..] syntax results in a call to Index<RangeFull>::index() and it's not just sugar for [0..collection.len()]. The latter would introduce the cost of bound checking. Gladly this is not the case in Rust so they both are equally fast.


    Relevant code:

    • index of String
    • deref of String
    • index of Vec (just returns self which triggers the deref coercion thus executes exactly the same code as just deref)
    • deref of Vec
    0 讨论(0)
  • 2021-01-17 15:11

    In the case of String and Vec, they do the same thing. In general, however, they aren't quite equivalent.

    First, you have to understand Deref. This trait is implemented in cases where a type is logically "wrapping" some lower-level, simpler value. For example, all of the "smart pointer" types (Box, Rc, Arc) implement Deref to give you access to their contents.

    It is also implemented for String and Vec: String "derefs" to the simpler str, Vec<T> derefs to the simpler [T].

    Writing *s is just manually invoking Deref::deref to turn s into its "simpler form". It is almost always written &*s, however: although the Deref::deref signature says it returns a borrowed pointer (&Target), the compiler inserts a second automatic deref. This is so that, for example, { let x = Box::new(42i32); *x } results in an i32 rather than a &i32.

    So &*s is really just shorthand for Deref::deref(&s).

    s[..] is syntactic sugar for s.index(RangeFull), implemented by the Index trait. This means to slice the "whole range" of the thing being indexed; for both String and Vec, this gives you a slice of the entire contents. Again, the result is technically a borrowed pointer, but Rust auto-derefs this one as well, so it's also almost always written &s[..].

    So what's the difference? Hold that thought; let's talk about Deref chaining.

    To take a specific example, because you can view a String as a str, it would be really helpful to have all the methods available on strs automatically available on Strings as well. Rather than inheritance, Rust does this by Deref chaining.

    The way it works is that when you ask for a particular method on a value, Rust first looks at the methods defined for that specific type. Let's say it doesn't find the method you asked for; before giving up, Rust will check for a Deref implementation. If it finds one, it invokes it and then tries again.

    This means that when you call s.chars() where s is a String, what's actually happening is that you're calling s.deref().chars(), because String doesn't have a method called chars, but str does (scroll up to see that String only gets this method because it implements Deref<Target=str>).

    Getting back to the original question, the difference between &*s and &s[..] is in what happens when s is not just String or Vec<T>. Let's take a few examples:

    • s: String; &*s: &str, &s[..]: &str.
    • s: &String: &*s: &String, &s[..]: &str.
    • s: Box<String>: &*s: &String, &s[..]: &str.
    • s: Box<Rc<&String>>: &*s: &Rc<&String>, &s[..]: &str.

    &*s only ever peels away one layer of indirection. &s[..] peels away all of them. This is because none of Box, Rc, &, etc. implement the Index trait, so Deref chaining causes the call to s.index(RangeFull) to chain through all those intermediate layers.

    Which one should you use? Whichever you want. Use &*s (or &**s, or &***s) if you want to control exactly how many layers of indirection you want to strip off. Use &s[..] if you want to strip them all off and just get at the innermost representation of the value.

    Or, you can do what I do and use &*s because it reads left-to-right, whereas &s[..] reads left-to-right-to-left-again and that annoys me. :)

    Addendum

    • There's the related concept of Deref coercions.
    • There's also DerefMut and IndexMut which do all of the above, but for &mut instead of &.
    0 讨论(0)
提交回复
热议问题