Изброени типове и съпоставяне на образци

22 октомври 2019

Преговор

Преговор

Преговор

Преговор

Преговор

Enums

1 2 3 4
enum IpAddrKind {
    V4,
    V6,
}

Enums

Инстанциране

1 2
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

Enums

Параметър

1 2 3 4
fn route(ip_type: IpAddrKind) { }

route(IpAddrKind::V4);
route(IpAddrKind::V6);

Enums

Данни

1 2 3 4 5 6 7 8 9 10 11 12 13 14
struct IpAddr {
    kind: IpAddrKind,
    address: String,
}

let home = IpAddr {
    kind: IpAddrKind::V4,
    address: String::from("127.0.0.1"),
};

let loopback = IpAddr {
    kind: IpAddrKind::V6,
    address: String::from("::1"),
};

Enums

Данни

По-удобен и четим начин

1 2 3 4 5 6 7 8
enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));

let loopback = IpAddr::V6(String::from("::1"));

Enums

Данни

Може да спестим памет като знаем че IPv4 използва стойности от 0-255

1 2 3 4 5 6 7 8
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

let home = IpAddr::V4(127, 0, 0, 1);

let loopback = IpAddr::V6(String::from("::1"));

Enums

Защо?

Enums

Защо?

Enums

Защо?

Enums

Защо?

1 2 3 4 5 6 7 8
struct IpAddr {
    v4: (u8, u8, u8, u8),
    v6: String,
}

// Warning: фалшив Rust код, няма NULL
let home = IpAddr { v4: (127, 0, 0, 1), v6: NULL };
let loopback = IpAddr { v4: NULL, v6: String::from("::1") };

Не е особено ясно -- човек трябва "да се усети" като чете кода.

Enums

Варианти

1 2 3 4 5 6 7 8 9 10 11
enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i64, i64, i64),
}

Message::Quit;
Message::Move { x: 3, y: 4 };
Message::Write(String::from("baba"));
Message::ChangeColor(255, 0, 0);

Enum варианти като структури

1 2 3 4 5 6 7 8 9 10 11 12
struct QuitMessage; // unit struct
struct MoveMessage {
    x: i64,
    y: i64,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i64, i64, i64); // tuple struct

QuitMessage;
MoveMessage { x: 3, y: 4 };
WriteMessage(String::from("baba"));
ChangeColorMessage(255, 0, 0);

Методи

1 2 3 4 5 6 7 8 9 10
enum Message { ... }

impl Message {
    fn call(&self) {
        // ...
    }
}

let m = Message::Write(String::from("hello"));
m.call();

Разполагане в паметта

Вариант Памет
8B 24B
Quit 0
Move { x: i64, y: i64 } 1 i64 i64
Write(String) 2 String
ChangeColor(i64, i64, i64) 3 i64 i64 i64

Без дискриминанта

1 2 3 4 5 6 7 8 9 10
use std::mem;

enum Basic {
    A,
    B,
}

fn main() {
    println!("{:?}", mem::size_of::<Basic>());
}
1
use std::mem;

enum Basic {
    A,
    B,
}

fn main() {
    println!("{:?}", mem::size_of::());
}

Null-pointer optimization

1 2 3 4 5 6 7 8 9 10 11
use std::mem;

enum LeanAndMean {
    A,
    B(String),
}

fn main() {
    println!("Without enum: {:?}", mem::size_of::<String>());
    println!("With enum:    {:?}", mem::size_of::<LeanAndMean>());
}
Without enum: 24 With enum: 24
use std::mem;

enum LeanAndMean {
    A,
    B(String),
}

fn main() {
    println!("Without enum: {:?}", mem::size_of::());
    println!("With enum:    {:?}", mem::size_of::());
}

Null-pointer optimization

1 2 3 4 5 6 7 8 9 10 11 12
use std::mem;

enum JustMean {
    A,
    B(String),
    C,
}

fn main() {
    println!("Without enum: {:?}", mem::size_of::<String>());
    println!("With enum:    {:?}", mem::size_of::<JustMean>());
}
Without enum: 24 With enum: 32
use std::mem;

enum JustMean {
    A,
    B(String),
    C,
}

