## DEV Community Petr Janik

Posted on • Updated on

# Generics

To prevent duplication of code like this:

``````fn largest_i32(list: &[i32]) -> i32 {
let mut largest = list;

for &item in list {
if item > largest {
largest = item;
}
}

largest
}

fn largest_char(list: &[char]) -> char {
let mut largest = list;

for &item in list {
if item > largest {
largest = item;
}
}

largest
}
``````

we can simplify the code using generics:

``````fn largest<T>(list: &[T]) -> T {
let mut largest = list;

for &item in list {
if item > largest {
largest = item;
}
}

largest
}
``````

## Structures

``````struct Point<T> {
x: T,
y: T,
}

fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
}
``````
``````struct Point<T, U> {
x: T,
y: U,
}

fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
}
``````

## Method

``````struct Point<T> {
x: T,
y: T,
}

impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}

fn main() {
let p = Point { x: 5, y: 10 };

println!("p.x = {}", p.x());
}
``````

## Enum

``````enum Result<T, E> {
Ok(T),
Err(E),
}
``````

# Traits

``````pub trait Summary {
fn summarize(&self) -> String;
}

pub struct NewsArticle {
pub location: String,
pub author: String,
pub content: String,
}

impl Summary for NewsArticle {
fn summarize(&self) -> String {
self.author, self.location)
}
}

pub struct Tweet {
pub content: String,
pub retweet: bool,
}

impl Summary for Tweet {
fn summarize(&self) -> String {
}
}
``````

## Default implementation

``````pub trait Summary {
fn summarize(&self) -> String {
}
}
``````

## Trait as a parameter

``````pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
``````

## Bound trait

``````pub fn notify<T: Summary>(item1: &T, item2: &T) { }
``````

## Multiple traits

``````fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 { }

// or
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{}
``````

## Return trait-compliant structure from function

``````fn returns_summarizable() -> impl Summary {
Tweet {
content: String::from(
"of course, as you probably already know, people",
),
retweet: false,
}
}
``````

# Iterators

Iterator is anything that implements an Iterator trait.

``````pub trait Iterator {
type Item;

fn next(&mut self) -> Option<Self::Item>;
}
``````

## Iterate vector

``````fn main() {
let v1 = vec![1, 2, 3];

let v1_iter = v1.iter();

for val in v1_iter {
println!("Got: {}", val);
}

// or
for val in v1 {
println!("Got: {}", val);
}
}
``````

# Functional constructs

## Map

``````let a = [1, 2, 3];

let mut iter = a.iter().map(|x| 2 * x);

assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), Some(6));
assert_eq!(iter.next(), None);
``````

## Filter

``````let a = [1, 4, 2, 3];

let sum = a.iter()
.cloned()
.inspect(|x| println!("about to filter: {}", x))
.filter(|x| x % 2 == 0)
.inspect(|x| println!("made it through filter: {}", x))
``````

## Enumerate

``````let a = ['a', 'b', 'c'];

let mut iter = a.iter().enumerate();

assert_eq!(iter.next(), Some((0, &'a')));
assert_eq!(iter.next(), Some((1, &'b')));
assert_eq!(iter.next(), Some((2, &'c')));
assert_eq!(iter.next(), None);
``````

## Skip

``````let a = [1, 2, 3];

let mut iter = a.iter().skip(2);

assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), None);
``````

## Take

``````let a = [1, 2, 3];

let mut iter = a.iter().take(2);

assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);
``````

## Sum

``````let a = [1, 4, 2, 3];

let sum: i32 = a.iter()
.cloned()
.filter(|x| x % 2 == 0)
.sum();

println!("{}", sum);
``````

## Zip

``````let enumerate: Vec<_> = "foo".chars().enumerate().collect();

// lazy evaluation of (0..)
let zipper: Vec<_> = (0..).zip("foo".chars()).collect();

assert_eq!((0, 'f'), enumerate);
assert_eq!((0, 'f'), zipper);

assert_eq!((1, 'o'), enumerate);
assert_eq!((1, 'o'), zipper);

assert_eq!((2, 'o'), enumerate);
assert_eq!((2, 'o'), zipper);
``````

## Fold

``````let a = [1, 2, 3];

// the sum of all elements of the array
let sum = a.iter().fold(0, |acc, x| acc + x);

assert_eq!(sum, 6);
``````

It is better to use `for` than iterators for side effects.

# Data structures

## Vec

when we want to:

• push elements at the end
• use it as a stack
• have an array with dynamic size
• have an array on heap
``````fn main() {
let mut v = Vec::new();

v.push(5);
v.push(6);
v.push(7);
v.push(8);
}
``````

## VecDeque

when we want to:

• have a vector that allows inserting elements at the beginning
• use it as a queue
• use it as a double-ended queue
``````use std::collections::VecDeque;

let mut buf = VecDeque::new();
buf.push_back(4);
buf.push_back(5);
buf.insert(0, 3);
if let Some(elem) = buf.get_mut(1) {
*elem = 7;
}

assert_eq!(buf, 3);
assert_eq!(buf, 7);
assert_eq!(buf, 5);
``````

## HashMap

when we want to:

• map keys to values
• have a cache
• use it as a dictionary
``````fn main() {
use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

// insert a value only if it doesn't already exist
scores.entry(String::from("Blue")).or_insert(70);
scores.entry(String::from("Red")).or_insert(80);

println!("Blue score: {}", scores.get("Blue").unwrap());
println!("Yellow score: {}", scores["Yellow"]);
println!("Red score: {}", scores["Red"]);
}
``````

