<commit_before># Standard Library Containers

## Reading the Documentation

In this section I'll briefly introduce some very useful parts of the Rust standard
library. The documentation (as always) is excellent but a little context and a few examples is
always useful.

Initially, reading the Rust documentation can be challenging. I'll take the `Vec` struct as an
example.  A useful tip is to tick the '[-]' box to collapse the docs. (If you download the
standard library source using `rustup component add rust-src` a '[src]' link will appear next to this.)
This gives you a bird's eye view of all the available methods.

The first point to notice is that _not all possible methods_ are defined on `Vec` itself. They are (mostly)
mutable methods that change the vector, e.g. `push`. Some methods are only implemented for vectors where
the type matches some constraint. For example, you can only call `dedup` (remove duplicates) if the
type is indeed something that can be compared for equality.  There are multiple `impl` blocks that
define `Vec` for different type constraints.

Then there's the very special relationship between `Vec<T>` and `&[T]`.  Any method that works on
slices will also directly work on vectors, without explicitly having to use the `as_slice` method.
This relationship is expressed by `Deref<Target=[T]>`. This also kicks in when you pass a vector by
mutable reference to something that expects a slice - this is one of the few places where
a conversion between types happens automatically. So slice methods like `first`, which maybe-returns
a reference to the first element, or `last`, work for vectors as well. Many of the methods are similar
to the corresponding string methods, so there's `split_at` for getting a pair of slices spit at an index,
`starts_with` to check whether a vector starts with sequence of values, and `contains` to check whether
a vector contains a particular value.

There's no `search` method for finding the index of a particular value, but here's a rule of thumb:
if you can't find a method on the container, look for a method on the iterator:

```rust
    let v = vec![10,20,30,40,50];
    assert_eq!(v.iter().position(|&i| i == 30).unwrap(), 2);
```

(The `&` is because this is an iterator over _references_ - alternatively you could say `*i == 30` for
the comparison.)

Likewise, there's no `map` method on vectors, because `iter().map(...).collect()` will do the job
just as well. Rust does not like to allocate unnecessarily - often you don't need the result of that `map`
as an actual allocated vector.

In other languages, things like `map` are functions. For instance, in C++ `std::count` is a function that
takes two C++ iterators to establish a range, but it's an iterator method in Rust.

So I'd suggest you become familiar with all the iterator methods, because they are crucial to writing
good Rust code without having to write loops all the time. As always, write little programs to explore
iterator methods, rather than wrestling with them in the context of a more complicated program.

The `Vec<T>` and `&[T]` methods are followed by the common traits: vectors know how to do a debug display of themselves
(but only if the elements implement `Debug`). Likewise, they are clonable if their elements are clonable.
They implement `Drop`, which happens when vectors get to finally die; memory is released,
and all the elements are dropped as well.

The `Extend` trait means values from iterators can be added to a vector without a loop:

```rust
v.extend([60,70,80].iter());
let mut strings = vec!["hello".to_string(), "dolly".to_string()];
strings.extend(["you","are","fine"].iter().map(|s| s.to_string()));
```

There's also `FromIterator`, which lets vectors be _constructed_ from iterators. (The iterator `collect`
method leans on this.)

Any container needs to be iterable as well. Recall that there are three kinds of iterators:

```rust
for x in v {...} // returns T, consumes v
for x in &v {...} // returns &T
for x in &mut v {...} // returns &mut T
```
The `for` statement relies on the `IntoIterator` trait, and there's indeed three implementations.

Then there is indexing, controlled by `Index` (reading from a vector) and `IndexMut` (modifying a
vector.)  There are many possibilities, because there's slice indexing as well, like `v[0..2]`,
returning these return slices, as well as plain `v[0]` which returns a reference to the first element.