fn main() {
    println!("Without enum: {:?}", mem::size_of::());
    println!("With enum:    {:?}", mem::size_of::());
}

Енумерацията Option

Енумерацията Option

Енумерацията Option

Енумерацията Option

Енумерацията Option

Енумерацията Option

Енумерацията Option

Option има 2 стойности:

Енумерацията Option

1 2 3 4 5 6 7
let some_number = Some(5);
let some_string = Some("string");
let absent_number: Option<i32> = None;

println!("{:?}", some_number);
println!("{:?}", some_string);
println!("{:?}", absent_number);
Some(5) Some("string") None
fn main() {
let some_number = Some(5);
let some_string = Some("string");
let absent_number: Option = None;

println!("{:?}", some_number);
println!("{:?}", some_string);
println!("{:?}", absent_number);
}

Енумерацията Option

1 2 3 4 5 6 7
let some_number: Option<u32> = Some(5);
let some_string: Option<&str> = Some("string");
let absent_number: Option<i32> = None;

println!("{:?}", some_number);
println!("{:?}", some_string);
println!("{:?}", absent_number);
Some(5) Some("string") None
fn main() {
let some_number: Option = Some(5);
let some_string: Option<&str> = Some("string");
let absent_number: Option = None;

println!("{:?}", some_number);
println!("{:?}", some_string);
println!("{:?}", absent_number);
}

Pattern Matching

Съпоставяне на образци

Pattern Matching

Съпоставяне на образци

Pattern Matching

Съпоставяне на образци

Pattern Matching

Съпоставяне на образци

Pattern Matching

Съпоставяне на образци

1 2 3 4 5 6
let x = Some(42_u32);

match x {
    Some(val) => println!("Value: {}", val),
    None      => println!("No value found"),
}
Value: 42
fn main() {
let x = Some(42_u32);

match x {
    Some(val) => println!("Value: {}", val),
    None      => println!("No value found"),
}
}

Pattern Matching

Съпоставяне на образци

1 2 3 4 5 6
let x: Option<u32> = None;

match x {
    Some(val) => println!("Value: {}", val),
    None      => println!("No value found"),
}
No value found
fn main() {
let x: Option = None;

match x {
    Some(val) => println!("Value: {}", val),
    None      => println!("No value found"),
}
}

Pattern Matching

Съпоставяне на образци

match може да върне стойност:

1 2 3 4 5 6 7 8
let x = Some(4);

let y = match x {
    Some(val) => Some(val * val),
    None => None,
};

println!("{:?}", y);
Some(16)
fn main() {
let x = Some(4);

let y = match x {
    Some(val) => Some(val * val),
    None => None,
};

println!("{:?}", y);
}

Pattern Matching

Съпоставяне на образци

match може да върне стойност:

1 2 3 4 5 6 7 8
let x = Some(4);

let y = match x {
    Some(val) => val * val,
    None => 0,
};

println!("{:?}", y);
16
fn main() {
let x = Some(4);

let y = match x {
    Some(val) => val * val,
    None => 0,
};

println!("{:?}", y);
}

Pattern Matching

Съпоставяне на образци

match може да излезе от функцията

1 2 3 4
let y = match x {
    Some(val) => val * val,
    None => return None,
};

Pattern Matching

Съпоставяне на образци

match може да съдържа блокове от код:

1 2 3 4 5 6 7 8 9 10
let y = match x {
    Some(val) => {
        println!("Will return {}", val * val);
        Some(val * val)
    },
    None => {
        println!("Will do nothing!!");
        None
    },
};

Pattern Matching

Съпоставяне на образци

Задължително трябва да се покрият всички случаи!

1 2 3 4 5
let x = Some(3);

