Те(к)стови работи

низове, тестване и една торба с инструменти

(най-рошавата лекция в курса (засега))

21 октомври 2020

Преговор

Енумерации

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);
#[allow(path_statements)]
fn main() {
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);
}

Преговор

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

1 2 3 4 5 6 7 8 9
let message = Message::Move { x: 1, y: 3 };

match message {
    Message::Quit => println!("What a quitter!"),
    Message::Move { x, y } => println!("Going to ({}, {})!", x, y),
    Message::Write(_) => println!("Pfff.. whatevs"),
    Message::ChangeColor(_, _, b) if b > 200 => println!("So much blue!"),
    _ => println!("Don't care."),
}
Going to (1, 3)!
fn main() {
#[allow(dead_code)]
enum Message {
Quit,
Move { x: i64, y: i64 },
Write(String),
ChangeColor(u8, u8, u8),
}

let message = Message::Move { x: 1, y: 3 };

match message {
    Message::Quit => println!("What a quitter!"),
    Message::Move { x, y } => println!("Going to ({}, {})!", x, y),
    Message::Write(_) => println!("Pfff.. whatevs"),
    Message::ChangeColor(_, _, b) if b > 200 => println!("So much blue!"),
    _ => println!("Don't care."),
}
}

Преговор

Option и представяне на липсваща стойност

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

Преговор

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

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

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

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

Съдържание

Кодиране на низове

Кодиране на низове

ASCII

Кодиране на низове

ASCII

Кодиране на низове

ASCII

Кодиране на низове

ASCII

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Кодиране на низове

Кодови таблици

Недостатъци:

Кодиране на низове

Кодови таблици

Недостатъци:

Кодиране на низове

Кодови таблици

Недостатъци:

Кодиране на низове

Кодови таблици

Недостатъци:

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Unicode

Кодиране на низове

Chars

Кодиране на низове

Chars

1 2 3 4
println!("0x{:x}", 'я' as u32);

use std::char;
println!("{:?}", char::from_u32(0x044f));
0x44f Some('я')
fn main() {
println!("0x{:x}", 'я' as u32);

use std::char;
println!("{:?}", char::from_u32(0x044f));
}

Кодиране на низове

UTF-32

Кодиране на низове

UTF-32

Кодиране на низове

UTF-16

Кодиране на низове

UTF-16

Кодиране на низове

UTF-16

Кодиране на низове

UTF-16

Кодиране на низове

UTF-8

Кодиране на низове

UTF-8

Кодиране на низове

UTF-8

Кодиране на низове

UTF-8

Кодиране на низове

UTF-8

Схема на кодирането

Брой байтове Първи code point Последен code point Байт 1 Байт 2 Байт 3 Байт 4
1 U+0000 U+007F 0xxxxxxx
2 U+0080 U+07FF 110xxxxx 10xxxxxx
3 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
4 U+10000 U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Кодиране на низове

UTF-8

Предимства:

Низове

Итерация

1 2 3 4 5 6 7 8 9 10
// bytes() връща итератор по байтовете на низа
let bytes: Vec<u8> = "Здравей! 😊".bytes().collect();

// chars() връща итератор по символите в низа
let chars: Vec<char> = "Здравей! 😊".chars().collect();

// аs_bytes() преобразува &str в &[u8]
println!("{:x?}", "Здравей! 😊".as_bytes());
println!("{:x?}", bytes);
println!("{:?}", chars);
[d0, 97, d0, b4, d1, 80, d0, b0, d0, b2, d0, b5, d0, b9, 21, 20, f0, 9f, 98, 8a] [d0, 97, d0, b4, d1, 80, d0, b0, d0, b2, d0, b5, d0, b9, 21, 20, f0, 9f, 98, 8a] ['З', 'д', 'р', 'а', 'в', 'е', 'й', '!', ' ', '😊']
fn main() {
// bytes() връща итератор по байтовете на низа
let bytes: Vec = "Здравей! 😊".bytes().collect();

// chars() връща итератор по символите в низа
let chars: Vec = "Здравей! 😊".chars().collect();

// аs_bytes() преобразува &str в &[u8]
println!("{:x?}", "Здравей! 😊".as_bytes());
println!("{:x?}", bytes);
println!("{:?}", chars);
}

Низове

Итерация