There's a few implementations of the `From` trait. For instance `Vec::from("hello".to_string())`
will give you a vector of the underlying bytes of the string `Vec<u8>`.
Now, there's already a method 'into_bytes` on `String`, so why the redundancy?
It seems confusing to have multiple ways of doing the same thing.
But it's needed because explicit traits make generic methods possible.

We've met this before with `String` - there seem to be so many ways to create strings! There's the
`to_string` method on `&str` - this comes from the `ToString` trait and will be implemented for anything
that knows how to `Display` itself.  There is `String::from` which comes from the `From` trait. There's
also `Into` which is the inverse of `From` and is automatically available if `From` exists.
If a function argument expects a `String`, you will often
see the value passed as `"some text".into()` since string slices implement `Into<String>`.

Sometimes limitations of the Rust type system make things clumsy. An example here is how `PartialEq`
is _separately_ defined for arrays up to size 32!  This will get better. This allows the convenience
of directly comparing vectors with arrays, but beware the limit.

This is still clearer than what the C++ docs say about `std::vector`

> The requirements that are imposed on the elements depend on the actual operations performed
> on the container. Generally, it is required that element type is a complete type and meets
> the requirements of Erasable, but many member functions impose stricter requirements.

Clearly, you're on your own! The explicitness of Rust is daunting at first, but as you learn to
read the constraints you will know exactly what any particular method of `Vec` requires.

## Maps

_Maps_  (sometimes called _associative arrays_ or _dicts_) let you look up values<commit_msg>section 5: error handling in iterators<commit_after># Standard Library Containers

## Reading the Documentation

In this section I'll briefly introduce some very useful parts of the Rust standard
library. The documentation (as always) is excellent but a little context and a few examples is
always useful.

Initially, reading the Rust documentation can be challenging. I'll take the `Vec` struct as an
example.  A useful tip is to tick the '[-]' box to collapse the docs. (If you download the
standard library source using `rustup component add rust-src` a '[src]' link will appear next to this.)
This gives you a bird's eye view of all the available methods.

The first point to notice is that _not all possible methods_ are defined on `Vec` itself. They are (mostly)
mutable methods that change the vector, e.g. `push`. Some methods are only implemented for vectors where
the type matches some constraint. For example, you can only call `dedup` (remove duplicates) if the
type is indeed something that can be compared for equality.  There are multiple `impl` blocks that
define `Vec` for different type constraints.

Then there's the very special relationship between `Vec<T>` and `&[T]`.  Any method that works on
slices will also directly work on vectors, without explicitly having to use the `as_slice` method.
This relationship is expressed by `Deref<Target=[T]>`. This also kicks in when you pass a vector by
mutable reference to something that expects a slice - this is one of the few places where
a conversion between types happens automatically. So slice methods like `first`, which maybe-returns
a reference to the first element, or `last`, work for vectors as well. Many of the methods are similar
to the corresponding string methods, so there's `split_at` for getting a pair of slices spit at an index,
`starts_with` to check whether a vector starts with sequence of values, and `contains` to check whether
a vector contains a particular value.

There's no `search` method for finding the index of a particular value, but here's a rule of thumb:
if you can't find a method on the container, look for a method on the iterator:

```rust
    let v = vec![10,20,30,40,50];
    assert_eq!(v.iter().position(|&i| i == 30).unwrap(), 2);