let y = match x {
    Some(i) => Some(i + 1),
};
error[E0004]: non-exhaustive patterns: `None` not covered --> src/bin/main_95c8682b7401bcccd982dc5ffc0ed7d72379cdae.rs:4:15 | 4 | let y = match x { | ^ pattern `None` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
fn main() {
let x = Some(3);

let y = match x {
    Some(i) => Some(i + 1),
};
}

Pattern Matching

Съпоставяне на образци

Работи и с прости стойности: _ означава всичко останало

1 2 3 4 5
match x {
    69  => println!("Nice."),
    666 => println!(r"\m/"),
    _   => println!(r"¯\_(ツ)_/¯"),
}

Pattern Matching

Съпоставяне на образци

НО! Ръкавите трябва да са от един и същ тип

1 2 3 4 5
let response = match x {
    69  => "Nice!",
    666 => r"\m/",
    _   => 0_0,
};
error[E0308]: match arms have incompatible types --> src/bin/main_48ccf2f6abbea81f52e3d53a72c573d98dd05a43.rs:6:12 | 3 | let response = match x { | ________________- 4 | | 69 => "Nice!", | | ------- this is found to be of type `&str` 5 | | 666 => r"\m/", | | ------ this is found to be of type `&str` 6 | | _ => 0_0, | | ^^^ expected &str, found integer 7 | | }; | |_- `match` arms have incompatible types | = note: expected type `&str` found type `{integer}`
fn main() {
let x = 3;
let response = match x {
    69  => "Nice!",
    666 => r"\m/",
    _   => 0_0,
};
}

More control flow

if let

Понякога да използваме match за един случай и да покрием всички други с _ е прекалено много код

1 2 3 4 5 6
let some_value = Some(8);

match some_value {
    Some(8) => println!("8)"),
    _ => (),
}
8)
fn main() {
let some_value = Some(8);

match some_value {
    Some(8) => println!("8)"),
    _ => (),
}
}

More control flow

if let

Запознайте се с if let:

1 2 3 4 5
let some_value = Some(8);

if let Some(8) = some_value {
    println!("::::)");
}
::::)
fn main() {
let some_value = Some(8);

if let Some(8) = some_value {
    println!("::::)");
}
}

More control flow

while let

А защо не и while let:

1 2 3 4 5 6
let so_eighty = [8, 8, 8, 88, 8];
let mut iter8or = so_eighty.iter();

while let Some(8) = iter8or.next() {
    println!("∞");
}
∞ ∞ ∞
fn main() {
let so_eighty = [8, 8, 8, 88, 8];
let mut iter8or = so_eighty.iter();

while let Some(8) = iter8or.next() {
    println!("∞");
}
}

Итерация

1 2 3
let numbers = [1, 2, 3].iter();                   // std::slice::Iter
let chars   = "abc".chars();                      // std::str::Chars
let words   = "one two three".split_whitespace(); // std::str::SplitWhitespace

Итерация

1 2 3 4 5 6 7
let numbers = [1, 2, 3].iter();                   // std::slice::Iter
let chars   = "abc".chars();                      // std::str::Chars
let words   = "one two three".split_whitespace(); // std::str::SplitWhitespace

println!("{:?}", numbers);
println!("{:?}", chars);
println!("{:?}", words);
Iter([1, 2, 3]) Chars(['a', 'b', 'c']) SplitWhitespace { inner: Filter { iter: Split(SplitInternal { start: 0, end: 13, matcher: CharPredicateSearcher { haystack: "one two three", char_indices: CharIndices { front_offset: 0, iter: Chars(['o', 'n', 'e', ' ', 't', 'w', 'o', ' ', 't', 'h', 'r', 'e', 'e']) } }, allow_trailing_empty: true, finished: false }) } }
fn main() {
let numbers = [1, 2, 3].iter();                   // std::slice::Iter
let chars   = "abc".chars();                      // std::str::Chars
let words   = "one two three".split_whitespace(); // std::str::SplitWhitespace

println!("{:?}", numbers);
println!("{:?}", chars);
println!("{:?}", words);
}

Итерация

1 2 3 4 5 6 7
let numbers: Vec<&u32> = [1, 2, 3].iter().collect();
let chars: Vec<char>  = "abc".chars().collect();
let words: Vec<&str>  = "one two three".split_whitespace().collect();

println!("{:?}", numbers);
println!("{:?}", chars);
println!("{:?}", words);
[1, 2, 3] ['a', 'b', 'c'] ["one", "two", "three"]
fn main() {
let numbers: Vec<&u32> = [1, 2, 3].iter().collect();
let chars: Vec  = "abc".chars().collect();
let words: Vec<&str>  = "one two three".split_whitespace().collect();

println!("{:?}", numbers);
println!("{:?}", chars);
println!("{:?}", words);
}