1 2 3 4 5 6
for c in "Здравей! 😊".chars() {
    let c_string: String = c.to_string();
    let c_utf8 = c_string.as_bytes();

    println!("{}: code_point={:x}, utf8={:x?}", c, c as u32, c_utf8);
}
З: code_point=417, utf8=[d0, 97] д: code_point=434, utf8=[d0, b4] р: code_point=440, utf8=[d1, 80] а: code_point=430, utf8=[d0, b0] в: code_point=432, utf8=[d0, b2] е: code_point=435, utf8=[d0, b5] й: code_point=439, utf8=[d0, b9] !: code_point=21, utf8=[21] : code_point=20, utf8=[20] 😊: code_point=1f60a, utf8=[f0, 9f, 98, 8a]
fn main() {
for c in "Здравей! 😊".chars() {
    let c_string: String = c.to_string();
    let c_utf8 = c_string.as_bytes();

    println!("{}: code_point={:x}, utf8={:x?}", c, c as u32, c_utf8);
}
}

Низове

Дължина

str::len() връща дължината на низ в брой байтове

1 2 3 4
let hi = "Здравей! 😊";

println!("{}", hi.len());
println!("{}", hi.chars().count());
20 10
fn main() {
let hi = "Здравей! 😊";

println!("{}", hi.len());
println!("{}", hi.chars().count());
}

Низове

Индексация

При взимане на резен от низ се оказват брой байтове

1 2
let sub_hi = &"Здравей! 😊"[0..6];
println!("{:?}", sub_hi);
"Здр"
fn main() {
let sub_hi = &"Здравей! 😊"[0..6];
println!("{:?}", sub_hi);
}
1 2
let sub_hi = &"Здравей! 😊"[0..3];
println!("{:?}", sub_hi);
thread 'main' panicked at 'byte index 3 is not a char boundary; it is inside 'д' (bytes 2..4) of `Здравей! 😊`', src/bin/main_2c3c4f5929c61b0516fdd13c207f4dee42a64790.rs:2:15 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fn main() {
let sub_hi = &"Здравей! 😊"[0..3];
println!("{:?}", sub_hi);
}

Низове

Заключение

Низове

Заключение

Низове

Заключение

Документация

Документация

1 2 3
fn main() {
    /// Ако мислихте, че коментарите не водят до компилационни грешки...
}
error[E0585]: found a documentation comment that doesn't document anything --> src/bin/main_db28f9dfd3554ccccfb66bc6a6fa18931a81dce1.rs:2:5 | 2 | /// Ако мислихте, че коментарите не водят до компилационни грешки... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: doc comments must come before what they document, maybe a comment was intended with `//`?
fn main() {
    /// Ако мислихте, че коментарите не водят до компилационни грешки...
}

Документация

Док-коментари

Документация

Док-коментари

Документация

Док-коментари

Документация

Док-коментари

Документация

На структури и полета

1 2 3 4 5 6 7 8 9 10
/// A (half-open) range bounded inclusively below and exclusively above (start..end).
///
/// The `Range` `start..end` contains all values with `x >= start` and `x < end`.
/// It is empty unless `start < end`.
pub struct Range<Idx> {
    /// The lower bound of the range (inclusive).
    pub start: Idx,
    /// The upper bound of the range (exclusive).
    pub end: Idx,
}
#![allow(dead_code)]
/// A (half-open) range bounded inclusively below and exclusively above (start..end).
///
/// The `Range` `start..end` contains all values with `x >= start` and `x < end`.
/// It is empty unless `start < end`.
pub struct Range {
    /// The lower bound of the range (inclusive).
    pub start: Idx,
    /// The upper bound of the range (exclusive).
    pub end: Idx,
}
fn main() {}

Документация

На функции и методи

1 2 3 4 5 6 7 8 9 10 11 12 13 14
impl String {
    /// Appends a given string slice onto the end of this `String`.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// let mut s = String::from("foo");
    /// s.push_str("bar");
    /// assert_eq!("foobar", s);
    /// ```
    pub fn push_str(&mut self, string: &str) {}
}
struct String;
impl String {
    /// Appends a given string slice onto the end of this `String`.
    ///
    /// Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// let mut s = String::from("foo");
    /// s.push_str("bar");
    /// assert_eq!("foobar", s);
    /// ```
    pub fn push_str(&mut self, string: &str) {}
}
fn main() {}

Документация

Вътрешни док-коментари

Коментари, които започват с //!, документират елемента в който се намират.

