Еnums, pattern matching

Енумерации, съпоставяне на образци

20 октомври 2026

Преговор

1 2 3 4 5 6
let s1 = String::from("foo bar");

let s2 = s1;

// println!("{}", s1); // error: use of moved value
println!("{}", s2);
foo bar
fn main() {
let s1 = String::from("foo bar");

let s2 = s1;

// println!("{}", s1); // error: use of moved value
println!("{}", s2);
}

Преговор

Преговор

Преговор

Преговор

Резени, масиви и низове

Масив Низ
статичен [T; N] - собственост над стойността
динамичен Vec<T> String собственост над стойността
резен &[T] &str заета назаем стойност (borrow)
mutable резен &mut [T] &mut str заета назаем стойност (borrow)

Структури

(преговор)

1 2 3 4 5
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
}
fn main() {
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
}
}

Tuples

(преговор)

1 2 3 4 5
let tuple: (i32, u32, bool) = (1, 2, false);

println!("{}", tuple.0);
println!("{}", tuple.1);
println!("{}", tuple.2);
1 2 false
fn main() {
let tuple: (i32, u32, bool) = (1, 2, false);

println!("{}", tuple.0);
println!("{}", tuple.1);
println!("{}", tuple.2);
}

Tuple structs

Именувани кортежи

1 2 3 4 5
struct Color(f32, f32, f32);
struct Point(f32, f32, f32);

let black = Color(0.0, 0.0, 0.0);
let origin = Point(0.0, 0.0, 0.0);
fn main() {
struct Color(f32, f32, f32);
struct Point(f32, f32, f32);

let black = Color(0.0, 0.0, 0.0);
let origin = Point(0.0, 0.0, 0.0);
}

Tuple structs

Полетата се достъпват с .0, .1, и т.н., както при нормален tupple

1 2 3 4 5
struct Color(f32, f32, f32);

let black = Color(0.0, 0.0, 0.0);

println!("r: {}, g: {}, b: {}", black.0, black.1, black.2);
r: 0, g: 0, b: 0
fn main() {
struct Color(f32, f32, f32);

let black = Color(0.0, 0.0, 0.0);

println!("r: {}, g: {}, b: {}", black.0, black.1, black.2);
}

Tuple structs

Защо? Type safety

1 2 3 4 5 6 7 8 9 10
struct Color(f32, f32, f32);
struct Point(f32, f32, f32);

fn draw(color: Color, from: Point, to: Point) { /* ... */ }