Итерация

1 2 3
let chars = String::from("abc").chars();

println!("{:?}", chars); // ???

Итерация

1 2 3
let chars = String::from("abc").chars();

println!("{:?}", chars);
error[E0716]: temporary value dropped while borrowed --> src/bin/main_fdc3411ae2754f8f369a49979e61218b7638899c.rs:2:13 | 2 | let chars = String::from("abc").chars(); | ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | creates a temporary which is freed while still in use 3 | 4 | println!("{:?}", chars); | ----- borrow later used here | = note: consider using a `let` binding to create a longer lived value
fn main() {
let chars = String::from("abc").chars();

println!("{:?}", chars);
}

Итерация

1 2 3 4
let string = String::from("abc");
let chars = string.chars();

println!("{:?}", chars);
Chars(['a', 'b', 'c'])
fn main() {
let string = String::from("abc");
let chars = string.chars();

println!("{:?}", chars);
}

Итерация

1 2 3 4 5 6 7
let string = String::from("abc");
let mut chars = string.chars(); // Mutable!

println!("{:?}", chars.next());
println!("{:?}", chars.next());
println!("{:?}", chars.next());
println!("{:?}", chars.next());
Some('a') Some('b') Some('c') None
fn main() {
let string = String::from("abc");
let mut chars = string.chars(); // Mutable!

println!("{:?}", chars.next());
println!("{:?}", chars.next());
println!("{:?}", chars.next());
println!("{:?}", chars.next());
}

Итерация

1 2 3 4 5 6
let string = String::from("abc");
let mut chars = string.chars(); // Mutable!

while let Some(c) = chars.next() {
    println!("{:?}", c);
}
'a' 'b' 'c'
fn main() {
let string = String::from("abc");
let mut chars = string.chars(); // Mutable!

while let Some(c) = chars.next() {
    println!("{:?}", c);
}
}

Итерация

1 2 3 4 5 6
let string = String::from("abc");
let mut chars = string.chars();

for c in chars {
    println!("{:?}", c);
}

Итерация

1 2 3 4 5 6
let string = String::from("abc");
let mut chars = string.chars();

for c in chars {
    println!("{:?}", c);
}
'a' 'b' 'c'
fn main() {
let string = String::from("abc");
let mut chars = string.chars();

for c in chars {
    println!("{:?}", c);
}
}

Итерация

1 2 3 4 5 6
let string = String::from("abc");
let chars = string.chars(); // Not Mutable!

for c in chars {
    println!("{:?}", c);
}
'a' 'b' 'c'
fn main() {
let string = String::from("abc");
let chars = string.chars(); // Not Mutable!

for c in chars {
    println!("{:?}", c);
}
}

Итерация

1 2 3 4 5 6 7 8 9 10
let string = String::from("abc");
let chars = string.chars(); // Not Mutable!

{
    // (sneakily add mutability)
    let mut chars = chars;
    for c in chars {
        println!("{:?}", c);
    }
}
'a' 'b' 'c'
fn main() {
let string = String::from("abc");
let chars = string.chars(); // Not Mutable!

{
    // (sneakily add mutability)
    let mut chars = chars;
    for c in chars {
        println!("{:?}", c);
    }
}
}

More control flow

All together now:

1 2 3 4 5 6 7 8 9 10
let counts = [1, 2, 3, 4];
let mut counter = counts.iter();

if let Some(n) = counter.next() {
    print!("{}", n);
    while let Some(n) = counter.next() {
        print!(" and {}", n);
    }
    println!();
}

More control flow

All together now:

1 2 3 4 5 6 7 8 9 10
let counts = [1, 2, 3, 4];
let mut counter = counts.iter();

if let Some(n) = counter.next() {
    print!("{}", n);
    while let Some(n) = counter.next() {
        print!(" and {}", n);
    }
    println!();
}
1 and 2 and 3 and 4
fn main() {
let counts = [1, 2, 3, 4];
let mut counter = counts.iter();

if let Some(n) = counter.next() {
    print!("{}", n);
    while let Some(n) = counter.next() {
        print!(" and {}", n);
    }
    println!();
}
}