```

(The `&` is because this is an iterator over _references_ - alternatively you could say `*i == 30` for
the comparison.)

Likewise, there's no `map` method on vectors, because `iter().map(...).collect()` will do the job
just as well. Rust does not like to allocate unnecessarily - often you don't need the result of that `map`
as an actual allocated vector.

In other languages, things like `map` are functions. For instance, in C++ `std::count` is a function that
takes two C++ iterators to establish a range, but it's an iterator method in Rust.

So I'd suggest you become familiar with all the iterator methods, because they are crucial to writing
good Rust code without having to write loops all the time. As always, write little programs to explore
iterator methods, rather than wrestling with them in the context of a more complicated program.

The `Vec<T>` and `&[T]` methods are followed by the common traits: vectors know how to do a debug display of themselves
(but only if the elements implement `Debug`). Likewise, they are clonable if their elements are clonable.
They implement `Drop`, which happens when vectors get to finally die; memory is released,
and all the elements are dropped as well.

The `Extend` trait means values from iterators can be added to a vector without a loop:

```rust
v.extend([60,70,80].iter());
let mut strings = vec!["hello".to_string(), "dolly".to_string()];
strings.extend(["you","are","fine"].iter().map(|s| s.to_string()));
```

There's also `FromIterator`, which lets vectors be _constructed_ from iterators. (The iterator `collect`
method leans on this.)

Any container needs to be iterable as well. Recall that there are three kinds of iterators:

```rust
for x in v {...} // returns T, consumes v
for x in &v {...} // returns &T
for x in &mut v {...} // returns &mut T
```
The `for` statement relies on the `IntoIterator` trait, and there's indeed three implementations.

Then there is indexing, controlled by `Index` (reading from a vector) and `IndexMut` (modifying a
vector.)  There are many possibilities, because there's slice indexing as well, like `v[0..2]`,
returning these return slices, as well as plain `v[0]` which returns a reference to the first element.

There's a few implementations of the `From` trait. For instance `Vec::from("hello".to_string())`
will give you a vector of the underlying bytes of the string `Vec<u8>`.
Now, there's already a method 'into_bytes` on `String`, so why the redundancy?
It seems confusing to have multiple ways of doing the same thing.
But it's needed because explicit traits make generic methods possible.

We've met this before with `String` - there seem to be so many ways to create strings! There's the
`to_string` method on `&str` - this comes from the `ToString` trait and will be implemented for anything
that knows how to `Display` itself.  There is `String::from` which comes from the `From` trait. There's
also `Into` which is the inverse of `From` and is automatically available if `From` exists.
If a function argument expects a `String`, you will often
see the value passed as `"some text".into()` since string slices implement `Into<String>`.

Sometimes limitations of the Rust type system make things clumsy. An example here is how `PartialEq`
is _separately_ defined for arrays up to size 32!  This will get better. This allows the convenience
of directly comparing vectors with arrays, but beware the limit.

And there are [Hidden Gems](http://xion.io/post/code/rust-iter-patterns.html) buried
deep in the documentation. As Karol says "Because let’s be honest: no one scrolls that far".
How does one handle errors in an iterator? Say you map over some operation that will
fail, and then wish to collect the results:

```rust
fn main() {
    let nums = ["5","52","65"];
    let iter = nums.iter().map(|s| s.parse::<i32>());
    let converted: Vec<_> = iter.collect();
    println!("{:?}",converted);
}
//[Ok(5), Ok(52), Ok(65)]
```

Fair enough, but now you have to unwrap these errors - carefully!.
But Rust already knows how to do the Right Thing,
if you ask for the vector to be _contained_ in a `Result`:

```rust
    let converted: Result<Vec<_>,_> = iter.collect();
//Ok([5, 52, 65])
```

And if there was a bad conversion? Then you would just get `Err` with the first
error encountered. It's a good example of how extremely flexible `convert` is.

So there's a _lot_ of detail in the documentation. 
But this is clearer than what the C++ docs say about `std::vector`

> The requirements that are imposed on the elements depend on the actual operations performed
> on the container. Generally, it is required that element type is a complete type and meets
> the requirements of Erasable, but many member functions impose stricter requirements.

Clearly, you're on your own! The explicitness of Rust is daunting at first, but as you learn to
read the constraints you will know exactly what any particular method of `Vec` requires.

## Maps

_Maps_  (sometimes called _associative arrays_ or _dicts_) let you look up values
{'fraction_non_alphanumeric': 0.08003835939805252, 'fraction_numerical': 0.005311301268810859, 'mean_word_length': 4.506498781478473, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2187680', 'n_tokens_mistral': 3684, 'n_tokens_neox': 3476, 'n_words': 2084}