draw(
    Point(-10.0, -10.0, -10.0),
    Point(10.0, 10.0, 10.0),
    Color(0.5, 0.5, 0.5),
);
error[E0308]: arguments to this function are incorrect --> src/bin/main_0cdcb94a65f72f59745d40fdc6a5ce241c0cc806.rs:7:1 | 7 | draw( | ^^^^ 8 | Point(-10.0, -10.0, -10.0), | -------------------------- expected `Color`, found `Point` 9 | Point(10.0, 10.0, 10.0), 10 | Color(0.5, 0.5, 0.5), | -------------------- expected `Point`, found `Color` | note: function defined here --> src/bin/main_0cdcb94a65f72f59745d40fdc6a5ce241c0cc806.rs:5:4 | 5 | fn draw(color: Color, from: Point, to: Point) { /* ... */ } | ^^^^ help: swap these arguments | 7 ~ draw( 8 + Color(0.5, 0.5, 0.5), 9 + Point(10.0, 10.0, 10.0), 10 + Point(-10.0, -10.0, -10.0), 11 ~ ); | For more information about this error, try `rustc --explain E0308`. error: could not compile `rust` (bin "main_0cdcb94a65f72f59745d40fdc6a5ce241c0cc806") due to 1 previous error
fn main() {
struct Color(f32, f32, f32);
struct Point(f32, f32, f32);

fn draw(color: Color, from: Point, to: Point) { /* ... */ }

draw(
    Point(-10.0, -10.0, -10.0),
    Point(10.0, 10.0, 10.0),
    Color(0.5, 0.5, 0.5),
);
}

Tuple structs

Newtype wrapper

1 2
#[derive(Debug, Clone, Copy)]
struct Token(u32);
#[derive(Debug, Clone, Copy)]
struct Token(u32);

fn main() {}

Tuple structs

Разлика между type alias и newtype

1
type Node<T> = Rc<RefCell<TreeNode<T>>>;
use std::rc::Rc;
use std::cell::RefCell;
struct TreeNode { x: T }
fn main() {}
type Node = Rc>>;

Tuple structs

Разлика между type alias и newtype

1 2 3 4 5 6 7 8 9
struct UserId(pub u32);

fn find_user(id: UserId) {}

// let id = 10;
// find_user(id); // error

let id = UserId(10);
find_user(id); // ok
fn main() {
struct UserId(pub u32);

fn find_user(id: UserId) {}

// let id = 10;
// find_user(id); // error

let id = UserId(10);
find_user(id); // ok
}

Празни структури

Празни структури

Можем да дефинираме структури без полета

1 2 3 4 5 6 7
struct Electron {}
struct Photon();
struct Muon;

let e = Electron {};
let p = Photon();
let m = Muon;
fn main() {
struct Electron {}
struct Photon();
struct Muon;

let e = Electron {};
let p = Photon();
let m = Muon;
}

Enums

Enums

1 2 3 4
enum IpAddrKind {
    V4,
    V6,
}
fn main() {
enum IpAddrKind {
    V4,
    V6,
}
}

Enums

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

1 2 3 4 5 6 7 8 9 10 11
#[derive(Clone, Copy, Debug)]
enum IpAddrKind {
    V4,
    V6,
}

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

println!("four = {:?}", four);
println!("six = {:?}", six);
four = V4 six = V6
fn main() {
#[derive(Clone, Copy, Debug)]
enum IpAddrKind {
    V4,
    V6,
}

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

println!("four = {:?}", four);
println!("six = {:?}", six);
}

Enums

Числени стойности

1 2 3 4 5 6 7 8
enum IpAddrKind {
    V4 = 4,
    V6 = 6,
}

let kind = IpAddrKind::V4;
let kind_int = kind as i32;
println!("kind = {}", kind_int);
kind = 4
fn main() {
enum IpAddrKind {
    V4 = 4,
    V6 = 6,
}

let kind = IpAddrKind::V4;
let kind_int = kind as i32;
println!("kind = {}", kind_int);
}

Enums

Числени стойности

Може и от конкретен тип число

1 2 3 4 5 6 7 8 9
#[repr(u16)]
enum IpAddrKind {
    V4 = 4,
    V6 = 6,
}

let kind = IpAddrKind::V4;
let kind_int = kind as u16;
println!("kind = {}", kind_int);
kind = 4
fn main() {
#[repr(u16)]
enum IpAddrKind {
    V4 = 4,
    V6 = 6,
}

let kind = IpAddrKind::V4;
let kind_int = kind as u16;
println!("kind = {}", kind_int);
}

Enums

Числени стойности

Конвертиране наобратно не е позволено (от число към енумерация)

1 2 3 4 5 6 7 8 9
#[repr(u16)]
enum IpAddrKind {
    V4 = 4,
    V6 = 6,
}

let kind_int = 4_u16;
let kind = kind_int as IpAddrKind;
println!("kind = {}", kind_int);
error[E0605]: non-primitive cast: `u16` as `IpAddrKind` --> src/bin/main_3a15928c30bea3641346b388b8a36f2135c6aae5.rs:9:12 | 9 | let kind = kind_int as IpAddrKind; | ^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object For more information about this error, try `rustc --explain E0605`. error: could not compile `rust` (bin "main_3a15928c30bea3641346b388b8a36f2135c6aae5") due to 1 previous error
fn main() {
#[repr(u16)]
enum IpAddrKind {
    V4 = 4,
    V6 = 6,
}

let kind_int = 4_u16;
let kind = kind_int as IpAddrKind;
println!("kind = {}", kind_int);
}

Enums

Числени стойности

Можем да конвертираме чрез match, но трябва да се погрижим за всички стойностти

1 2 3 4 5 6 7 8 9 10 11 12 13
#[repr(u16)]
enum IpAddrKind {
    V4 = 4,
    V6 = 6,
}

let kind_int = 4;
let kind = match kind_int {
    4 => IpAddrKind::V4,
    6 => IpAddrKind::V6,
    _ => panic!("not a valid ip kind"),
};
println!("kind = {}", kind_int);
kind = 4
fn main() {
#[repr(u16)]
enum IpAddrKind {
    V4 = 4,
    V6 = 6,
}

let kind_int = 4;
let kind = match kind_int {
    4 => IpAddrKind::V4,
    6 => IpAddrKind::V6,
    _ => panic!("not a valid ip kind"),
};
println!("kind = {}", kind_int);
}

Enums

Enums

Enums

Enums

Данни

Можем да асоциираме данни с всеки вариант

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

let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
fn main() {
enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}

Enums

Данни

Данните може да са от различни типове

1 2 3 4 5 6 7
enum IpAddr {
    V4([u8; 4]),
    V6([u16; 8]),
}

let home = IpAddr::V4([127, 0, 0, 1]);
let loopback = IpAddr::V6([0, 0, 0, 0, 0, 0, 0, 1]);
fn main() {
enum IpAddr {
    V4([u8; 4]),
    V6([u16; 8]),
}

let home = IpAddr::V4([127, 0, 0, 1]);
let loopback = IpAddr::V6([0, 0, 0, 0, 0, 0, 0, 1]);
}

Enums

Защо?

1 2 3 4 5 6 7 8 9
struct IpAddr {
    kind: IpAddrKind,
    v4: [u8; 4],
    v6: String,
}

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

Enums

Защо?

1 2 3 4
enum IpAddr {
    V4([u8; 4]),
    V6(String),
}
fn main() {}
enum IpAddr {
    V4([u8; 4]),
    V6(String),
}

Enums

Защо?

Отклонение

Алгебрични типове данни

Защо се наричат algebraic datatypes?

Отклонение

Алгебрични типове данни

1
struct User { username: String, email: String, sign_in_count: u64 }

Отклонение

Алгебрични типове данни

1 2 3 4 5
// ignore
enum IpAddr {
    V4([u8; 4]),
    V6(String),
}

Отклонение

Алгебрични типове данни

Празната структура и празният кортеж представляват множества с един елемент

1 2 3 4 5
struct UnitStruct{}
// set_of_values = { UnitStruct{} }

type UnitTuple = ();
// set_of_values = { () }
fn main() {
struct UnitStruct{}
// set_of_values = { UnitStruct{} }

type UnitTuple = ();
// set_of_values = { () }
}

Отклонение

Алгебрични типове данни

Q: Какво представлява празната енумерация?

Отклонение

Алгебрични типове данни

Q: Какво представлява празната енумерация?
A: Празно множество

1 2
enum Empty {}
// set_of_values = ∅
fn main() {
enum Empty {}
// set_of_values = ∅
}

Отклонение

Алгебрични типове данни

Отклонение

Алгебрични типове данни

Отклонение

Алгебрични типове данни

Отклонение

Алгебрични типове данни

Отклонение

Алгебрични типове данни

Отклонение

Алгебрични типове данни

1 2 3 4 5 6 7 8
// Към Rust 1.99, типа `!` все още не е стабилизиран,
// но се очаква тоав да стане скоро.
// За момента можем да използваме наш празен тип.
enum Never {}

let x: Never = loop {};
let x: Never = std::process::exit(0);
let x: Never = panic!("boom");
#![allow(unreachable_code)]
fn main() {
// Към Rust 1.99, типа `!` все още не е стабилизиран,
// но се очаква тоав да стане скоро.
// За момента можем да използваме наш празен тип.
enum Never {}

let x: Never = loop {};
let x: Never = std::process::exit(0);
let x: Never = panic!("boom");
}

Enums

Защо "tagged union"?

Можем да си представим enum-а като комбинация от:

Enums

Защо "tagged union"?

Можем да си представим enum-а като комбинация от:

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),
}