Pattern Matching

Guards (допълнителни условия)

1 2 3 4 5 6 7 8 9
let pair = (2, -2);

match pair {
    (x, y) if x == y                   => println!("Едно и също"),
    (x, y) if x + y == 0               => println!("Противоположни"),
    (x, y) if x % 2 == 1 && y % 2 == 0 => println!("X е нечетно, Y е четно"),
    (x, _) if x % 2 == 1               => println!("X е нечетно"),
    _                                  => println!("Нищо интересно"),
}

Pattern Matching

Ranges

1 2 3 4 5 6 7 8 9
let age: i32 = -5;

match age {
    n if n < 0 => println!("Ще се родя след {} години.", n.abs()),
    0          => println!("Новородено съм."),
    1 ... 12   => println!("Аз съм лапе."),
    13 ... 19  => println!("Аз съм тийн."),
    _          => println!("Аз съм дърт."),
}

Pattern Matching

Bindings

1 2 3 4 5 6 7 8 9
let age: i32 = -5;

match age {
    n if n < 0    => println!("Ще се родя след {} години.", n.abs()),
    0             => println!("Новородено съм."),
    n @ 1 ... 12  => println!("Аз съм лапе на {}.", n),
    n @ 13 ... 19 => println!("Аз съм тийн на {}.", n),
    n             => println!("Аз съм дърт, на {} съм вече.", n),
}

Pattern Matching

Multiple patterns

1 2 3 4 5 6
let score: u32 = 5;

match score {
    0 | 1 => println!("слабичко :("),
    _     => println!("стаа"),
}

Pattern Matching

Structs

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
struct User {
    name: &'static str,
    age: u8
}

let user = User {
    name: "Пешо",
    age: 12
};

match user {
    User { name: "Пешо", age: _ } => println!("Ко стаа, Пешо"),
    User { name: _, age: 12 }     => println!("Ко стаа, лапе"),
    User { name: x, .. }          => println!("Ко стаа, {}", x),
    _                             => println!("Ко стаа")
}

Destructuring

ref

1 2 3 4 5 6 7 8 9 10 11 12
#[derive(Debug)]
enum Token { Text(String), Number(f64) }

fn main() {
    let token = Token::Text(String::from("Отговора е 42"));
    match token {
        Token::Text(text) => println!("Токена е текст: '{}'", text),
        Token::Number(n)  => println!("Токена е число: {}", n),
    }

    println!("В крайна сметка, токена е {:?}!", token);
}
error[E0382]: borrow of moved value: `token` --> src/bin/main_7de83c4ece9c335fd8a37b17110e37b3955439df.rs:11:49 | 7 | Token::Text(text) => println!("Токена е текст: '{}'", text), | ---- value moved here ... 11 | println!("В крайна сметка, токена е {:?}!", token); | ^^^^^ value borrowed here after partial move | = note: move occurs because value has type `std::string::String`, which does not implement the `Copy` trait
#[derive(Debug)]
enum Token { Text(String), Number(f64) }

fn main() {
    let token = Token::Text(String::from("Отговора е 42"));
    match token {
        Token::Text(text) => println!("Токена е текст: '{}'", text),
        Token::Number(n)  => println!("Токена е число: {}", n),
    }

    println!("В крайна сметка, токена е {:?}!", token);
}

Destructuring

ref

Чрез ref стойността няма да се премести.

1 2 3 4 5 6 7 8 9 10 11 12
#[derive(Debug)]
enum Token { Text(String), Number(f64) }

fn main() {
    let token = Token::Text(String::from("Отговора е 42"));
    match token {
        Token::Text(ref text) => println!("Токена е текст: '{}'", text),
        Token::Number(n)      => println!("Токена е число: {}", n),
    }

    println!("В крайна сметка, токена е {:?}!", token);
}
Токена е текст: 'Отговора е 42' В крайна сметка, токена е Text("Отговора е 42")!
#[allow(dead_code)]
#[derive(Debug)]
enum Token { Text(String), Number(f64) }