## HashSet

when we want to:

• store elements withou duplicates
``````use std::collections::HashSet;
// Type inference lets us omit an explicit type signature (which
// would be `HashSet<String>` in this example).
let mut books = HashSet::new();

books.insert("A Dance With Dragons".to_string());
books.insert("To Kill a Mockingbird".to_string());
books.insert("The Odyssey".to_string());
books.insert("The Great Gatsby".to_string());

// Check for a specific one.
if !books.contains("The Winds of Winter") {
println!("We have {} books, but The Winds of Winter ain't one.",
books.len());
}

// Remove a book.
books.remove("The Odyssey");
``````

## BTreeMap

when we want to:

• have a map ordered by keys
• get elements in range
• get smallest or largest element fast
• find keys that are greater or smaller that other
``````fn main() {
use std::collections::BTreeMap;

// type inference lets us omit an explicit type signature (which
// would be `BTreeMap<&str, u8>` in this example).
let mut player_stats = BTreeMap::new();

fn random_stat_buff() -> u8 {
// could actually return some random value here - let's just return
// some fixed value for now
println!("Some intensive computation.");
42
}

// insert a key only if it doesn't already exist
player_stats.entry("health").or_insert(100);
// unnecessary intensive computation
player_stats.entry("health").or_insert(random_stat_buff());
// insert a key using a function that provides a new value only if it
player_stats
.entry("health")
.or_insert_with(random_stat_buff);

// update a key, guarding against the key possibly not being set
let stat = player_stats.entry("attack").or_insert(100);
*stat += random_stat_buff();
}
``````
``````use std::collections::BTreeMap;

let mut movie_reviews = BTreeMap::new();

// review some movies.
movie_reviews.insert("Office Space",       "Deals with real issues in the workplace.");
movie_reviews.insert("Pulp Fiction",       "Masterpiece.");
movie_reviews.insert("The Godfather",      "Very enjoyable.");
movie_reviews.insert("The Blues Brothers", "I liked it a lot.");

// check for a specific one.
if !movie_reviews.contains_key("Les Misérables") {
println!("We've got {} reviews, but Les Misérables ain't one.",
movie_reviews.len());
}

// oops, this review has a lot of spelling mistakes, let's delete it.
movie_reviews.remove("The Blues Brothers");

// look up the values associated with some keys.
let to_find = ["Up!", "Office Space"];
for movie in &to_find {
match movie_reviews.get(movie) {
Some(review) => println!("{}: {}", movie, review),
None => println!("{} is unreviewed.", movie)
}
}

// Look up the value for a key (will panic if the key is not found).
println!("Movie review: {}", movie_reviews["Office Space"]);

// iterate over everything.
for (movie, review) in &movie_reviews {
println!("{}: \"{}\"", movie, review);
}
``````

## BinaryHeap

when we want to:

• save elements but we need to process only the largest or most important
• have a priority queue
``````use std::collections::BinaryHeap;

// Type inference lets us omit an explicit type signature (which
// would be `BinaryHeap<i32>` in this example).
let mut heap = BinaryHeap::new();

// We can use peek to look at the next item in the heap. In this case,
// there's no items in there yet so we get None.
assert_eq!(heap.peek(), None);

heap.push(1);
heap.push(5);
heap.push(2);

// Now peek shows the most important item in the heap.
assert_eq!(heap.peek(), Some(&5));

// We can check the length of a heap.
assert_eq!(heap.len(), 3);

// We can iterate over the items in the heap, although they are returned in
// a random order.
for x in &heap {
println!("{}", x);
}

// If we instead pop these scores, they should come back in order.
assert_eq!(heap.pop(), Some(5));
assert_eq!(heap.pop(), Some(2));
assert_eq!(heap.pop(), Some(1));
assert_eq!(heap.pop(), None);

// We can clear the heap of any remaining items.
heap.clear();

// The heap should now be empty.
assert!(heap.is_empty())
``````

# Files

``````use std::env;
use std::fs;

fn main() {
let filename = "foo.txt";
println!("In file {}", filename);

.expect("Something went wrong when reading the file");

println!("With text:\n{}", contents);
}
``````

## Working with large files

``````use std::fs::File;

fn main() -> io::Result<()> {
let file = File::open("foo.txt")?;

println!("{}", line?);
}

Ok(())
}
``````

## Write to file

``````use std::io::prelude::*;
use std::io::BufWriter;
use std::fs::File;

fn main() -> std::io::Result<()> {
let mut buffer = BufWriter::new(File::create("foo.txt")?);

buffer.write_all(b"some bytes")?;
buffer.write_all("Hello World".as_bytes())?;
buffer.flush()?;
Ok(())
}
``````

## Append to file

``````use std::fs::OpenOptions;
use std::io::prelude::*;

fn main() {
let mut file = OpenOptions::new()
.write(true)  // before version 1.8, both write and append
.append(true) // had to be true; now append(true) is enough
.create(true)
.open("foo.txt")
.unwrap();

if let Err(e) = writeln!(file, "A new line!") {
eprintln!("Couldn't write to file: {}", e);
}
}
``````

# Exercises

Create a program that reads password entries from first file in one format and writes them to second file in CSV format.
When there are multiple entries with the same url and login, export the last one.

### Solution

Create an adventure game. There will be a file with scenes. Each scene contains a story and choices the user can make. Each choice moves the user to another scene. Some scenes will be marked as end scenes.

### Solution

Check out my learning Rust repo on GitHub!

## petr7555 / Learning-Rust 