let msg = Message::Quit;
let msg = Message::Move { x: 3, y: 4 };
let msg = Message::Write(String::from("baba"));
let msg = Message::ChangeColor(255, 0, 0);
fn main() {
enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i64, i64, i64),
}

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

Enums

Защо "tagged union"?

Можем да си представим enum-а като комбинация от:

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

let msg = Message::Quit;
let msg = Message::Move { x: 3, y: 4 };
let msg = Message::Write(String::from("baba"));
let msg = Message::ChangeColor(255, 0, 0);
fn main() {
enum Message {
    Quit,                       // unit struct
    Move { x: i64, y: i64 },    // struct
    Write(String),              // tuple struct
    ChangeColor(i64, i64, i64), // tuple struct
}

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

Enums

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

Вариант Памет

Message::Quit

Message::Move { x: i64, y: i64 }

Message::Write(String)

Message::ChangeColor(i64, i64, i64)

+–––––––++–––––––––––––––––––––––+
│   0   ││                       │
+–––––––++–––––––+–––––––+–––––––+
│   1   ││  i64  │  i64  │       │
+–––––––++–––––––+–––––––+–––––––+
│   2   ││       String          │
+–––––––++–––––––+–––––––+–––––––+
│   3   ││  i64  │  i64  │  i64  │
+–––––––++–––––––+–––––––+–––––––+

Enums

C-style enum

Това ни дава алтернативна интерпретация на енумерацията с числени стойности:

1 2 3 4 5 6
#[repr(u16)]
enum MyEnum {
   VariantA = 0,
   VariantB = 1,
   VariantC = 3,
}
fn main() {
#[repr(u16)]
enum MyEnum {
   VariantA = 0,
   VariantB = 1,
   VariantC = 3,
}
}

Pattern Matching

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

Pattern Matching

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

Pattern Matching

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

Pattern Matching

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

Pattern Matching

1 2 3 4 5 6 7 8
fn call(msg: &Message) {
    match msg {
        Message::Quit => println!("got quit"),
        Message::Move {x, y} => println!("got move {x}, {y}"),
        Message::Write(s) => println!("got write {s:?}"),
        Message::ChangeColor(r, g, b) => println!("got change_color {r}, {g}, {b}"),
    }
}
fn main() {
enum Message {
Quit,
Move { x: i64, y: i64 },
Write(String),
ChangeColor(i64, i64, i64),
}

fn call(msg: &Message) {
    match msg {
        Message::Quit => println!("got quit"),
        Message::Move {x, y} => println!("got move {x}, {y}"),
        Message::Write(s) => println!("got write {s:?}"),
        Message::ChangeColor(r, g, b) => println!("got change_color {r}, {g}, {b}"),
    }
}
}

Pattern Matching

1 2 3 4 5
match value {
    PATTERN1 => expr1,
    PATTERN2 => expr2,
    PATTERN3 => expr3,
}

Pattern Matching

1 2 3 4 5
match value {
    PATTERN1 => expr1,
    PATTERN2 => expr2,
    PATTERN3 => expr3,
}

Pattern Matching

1 2 3 4 5
match value {
    PATTERN1 => expr1,
    PATTERN2 => expr2,
    PATTERN3 => expr3,
}

Pattern Matching

1 2 3 4 5
match value {
    PATTERN1 => expr1,
    PATTERN2 => expr2,
    PATTERN3 => expr3,
}

Pattern Matching

1 2 3 4 5
match value {
    PATTERN1 => expr1,
    PATTERN2 => expr2,
    PATTERN3 => expr3,
}

Pattern Matching

Може да се съпоставя по стойност

1 2 3 4 5
let condition = true;
match condition {
    true => println!("it's ok"),
    false => println!("it's bad"),
}
it's ok
fn main() {
let condition = true;
match condition {
    true => println!("it's ok"),
    false => println!("it's bad"),
}
}

Pattern Matching

Образеца _ отговаря на всяка възможна стойност

1 2 3 4 5 6 7
let my_number = 14;
match my_number {
    1 => println!("it's one"),
    2 => println!("it's two"),
    3 => println!("it's three"),
    _ => println!("it's something else"),
}
it's something else
fn main() {
let my_number = 14;
match my_number {
    1 => println!("it's one"),
    2 => println!("it's two"),
    3 => println!("it's three"),
    _ => println!("it's something else"),
}
}

Pattern Matching

Може да се сравнява с интервали от стойности

1 2 3 4 5 6 7
let my_number = 14_u32;
match my_number {
    0 => println!("none"),
    1..3 => println!("small"),
    3..=14 => println!("medium"),
    15.. => println!("large"),
}
medium
fn main() {
let my_number = 14_u32;
match my_number {
    0 => println!("none"),
    1..3 => println!("small"),
    3..=14 => println!("medium"),
    15.. => println!("large"),
}
}

Pattern Matching

Съвпадналата стойност може да бъде присвоена на променлива с @

1 2 3 4 5 6 7
let my_number = 14_u32;
match my_number {
    0 => println!("none"),
    n @ 1..3 => println!("{n} is small"),
    n @ 3..=14 => println!("{n} is medium"),
    15.. => println!("too large"),
}
14 is medium
fn main() {
let my_number = 14_u32;
match my_number {
    0 => println!("none"),
    n @ 1..3 => println!("{n} is small"),
    n @ 3..=14 => println!("{n} is medium"),
    15.. => println!("too large"),
}
}

Pattern Matching

Име (identifier) съвпада на всяка стойност и присвоява стойността на променлива с това име. Все едно var @ _.

1 2 3 4 5
let my_number = 14_u32;
match my_number {
    0 => println!("it is zero"),
    n => println!("it is non-zero"),
}
it is non-zero
fn main() {
let my_number = 14_u32;
match my_number {
    0 => println!("it is zero"),
    n => println!("it is non-zero"),
}
}

Pattern Matching

Име в образен винаги създава нова променлива, която засенчва съществуващи променливи.
Не може да се използва за съпоставяне със стойността на съществуваща променлива

1 2 3 4 5 6 7
#![allow(unreachable_patterns)]

let x = 5;
match 123 {
    x => println!("it should be five; got x={x}"), // wrong
    _ => println!("it's not five"), // wrong
}
it should be five; got x=123
#![allow(unreachable_patterns)]

fn main() {
let x = 5;
match 123 {
    x => println!("it should be five; got x={x}"), // wrong
    _ => println!("it's not five"), // wrong
}
}

Pattern Matching

Име в образен винаги създава нова променлива, която засенчва съществуващи променливи.
Не може да се използва за съпоставяне със стойността на съществуваща променлива

1 2 3 4 5 6 7
// #![allow(unreachable_patterns)]

let x = 5;
match 123 {
    x => println!("it should be five; got x={x}"), // wrong
    _ => println!("it's not five"), // wrong
}
warning: unreachable pattern --> src/bin/main_0992ff311fb9eb93ad5bdedc2e5e35d62833f5ba.rs:7:5 | 6 | x => println!("it should be five; got x={x}"), // wrong | - matches any value 7 | _ => println!("it's not five"), // wrong | ^ no value can reach this | note: there is a binding of the same name; if you meant to pattern match against the value of that binding, that is a feature of constants that is not available for `let` bindings --> src/bin/main_0992ff311fb9eb93ad5bdedc2e5e35d62833f5ba.rs:4:5 | 4 | let x = 5; | ^ = note: `#[warn(unreachable_patterns)]` (part of `#[warn(unused)]`) on by default
fn main() {
let x = 5;
match 123 {
    x => println!("it should be five; got x={x}"), // wrong
    _ => println!("it's not five"), // wrong
}
}

Pattern Matching

1 2 3 4 5 6 7 8 9 10
enum FooBar {
    Foo,
    Bar,
}

let val = FooBar::Foo;
match val {
    FooBar::Foo => println!("it's foo"),
    FooBar::Bar => println!("it's bar"),
}
it's foo
fn main() {
enum FooBar {
    Foo,
    Bar,
}

let val = FooBar::Foo;
match val {
    FooBar::Foo => println!("it's foo"),
    FooBar::Bar => println!("it's bar"),
}
}

Pattern Matching

1 2 3 4 5 6 7
let coords = (3, 3);
match coords {
    (0, 0) => println!("center"),
    (0, y) => println!("vertical {y}"),
    (x, 0) => println!("horizontal {x}"),
    (x, y) => println!("nowhere in particular {x},{y}"),
}
nowhere in particular 3,3
fn main() {
let coords = (3, 3);
match coords {
    (0, 0) => println!("center"),
    (0, y) => println!("vertical {y}"),
    (x, 0) => println!("horizontal {x}"),
    (x, y) => println!("nowhere in particular {x},{y}"),
}
}

Pattern Matching

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

1 2 3 4 5 6 7
let cake: &[&str] = &["vanilla", "strawberry", "chocolate"];

match cake {
    []         => println!("Turns out it's a lie :/"),
    [one_item] => println!("One slice is better than nothing"),
    _          => println!("Wow, that's a lotta slices!")
}
Wow, that's a lotta slices!
fn main() {
let cake: &[&str] = &["vanilla", "strawberry", "chocolate"];

match cake {
    []         => println!("Turns out it's a lie :/"),
    [one_item] => println!("One slice is better than nothing"),
    _          => println!("Wow, that's a lotta slices!")
}
}

Pattern Matching

.. отговаря на всичко останало от масива

1 2 3 4 5 6
let nums = &[1, 2, 3][..];
match nums {
    [] => println!("empty"),
    [a] => println!("just {a}"),
    [x, ..] => println!("head is {x}"),
}
head is 1
fn main() {
let nums = &[1, 2, 3][..];
match nums {
    [] => println!("empty"),
    [a] => println!("just {a}"),
    [x, ..] => println!("head is {x}"),
}
}

Pattern Matching

.. може да се комбинира с @

1 2 3 4 5 6
let nums = &[1, 2, 3][..];
match nums {
    [] => println!("empty"),
    [a] => println!("just {a}"),
    [x, rest @ ..] => println!("head is {x}, tail is {rest:?}"),
}
head is 1, tail is [2, 3]
fn main() {
let nums = &[1, 2, 3][..];
match nums {
    [] => println!("empty"),
    [a] => println!("just {a}"),
    [x, rest @ ..] => println!("head is {x}, tail is {rest:?}"),
}
}

Pattern Matching

.. работи и в началото или средата.

1 2 3 4 5 6
let nums = &[1, 2, 3, 4][..];
match nums {
    [] => println!("empty"),
    [a] => println!("just {a}"),
    [first, mid @ .., last] => println!("{first} then {mid:?} then {last}"),
}
1 then [2, 3] then 4
fn main() {
let nums = &[1, 2, 3, 4][..];
match nums {
    [] => println!("empty"),
    [a] => println!("just {a}"),
    [first, mid @ .., last] => println!("{first} then {mid:?} then {last}"),
}
}

Pattern Matching

Но може да се среща само веднъж в целия образец

1 2 3 4 5 6
let nums = &[1, 2, 3, 4][..];
match nums {
    [] => println!("empty"),
    [a] => println!("just {a}"),
    [.., mid, ..] => println!("mid is {mid}"),
}
error: `..` can only be used once per slice pattern --> src/bin/main_cc2e630a367d1ea68231c8d5466227007d241c57.rs:6:15 | 6 | [.., mid, ..] => println!("mid is {mid}"), | -- ^^ can only be used once per slice pattern | | | previously used here error: could not compile `rust` (bin "main_cc2e630a367d1ea68231c8d5466227007d241c57") due to 1 previous error
fn main() {
let nums = &[1, 2, 3, 4][..];
match nums {
    [] => println!("empty"),
    [a] => println!("just {a}"),
    [.., mid, ..] => println!("mid is {mid}"),
}
}

Pattern Matching

Още: guards (допълнителни условия)

1 2 3 4
match value {
    PATTERN if CONDITION => EXPR_1,
    PATTERN if CONDITION => EXPR_2,
}

Pattern Matching

Още: алтернативност

1 2 3 4 5 6 7 8 9 10 11
match value {
    PATTERN1 | PATTERN2 => EXPR_1
    PATTERN3 | PATTERN4 => EXPR_2
}

match value {
    | PATTERN1
    | PATTERN2 => EXPR_1
    | PATTERN3
    | PATTERN4 => EXPR_2
}

Pattern Matching

Ограничения - типовете трябва да съответстват

1 2 3 4 5 6 7 8 9 10 11 12
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, .. }             => println!("Ко стаа, {name}"),
}
Ко стаа, Пешо
fn main() {
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, .. }             => println!("Ко стаа, {name}"),
}
}