fn main() {
    let token = Token::Text(String::from("Отговора е 42"));
    match token {
        Token::Text(ref text) => println!("Токена е текст: '{}'", text),
        Token::Number(n)      => println!("Токена е число: {}", n),
    }

    println!("В крайна сметка, токена е {:?}!", token);
}

Destructuring

ref

Какво всъщност прави ref? Едно просто обяснение е чрез пример:

1 2
let x = &1;
let ref y = 1;

Destructuring

ref

Какво всъщност прави ref? Едно просто обяснение е чрез пример:

1 2
let x = &1;
let ref y = 1;

Destructuring

ref

Какво всъщност прави ref? Едно просто обяснение е чрез пример:

1 2
let x = &1;
let ref y = 1;

Destructuring

ref

Какво всъщност прави ref? Едно просто обяснение е чрез пример:

1 2
let x = &1;
let ref y = 1;

Destructuring

ref

Какво всъщност прави ref? Едно просто обяснение е чрез пример:

1 2
let x = &1;
let ref y = 1;

Destructuring

ref mut

Същата идея:

1 2 3 4 5 6 7 8 9
let mut token = Token::Text(String::from("Отговора е 42"));

match token {
    Token::Text(ref mut text) => {
        *text = String::from("Може би");
        println!("Токена е Text('{}')", text)
    },
    Token::Number(n) => println!("Токена е Number({})", n),
}

Destructuring

let

1 2 3 4
let (mut a, b) = (1, 2);
let User { name: name, .. } = user;
let User { name, .. } = user;
let Some(val) = Some(5);    // ??

Destructuring

let

1 2 3 4
let (mut a, b) = (1, 2);
// let User { name: name, .. } = user;
// let User { name, .. } = user;
let Some(val) = Some(5);    // ??
error[E0005]: refutable pattern in local binding: `None` not covered --> src/bin/main_d1511059f296e51051e460e98c50c64b6a469db5.rs:5:5 | 5 | let Some(val) = Some(5); // ?? | ^^^^^^^^^ pattern `None` not covered
fn main() {
let (mut a, b) = (1, 2);
// let User { name: name, .. } = user;
// let User { name, .. } = user;
let Some(val) = Some(5);    // ??
}

Refutable/Irrefutable patterns

1 2 3 4 5
let (a, b) = (1, 2); // -> Irrefutable pattern

if let Some(val) = Some(5) { // -> Refutable pattern
    println!("Okay!");
}
Okay!
#[allow(unused_variables)]
fn main() {
let (a, b) = (1, 2); // -> Irrefutable pattern

if let Some(val) = Some(5) { // -> Refutable pattern
    println!("Okay!");
}
}

Refutable/Irrefutable patterns

1 2 3 4 5
if let (a, b) = (1, 2) {
    println!("Nope!");
}

let Some(val) = Some(5);
warning: irrefutable if-let pattern --> src/bin/main_a7d8ae0df38a9baad785d12585ab8eec8e5c4c94.rs:3:1 | 3 | / if let (a, b) = (1, 2) { 4 | | println!("Nope!"); 5 | | } | |_^ | = note: `#[warn(irrefutable_let_patterns)]` on by default error[E0005]: refutable pattern in local binding: `None` not covered --> src/bin/main_a7d8ae0df38a9baad785d12585ab8eec8e5c4c94.rs:7:5 | 7 | let Some(val) = Some(5); | ^^^^^^^^^ pattern `None` not covered
#[allow(unused_variables)]
fn main() {
if let (a, b) = (1, 2) {
    println!("Nope!");
}

let Some(val) = Some(5);
}

Refutable/Irrefutable patterns

1 2 3 4 5 6 7 8 9
if let (1, b) = (1, 2) {
    println!("{}", b);
}

if let (8, b) = (1, 2) {
    println!("{}", b);
} else {
    println!("8(");
}
2 8(
#[allow(unused_variables)]
fn main() {
if let (1, b) = (1, 2) {
    println!("{}", b);
}

if let (8, b) = (1, 2) {
    println!("{}", b);
} else {
    println!("8(");
}
}

Въпроси