I was looking through Rust's source code to better acquaint myself with the language. I came across this snippet.
// Collect program arguments as a Vec<String>.
let cmd: Vec<_> = env::args().collect();
// Some unrelated code omitted here.
match subcommand::parse_name(&cmd[1][..]) {
// It did some stuff here.
}
I didn't understand the [..]
. So, I went and checked out the declaration of parse_name
:
pub fn parse_name(name: &str) -> Option<Box<Subcommand>>
It's what I expected, but I still don't get the [..]
. What does it mean in this context? Isn't it just passing the first String
in cmd
as a &str
? If so, is this equivalent to just writing cmd[1]
? Why did they do it this way?
Two periods (..
) is the range operator. You can find this in the Operators and Symbols Appendix of the Rust book. There are six flavors:
Range
: 1..10
RangeFrom
: 1..
RangeTo
: ..10
RangeFull
: ..
RangeInclusive
: 1..=10
RangeToInclusive
: ..=10
When no item occupies an end position, the range goes on "forever" in that direction.
This combines with the Index
trait (or IndexMut
, if mutation is required). In your example, you have a string slice (kind of, see next point) that you are applying indexing to: "foo"[2..]
.
Specifically, &str
implements Index
as
Returns a slice of the given string from the byte range
Then there's a third bit of ergonomics happening: Deref
(or DerefMut
in similar cases). String
implements Deref
by returning a &str
, so any method available to a &str
is available to a String
.
This is just a way to explicitly coerce from String
to &str
. In this case, the [..]
is actually unnecessary as Deref
coercions means that parse_name(&args[1])
is valid too: &String
will borrow to &str
implicitly.
The [ ]
indexing operator is calling the std::ops::Index
trait, and the ..
syntax is creating a std::ops::RangeFull
value. cmd
is a Vec<String>
, since std::env::args()
returns an Iterator
over String
s.
Hence, the foo[..]
syntax is calling the implementation of Index<RangeFull>
for String
(which you can see in the list of Implementors on the Index
page). The implementation looks like:
impl ops::Index<ops::RangeFull> for String {
type Output = str;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &str {
unsafe { mem::transmute(&*self.vec) }
}
}
The &*self.vec
is borrowing the String
's internal Vec<u8>
to &[u8]
, and then the transmute
is explicitly casting that to a &str
, which is safe because String
's API ensures that the internal Vec<u8>
is valid UTF-8, which is what str
requires.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With