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

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

24 октомври 2019

Преговор

Енумерации

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

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

Разширено ASCII

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

Разширено ASCII

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

Разширено ASCII

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

Разширено ASCII

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

Разширено ASCII

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

Разширено ASCII

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

Разширено ASCII

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

Unicode

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

Unicode

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

Unicode

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

Unicode

символ → ℕ

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

Unicode

символ → ℕ

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

Unicode

символ → ℕ

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

Chars

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

Chars

1
println!("0x{:x}", 'я' as u32);
0x44f
fn main() {
println!("0x{:x}", 'я' as u32);
}

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

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

UTF-32

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

UTF-32

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

UTF-32

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

UTF-32

1
let utf32 = "Hello😊".chars().collect::<Vec<char>>();
#![allow(unused_variables)]
fn main() {
let utf32 = "Hello😊".chars().collect::>();
}
Glyphs H e l l o 😊
Chars 0x48 0x65 0x6c 0x6c 0x6f 0x1f60a
Bytes (be) 00 00 00 48 00 00 00 65 00 00 00 6c 00 00 00 6c 00 00 00 6f 00 01 f6 0a
Glyphs З д р а в е й
Chars 0x417 0x434 0x440 0x430 0x432 0x435 0x439
Bytes (be) 00 00 04 17 00 00 04 34 00 00 04 40 00 00 04 30 00 00 04 32 00 00 04 35 00 00 04 39

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

UTF-16

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

UTF-16

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

UTF-16

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

UTF-16

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

UTF-16

1
let utf16 = "Hello😊".encode_utf16().collect::<Vec<u16>>();
#![allow(unused_variables)]
fn main() {
let utf16 = "Hello😊".encode_utf16().collect::>();
}
Glyphs H e l l o 😊
Chars 0x48 0x65 0x6c 0x6c 0x6f 0x1f60a
Bytes (be) 00 48 00 65 00 6c 00 6c 00 6f d8 3d de 0a
Glyphs З д р а в е й
Chars 0x417 0x434 0x440 0x430 0x432 0x435 0x439
Bytes (be) 04 17 04 34 04 40 04 30 04 32 04 35 04 39

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

UTF-8

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

UTF-8

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

UTF-8

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

UTF-8

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

UTF-8

1
let bytes: &[u8] = "Hello😊".as_bytes();
#![allow(unused_variables)]
fn main() {
let bytes: &[u8] = "Hello😊".as_bytes();
}
Glyphs H e l l o 😊
Chars 0x48 0x65 0x6c 0x6c 0x6f 0x1f60a
Bytes 48 65 6c 6c 6f f0 9f 98 8a
Glyphs З д р а в е й
Chars 0x417 0x434 0x440 0x430 0x432 0x435 0x439
Bytes d0 97 d0 b4 d1 80 d0 b0 d0 b2 d0 b5 d0 b9

Низове

Заключение

Низове

Заключение

Низове

Заключение

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

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

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

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

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

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

rustdoc

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

rustdoc

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

rustdoc

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

rustdoc

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

rustdoc

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

rustdoc

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

rustdoc

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

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

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

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

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

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

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

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

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

На структури

1 2 3 4 5 6 7 8
/// 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> {
    pub start: Idx,
    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 {
    pub start: Idx,
    pub end: Idx,
}
fn main() {}

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

На структури

1 2 3 4 5 6 7 8
/// 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> {
    pub start: Idx,
    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 {
    pub start: Idx,
    pub end: Idx,
}
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
/// 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 {
    /* ... */
}
///
/// 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 {
    /* ... */
}
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 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() {}

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

Примери

Мarkdown поддържа блокове код. Чрез тях може да се добавят примери за използване на библиотеката

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
/**
 * 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

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

Как работи това?

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

Как работи това?

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

Как работи това?

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

Как работи това?

1 2 3 4
/// ```
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// ```

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

Как работи това?

1 2 3 4
/// ```
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// ```
1 2 3 4 5 6
/// ```
/// fn main() {
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// }
/// ```

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

Как работи това?

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

Как работи това?

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

Как работи това?

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

Как работи това?

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

Как работи това?

1 2 3 4 5 6
/// ```
/// use my_crate_name::{Foo, Bar};
///
/// let foo = Foo::new();
/// let bar = Bar::from(foo);
/// ```

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

Скриване на редове

Ако ред в пример започва с "# " този ред няма да се покаже в документацията

1 2 3 4 5 6 7 8
/// # struct User { username: String, email: String, sign_in_count: u64 }
/// # fn main() {
/// let user = User {
///     username: String::from("Иванчо"),
///     email: String::from("ivan40@abv.bg"),
///     sign_in_count: 10,
/// };
/// # }

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

Атрибути

Атрибути

Атрибути

Атрибути

Атрибути

Атрибути

Атрибути

Атрибути

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

Атрибути

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

Атрибути

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

Атрибути

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

Атрибути

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

Атрибути

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

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

Атрибути

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

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

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

Тестове

Asserts

1 2 3
fn main() {
    assert!(add_two(2) == 5);
}
thread 'main' panicked at 'assertion failed: add_two(2) == 5', src/bin/main_1f45ef3627e1b9c20a54553adb5c9901c069d6c0.rs:3: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
fn main() {
    assert_eq!(add_two(2), 5);
}
thread 'main' panicked at 'assertion failed: `(left == right)` left: `4`, right: `5`', src/bin/main_487c7438e71705ec5d9695e139f4cdbbe443c2b1.rs:3: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: String) {
    // 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".to_string());
}
#![allow(dead_code)]
#![allow(unused_variables)]
use std::net::Ipv4Addr;
fn connect(addr: String) {
    // 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".to_string());
}
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
#![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

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

Въпроси