Pattern Matching

Ограничения - типовете трябва да съответстват

1 2 3 4 5 6 7 8 9 10 11 12
struct User {
    name: String,   // <- динамичен низ
    age: u8
}

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

match user {            // не е ок
    User { name: "Пешо", age: _ } => println!("Ко стаа, Пешо"),
    User { name: _, age: 12 }     => println!("Ко стаа, лапе"),
    User { name, .. }             => println!("Ко стаа, {name}"),
}
error[E0308]: mismatched types --> src/bin/main_e00feb4f04a8f7d49f75b302dace00fdbfee96fa.rs:10:18 | 9 | match user { // не е ок | ---- this expression has type `User` 10 | User { name: "Пешо", age: _ } => println!("Ко стаа, Пешо"), | ^^^^^^ expected `String`, found `&str` | help: consider converting the `String` to a `&str` using `.as_str()` | 9 | match user.as_str() { // не е ок | +++++++++ For more information about this error, try `rustc --explain E0308`. error: could not compile `rust` (bin "main_e00feb4f04a8f7d49f75b302dace00fdbfee96fa") due to 1 previous error
fn main() {
struct User {
    name: String,   // <- динамичен низ
    age: u8
}

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

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

Destructuring

Destructuring

let bindings

Повечето от образците, поддържани от match, могат да се използват и за разбиране на стойност в let клаузи.
Дясната страна трябва винаги да може да се съпостави на лявата - irrefutable binding.

1 2 3 4 5 6 7
let (a, b, c) = (1, true, "three");
let (a, ..) = (1, true, "three");
let (.., c) = (1, true, "three");

let [a, b, c] = [1, 2, 3];
let [a, ..] = [1, 2, 3];
let [.., c] = [1, 2, 3];
fn main() {
let (a, b, c) = (1, true, "three");
let (a, ..) = (1, true, "three");
let (.., c) = (1, true, "three");

let [a, b, c] = [1, 2, 3];
let [a, ..] = [1, 2, 3];
let [.., c] = [1, 2, 3];
}

Destructuring

let bindings

Повечето от образците, поддържани от match, могат да се използват и за разбиране на стойност в let клаузи.
Дясната страна трябва винаги да може да се съпостави на лявата - irrefutable binding.

1 2 3 4 5
let User { name: name_var, .. } = user;
println!("{}", name_var);

let User { name, .. } = user;
println!("{}", name);
fn main() {
#[derive(Default)] struct User { name: String, email: String }
let user = User::default();
let User { name: name_var, .. } = user;
println!("{}", name_var);

let user = User::default();
let User { name, .. } = user;
println!("{}", name);
}

Destructuring

arguments

Destructuring може да се прилага и в аргументи на функции

1 2 3 4 5 6 7 8
fn goto_v1(coords: (i32, i32)) {
    let (x, y) = coords;
    /* ... */
}

fn goto_v2((x, y): (i32, i32)) {
    /* ... */
}
fn main() {}
fn goto_v1(coords: (i32, i32)) {
    let (x, y) = coords;
    /* ... */
}

fn goto_v2((x, y): (i32, i32)) {
    /* ... */
}

More control flow

if let

1 2 3
if let PATTERN = value {
    code()
}

Еквиваленто на долния match.
Но спестява едно ниво на индентация и няколко реда

1 2 3 4 5 6
match value {
    PATTERN => {
        code()
    }
    _ => {}
}

More control flow

while let

А защо не и while let:

1 2 3
while let PATTERN = expr() {
    code()
}

Еквивалентно на

1 2 3 4 5 6 7 8 9 10
loop {
    match expr() {
        PATTERN => {
            code()
        }
        _ => {
            break
        }
    }
}

Option

Option

Option

Option

Option

Option

Option

При възможност за липсваща стойност се използва Option.
Има 2 стойности:

1 2 3 4
enum Option<T> {
    Some(T),
    None,
}
fn main() {}
enum Option {
    Some(T),
    None,
}

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);
}

