Are the following two examples equivalent?
Example 1:
let x = String::new();
let y = &x[..];
Example 2:
let x = String::new();
let y = &*x;
Is one more efficient than the other or are they basically the same?
Are the following two examples equivalent?
Example 1:
let x = String::new();
let y = &x[..];
Example 2:
let x = String::new();
let y = &*x;
Is one more efficient than the other or are they basically the same?
They are completely the same for
String
andVec
.The
[..]
syntax results in a call toIndex<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
ofString
deref
ofString
index
ofVec
(just returnsself
which triggers the deref coercion thus executes exactly the same code as justderef
)deref
ofVec
In the case of
String
andVec
, 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
) implementDeref
to give you access to their contents.It is also implemented for
String
andVec
:String
"derefs" to the simplerstr
,Vec<T>
derefs to the simpler[T]
.Writing
*s
is just manually invokingDeref::deref
to turns
into its "simpler form". It is almost always written&*s
, however: although theDeref::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 ani32
rather than a&i32
.So
&*s
is really just shorthand forDeref::deref(&s)
.s[..]
is syntactic sugar fors.index(RangeFull)
, implemented by theIndex
trait. This means to slice the "whole range" of the thing being indexed; for bothString
andVec
, 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 astr
, it would be really helpful to have all the methods available onstr
s automatically available onString
s as well. Rather than inheritance, Rust does this byDeref
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()
wheres
is aString
, what's actually happening is that you're callings.deref().chars()
, becauseString
doesn't have a method calledchars
, butstr
does (scroll up to see thatString
only gets this method because it implementsDeref<Target=str>
).Getting back to the original question, the difference between
&*s
and&s[..]
is in what happens whens
is not justString
orVec<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 ofBox
,Rc
,&
, etc. implement theIndex
trait, soDeref
chaining causes the call tos.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
Deref
coercions.DerefMut
andIndexMut
which do all of the above, but for&mut
instead of&
.