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

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

20 октомври 2022

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

Преговор

Енумерации

1 2 3 4 5 6 7 8 9 10 11
enum IpAddrKind {
    V4,
    V6,
}

enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i64, i64, i64),
}
fn main() {
enum IpAddrKind {
    V4,
    V6,
}

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

Преговор

Енумерации

1 2 3 4 5 6 7 8 9 10 11
enum IpAddrKind {
    V4,
    V6,
}

enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i64, i64, i64),
}
fn main() {
enum IpAddrKind {
    V4,
    V6,
}

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

Преговор

Енумерации

При някои други езици бихме написали нещо подобно

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

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

Преговор

Енумерации

При някои други езици бихме написали нещо подобно

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

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

Но това има недостатъци:

Преговор

Енумерации

При някои други езици бихме написали нещо подобно

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

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

Но това има недостатъци:

Преговор

Енумерации

При някои други езици бихме написали нещо подобно

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

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

Но това има недостатъци:

Преговор

Енумерации

При Rust можем да окажем такива инварианти в типовата система

1 2 3 4
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

Преговор

Енумерации

При Rust можем да окажем такива инварианти в типовата система

1 2 3 4
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

Преговор

Енумерации

При Rust можем да окажем такива инварианти в типовата система

1 2 3 4
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

Преговор

Енумерации

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 a = Message::Quit;
let b = Message::Move { x: 3, y: 4 };
let c = Message::Write(String::from("baba"));
let d = Message::ChangeColor(255, 0, 0);
fn main() {
enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i64, i64, i64),
}

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

Преговор

Енумерации

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

let message = Message::Move { x: 3, y: 4 };
println!("{}", message.x); // ?

Преговор

Енумерации

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

let message = Message::Move { x: 3, y: 4 };
println!("{}", message.x); // 💥
error[E0609]: no field `x` on type `Message` --> src/bin/main_f4afc8bfc4bab7d4a12a395757ee11c349d457b1.rs:10:24 | 10 | println!("{}", message.x); // 💥 | ^ For more information about this error, try `rustc --explain E0609`. error: could not compile `rust` due to previous error
fn main() {
enum Message {
    Quit,
    Move { x: i64, y: i64 },
    Write(String),
    ChangeColor(i64, i64, i64),
}

let message = Message::Move { x: 3, y: 4 };
println!("{}", message.x); // 💥
}

Преговор

Съпоставяне на образци (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!"),
    Message::ChangeColor(_, _, _)            => 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!"),
    Message::ChangeColor(_, _, _)            => println!("Don't care."),
}
}

Преговор

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

1 2 3 4
enum Option<T> {
    Some(T),
    None,
}
fn main() {
enum Option {
    Some(T),
    None,
}
}
1 2
let five = Some(5);
let none: Option<i32> = None;
fn main() {
let five = Some(5);
let none: Option = 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!");
}
}

Преговор

Итерация

1 2 3 4 5 6 7 8 9 10 11
let arr = &["едно", "две", "три"];

let mut iter = arr.iter();
while let Some(val) = iter.next() {
    println!("{}", val);
}

let iter = arr.iter();
for val in iter {
    println!("{}", val);
}
едно две три едно две три
#[allow(unused_variables)]
fn main() {
let arr = &["едно", "две", "три"];

let mut iter = arr.iter();
while let Some(val) = iter.next() {
    println!("{}", val);
}

let iter = arr.iter();
for val in iter {
    println!("{}", val);
}
}

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

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

ASCII

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

ASCII

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

ASCII

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

ASCII

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Unicode

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

Unicode

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

Unicode

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

Unicode

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

Unicode

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

Unicode

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

Chars

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

Chars

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

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

println!("{:?}", std::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

Пример: (U+d55c)

Unicode code point bits:

                  11010101 01011100

UTF-8 bits:

         11101101 10010101 10011100

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

UTF-8

Пример: 💣 (U+1f4a3)

Unicode code point bits:

         00000001 11110100 10100011

UTF-8 bits:

11110000 10011111 10010010 10100011

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

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

Тестове

Тестове

Ако си създадем проект - библиотека, 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() {}

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(){}

Тестове

Изпълнение

С 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

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

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

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);
    }
}
#![allow(dead_code)]
#![allow(unused_imports)]
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);
}

Въпроси