Option

Non-zero оптимизация

За много типове нула/null е невалидна стойност (напр &T).
Или съдържат поле, за което е невалидна стойност (напр ptr в String).

Ако такъв тип се използва в enum, компилатора може да използва свободния слот, за да помести другите варианти.

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::());
}

Option

Non-zero оптимизация

Това много често се случва при Option.
При всички тези случаи Option е zero cost abstraction.

1 2 3 4 5
println!("&T:     {:?} vs {:?}", mem::size_of::<&u32>(),     mem::size_of::<Option<&u32>>());
println!("Box<T>: {:?} vs {:?}", mem::size_of::<Box<u32>>(), mem::size_of::<Option<Box<u32>>>());
println!("&str:   {:?} vs {:?}", mem::size_of::<&str>(),     mem::size_of::<Option<&str>>());
println!("String: {:?} vs {:?}", mem::size_of::<String>(),   mem::size_of::<Option<String>>());
println!("Vec<_>: {:?} vs {:?}", mem::size_of::<Vec<u32>>(), mem::size_of::<Option<Vec<u32>>>());
&T: 8 vs 8 Box<T>: 8 vs 8 &str: 16 vs 16 String: 24 vs 24 Vec<_>: 24 vs 24
use std::mem;
fn main() {
println!("&T:     {:?} vs {:?}", mem::size_of::<&u32>(),     mem::size_of::>());
println!("Box: {:?} vs {:?}", mem::size_of::>(), mem::size_of::>>());
println!("&str:   {:?} vs {:?}", mem::size_of::<&str>(),     mem::size_of::>());
println!("String: {:?} vs {:?}", mem::size_of::(),   mem::size_of::>());
println!("Vec<_>: {:?} vs {:?}", mem::size_of::>(), mem::size_of::>>());
}

