While programming some small beginner exercises trying to get used to Rust, I came across some output I don't understand using Vec::get
. Here's the code:
fn main() {
let command = [('G', 'H'), ('H', '5')];
for i in 0..3 {
print!(" {} ", i);
println!("{:?}", command.get(i));
}
}
the output is
0 Some(('G', 'H'))
1 Some(('H', '5'))
2 None
I've dabbled in Haskell before, and by that I mean looked at a tutorial site for 10 minutes and ran back to C++, but I remember reading something about Some
and None
for Haskell. I was surprised to see this here in Rust. Could someone explain why .get()
returns Some
or None
?
The signature of get
(for slices, not Vec
, since you're using an array/slice) is
fn get(&self, index: usize) -> Option<&T>
That is, it returns an Option
, which is an enum defined like
pub enum Option<T> {
None,
Some(T),
}
None
and Some
are the variants of the enum, that is, a value with type Option<T>
can either be a None
, or it can be a Some
containing a value of type T
.
This is the same as the core data Maybe a = Nothing | Just a
type in Haskell; both represent an optional value, it's either there (Some
/Just
), or it's not (None
/Nothing
).
These types are often used to represent failure when there's only one possibility for why something failed, for example, .get
uses Option
to give type-safe bounds-checked array access: it returns None
(i.e. no data) when the index is out of bounds, otherwise it returns a Some
containing the requested pointer.
command
is not a vector (type Vec<T>
), it is a fixed-size array (type [(char, char); 2]
in your case), and arrays are automatically borrowed into slices (views into arrays), hence you can use all methods defined on slices, including get
:
Returns the element of a slice at the given index, or None
if the index is out of bounds.
The behavior is pretty obvious: when given index is valid, it returns Some
with the element under that index, otherwise it returns None
.
There is another way to access elements in a slice - the indexing operator, which should be familiar to you:
let nums = [1, 2, 3];
let x = nums[1];
It returns the element of the slice directly, but it will fail the current task if the index is out of bounds:
fn main() {
let x = [1, 2];
for i in 0..3 {
println!("{}", x[i]);
}
}
This program fails:
% ./main2
1
2
task '<main>' failed at 'index out of bounds: the len is 2 but the index is 2', main2.rs:4
get()
method is needed for convenience; it saves you from checking in advance if the given index is valid.
If you don't know what Some
and None
really are and why they are needed in general, you should read the official tutorial, it explains it because it is very basic concept.
Think of Some
and None
as the canonical "safe" way of working around the fact that the Rust language does not support "safe" use of NULL
pointers. Since the length of your Vec
is 3, and you have only specified two pairs, the third pair is effectively NULL
; instead of returning NULL
, it returns None
.
Rust provides safety guarantees by forcing us at compile-time, via Some
/ None
, to always deal with the possibility of None
being returned.