1 2 3 4 5 6 7 8 9
mod fs {
    //! Filesystem manipulation operations.
    //!
    //! This module contains basic methods to manipulate the
    //! contents of the local filesystem. All methods in this
    //! module represent cross-platform filesystem operations.

    /* ... */
}
mod fs {
    //! Filesystem manipulation operations.
    //!
    //! This module contains basic methods to manipulate the
    //! contents of the local filesystem. All methods in this
    //! module represent cross-platform filesystem operations.

    /* ... */
}
fn main() {}

Документация

Документация

Документация

Документация

Документация

Документация

Документация

Тестване на примери

1 2 3 4 5 6
/**
 * Always returns true.
 */
public boolean isAvailable() {
    return false;
}

Документация

Тестване на примери

Документация

Тестване на примери

Документация

Тестване на примери

Документация

Тестване на примери

Документация

Тестване на примери

Документация

Тестване на примери

1 2 3 4 5 6 7 8 9 10
/// Converts a `char` to a digit in the given radix.
///
///
/// # Examples
///
/// ```
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// ```
pub fn to_digit(self, radix: u32) -> Option<u32>;

Документация

Тестване на примери

1 2 3 4 5 6 7 8 9 10
/// Converts a `char` to a digit in the given radix.
///
///
/// # Examples
///
/// ```
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// ```
pub fn to_digit(self, radix: u32) -> Option<u32>;
   Doc-tests example

running 1 test
test src/lib.rs - to_digit (line 8) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Документация

The rustdoc book

Подробности относно документацията и тестването на примери можете да намерите в Rustdoc книгата
https://doc.rust-lang.org/rustdoc/index.html

Атрибути

Атрибути

Атрибути

Атрибути

Атрибути

Атрибути

Атрибути

Атрибути

Служат за различни неща

Атрибути

Служат за различни неща

Атрибути

Служат за различни неща

Атрибути

Служат за различни неща

Атрибути

Служат за различни неща

Атрибути

Mожем да използваме атрибути за да имплементираме някои често използвани трейтове за наш тип

1 2 3 4 5
#[derive(Debug)]
struct User {
    name: String,
    age: i32,
}
#![allow(dead_code)]
fn main() {
#[derive(Debug)]
struct User {
    name: String,
    age: i32,
}
}

Атрибути

Mожем да използваме атрибути за да имплементираме някои често използвани трейтове за наш тип

1 2 3 4 5 6 7 8 9
#[derive(Debug)]
struct User {
    name: String,
    age: i32,
}

// `User` вече имплементира `Debug`
let user = User { name: String::from("Пешо"), age: 14 };
println!("{:?}", user);
User { name: "Пешо", age: 14 }
#![allow(dead_code)]
fn main() {
#[derive(Debug)]
struct User {
    name: String,
    age: i32,
}

// `User` вече имплементира `Debug`
let user = User { name: String::from("Пешо"), age: 14 };
println!("{:?}", user);
}

Тестове

Тестове

Ако си създадем проект - библиотека, cargo създава примерен код с един тест

1
cargo new --lib example

Тестове

Ако си създадем проект - библиотека, cargo създава примерен код с един тест

1
cargo new --lib example
1 2 3 4 5 6 7 8 9
// src/lib.rs

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
fn main() {}

Тестове

Тестовете са функции, анотирани с #[test]

1 2 3 4 5 6 7 8
fn add_two(x: i32) -> i32 {
    x + 2
}

#[test]
fn it_works() {
    assert_eq!(add_two(2), 4);
}
#![allow(dead_code)]
fn add_two(x: i32) -> i32 {
    x + 2
}

#[test]
fn it_works() {
    assert_eq!(add_two(2), 4);
}
fn main() {}

Тестове

1 2 3 4 5 6 7 8
#[test]
fn always_succeeds() {
}

#[test]
fn always_fails() {
    panic!(":@");
}
#![allow(dead_code)]
#[test]
fn always_succeeds() {
}

#[test]
fn always_fails() {
    panic!(":@");
}
fn main() {}

Тестове

С cargo test се изпълняват всички тестове

     Running target/debug/deps/example-32a7ca0b7a4e165f

running 3 tests
test always_succeeds ... ok
test it_works ... ok
test always_fails ... FAILED

failures:

---- always_fails stdout ----
thread 'always_fails' panicked at ':@', src/lib.rs:16:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    always_fails