Result

Друг много често срещан тип е Result.
В Rust няма exceptions - функции които могат да се провалят връщат Result

1 2 3 4
enum Result<T, E> {
    Ok(T),
    Err(E),
}
fn main() {}
enum Result {
    Ok(T),
    Err(E),
}

Option и Result

Извличане на стойност

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

let double = match some_number {
    Some(val) => val + val,
    None => 0,
};

println!("{:?}", double);
10
fn main() {
let some_number = Some(5);

let double = match some_number {
    Some(val) => val + val,
    None => 0,
};

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

Option и Result

panic

1
panic!("boom")
thread 'main' (4418) panicked at src/bin/main_f7805e0522dc52087809f00b5c53095884d8a7cf.rs:2:1: boom note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fn main() {
panic!("boom")
}

Option и Result

Извличане на стойност

Option и Result

Извличане на стойност

1 2 3 4 5 6 7 8 9
let optional_number = Some(5);
let number = optional_number.unwrap();

// e горе-долу еквивалетно на

let number = match optional_number {
    Some(n) => n,
    None => panic!("`unwrap()` called on `None` value"),
};
fn main() {
let optional_number = Some(5);
let number = optional_number.unwrap();

// e горе-долу еквивалетно на

let number = match optional_number {
    Some(n) => n,
    None => panic!("`unwrap()` called on `None` value"),
};
}

Въпроси