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

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

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

20 октомври 2017

Административни неща

Преговор

Enums

enum IpAddrKind {
    V4,
    V6,
}

Enums

Защо enum?

Enums

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

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

Enums

параметър

fn route(ip_type: IpAddrKind) { }

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

Enums

данни

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

данни

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

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

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

варианти

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

Enum variants as Structs

struct QuitMessage; // unit struct
struct MoveMessage {
    x: i32,
    y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct

Memory layout

VariantDiscriminantData
no-mem{unsigned}12 bytes
Quit0
Move1i32i32
Write2String
ChangeColor3i32i32i32

Методи

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

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

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

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

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

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

let some_number = Some(5);
let some_string = Some("string")
let absent_number: Option<i32> = None;

Pattern Matching

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

Pattern Matching

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

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

Pattern Matching

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

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

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

Pattern Matching

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

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

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

Pattern Matching

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

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

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

Pattern Matching

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

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

let y = match x {
    Some(val) => {
        println!("Will return {}", val * val);
        Some(val * val)
    },
    None => {
        println!("Will do nothing!!");
        None
    },
};

Pattern Matching

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

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

match x {
    Some(i) => Some(i + 1),
}
 error[E0004]: non-exhaustive patterns: `None` not covered
  -->
   |
 6 |         match x {
   |               ^ pattern `None` not covered 

Pattern Matching

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

Работи и с прости стойности:

'_' означава всичко останало

match x {
    5 => println!("Five"),
    _ => println!("Not five"),
}

More control flow

if let

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

let some_value = Some(0u8);

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

More control flow

if let

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

let some_value = Some(0u8);

if let Some(3) = some_value {
    println!("three");
}

Pattern Matching

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

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

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

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

let score: u32 = 5;

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

Pattern Matching

structs

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

#[derive(Debug)]
enum Token {
    Text(String),
    Number(f64)
}

let token = Token::Text(String::from("Отговора е 42"));

match token {
    Token::Text(text) => println!("Токена е Text('{}')", text),
    Token::Number(n) => println!("Токена е Number({})", n),
}

// компилационна грешка, ползваме moved value, заради match-ването на `text`
println!("В крайна сметка, токена е {:?}!", token);

Destructuring

ref

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

match token {
    Token::Text(ref text) => println!("Токена е Text('{}')", text),
    Token::Number(n) => println!("Токена е Number({})", n),
}

// няма грешка, `text` е взето като reference
println!("В крайна сметка, токена е {:?}!", token);

Destructuring

ref

Какво всъщност прави ref?

Едно просто обяснение е чрез пример

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

Destructuring

ref mut

Може би се досещате?

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

let (mut a, b) = (1, 2);
let User { name: name, ..} = user;
let User { name, ..} = user;
let Some(val) = Some(5);    // ??
let (a, b) = (b, a);        // Please don't use this.. Use mem::swap instead.

Въпроси