test result: FAILED. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

Panic

Panic

Panic

Panic

Panic

Panic

Примери

Panic

Макроси

Panic

Макроси

Panic

Макроси

Panic

Макроси

Panic

Макроси

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

1 2 3 4 5
fn add_two(x: i32) -> i32 { x + 2 }

fn main() {
    assert!(add_two(2) == 5);
}
thread 'main' panicked at 'assertion failed: add_two(2) == 5', src/bin/main_2258266c6522f9d4e749aa7dbf59e6832f74bb89.rs:4:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fn add_two(x: i32) -> i32 { x + 2 }

fn main() {
    assert!(add_two(2) == 5);
}

Тестове

Asserts

assert_eq! и assert_ne! показват и какви са стойностите, които сравняваме

1 2 3 4 5
fn add_two(x: i32) -> i32 { x + 2 }

fn main() {
    assert_eq!(add_two(2), 5);
}
thread 'main' panicked at 'assertion failed: `(left == right)` left: `4`, right: `5`', src/bin/main_b75edb5d696cf9ff5dbb5197fef7b52a9a8afdba.rs:4:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fn add_two(x: i32) -> i32 { x + 2 }

fn main() {
    assert_eq!(add_two(2), 5);
}

Тестове

Panics

За да тестваме, че при определен вход програмата гърми, има допълнителен атрибут #[should_panic]

1 2 3 4 5 6 7 8 9 10
fn connect(addr: &str) {
    // no error handling, will panic if it can't parse `addr`
    let ip_addr: Ipv4Addr = addr.parse().unwrap();
}

#[test]
#[should_panic]
fn cant_connect_to_invalid_ip() {
    connect("10.20.30.1234");
}
#![allow(dead_code)]
#![allow(unused_variables)]
use std::net::Ipv4Addr;
fn connect(addr: &str) {
    // no error handling, will panic if it can't parse `addr`
    let ip_addr: Ipv4Addr = addr.parse().unwrap();
}

#[test]
#[should_panic]
fn cant_connect_to_invalid_ip() {
    connect("10.20.30.1234");
}
fn main(){}

Организация на тестовете

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

1 2 3 4 5 6 7 8 9 10 11 12
fn add_two(x: i32) -> i32 {
    x + 2
}

mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
warning: unused import: `super::*` --> src/bin/main_a31459644e03778e3e1ebe68bc367cdcce44684f.rs:7:9 | 7 | use super::*; | ^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: 1 warning emitted
#![allow(dead_code)]
fn add_two(x: i32) -> i32 {
    x + 2
}

mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
fn main() {}

Тестове

С #[cfg(test)] тестовете се компилират само при cargo test

1 2 3 4 5 6 7 8 9 10 11 12 13
fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
#![allow(dead_code)]
fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(add_two(2), 4);
    }
}
fn main() {}

Организация на тестовете

Unit tests

Организация на тестовете

Unit tests

Организация на тестовете

Unit tests

Организация на тестовете

Unit tests

1 2 3 4 5 6 7 8 9 10 11 12 13
fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adder_adds() {
        assert_eq!(add_two(2), 4);
    }
}
#![allow(dead_code)]
fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adder_adds() {
        assert_eq!(add_two(2), 4);
    }
}
fn main() {}

Организация на тестовете

Unit tests

Като подмодул тестовете имат достъп до private функционалността на модула

1 2 3 4 5 6 7 8 9 10 11 12 13
pub fn add_two(x: i32) -> i32 { internal_adder(x) }

fn internal_adder(x: i32) -> i32 { x + 2 }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adder_adds() {
        assert_eq!(internal_adder(2), 4);
    }
}
#![allow(dead_code)]
pub fn add_two(x: i32) -> i32 { internal_adder(x) }

fn internal_adder(x: i32) -> i32 { x + 2 }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn adder_adds() {
        assert_eq!(internal_adder(2), 4);
    }
}
fn main() {}

Организация на тестовете

Integration tests

Организация на тестовете

Integration tests

Организация на тестовете

Integration tests

Организация на тестовете

Integration tests

1 2 3 4 5 6 7
adder
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    └── adder.rs

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Организация на тестовете

Integration tests

1 2 3 4 5 6
// tests/adder.rs

#[test]
fn adder_adds() {
    assert_eq!(adder::add_two(2), 4);
}

Въпроси