Документация, тестване
и една торба с инструменти
24 октомври 2019
Преговор
Енумерации
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)
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 и представяне на липсваща стойност
enum Option<T> {
Some(T),
None,
}
fn main() { enum Option{ Some(T), None, } }
Преговор
Refutable/Irrefutable patterns
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
- (един от) най-ранните стандарти за кодиране, създаден през 60-те
Кодиране на низове
ASCII
- (един от) най-ранните стандарти за кодиране, създаден през 60-те
- 128 символа, кодирани с числа от 0 до 127
- 32 контролни символа
- 96 - латинската азбука, арабските цифри и най-често използваната пунктуация
Кодиране на низове
ASCII
- (един от) най-ранните стандарти за кодиране, създаден през 60-те
- 128 символа, кодирани с числа от 0 до 127
- 32 контролни символа
- 96 - латинската азбука, арабските цифри и най-често използваната пунктуация
- повечето модерни схеми за кодиране се базират на него
Кодиране на низове
Разширено ASCII
Кодиране на низове
Разширено ASCII
- има нужда да се представят други символи (нелатинска азбука, друга пунктоация, емотикони, ..)
Кодиране на низове
Разширено ASCII
- има нужда да се представят други символи (нелатинска азбука, друга пунктоация, емотикони, ..)
- в
u8
се побират 256 различни стойности, ASCII кодирането заема само 128
Кодиране на низове
Разширено ASCII
- има нужда да се представят други символи (нелатинска азбука, друга пунктоация, емотикони, ..)
- в
u8
се побират 256 различни стойности, ASCII кодирането заема само 128 - затова са се появили много (стотици) разширени таблици с кодове за различни случаи
Кодиране на низове
Разширено ASCII
- има нужда да се представят други символи (нелатинска азбука, друга пунктоация, емотикони, ..)
- в
u8
се побират 256 различни стойности, ASCII кодирането заема само 128 - затова са се появили много (стотици) разширени таблици с кодове за различни случаи
- във всяка таблица от 128 до 255 се кодират различни символи
Кодиране на низове
Разширено ASCII
- има нужда да се представят други символи (нелатинска азбука, друга пунктоация, емотикони, ..)
- в
u8
се побират 256 различни стойности, ASCII кодирането заема само 128 - затова са се появили много (стотици) разширени таблици с кодове за различни случаи
- във всяка таблица от 128 до 255 се кодират различни символи
- пример:
latin-1
,windows-1251
, …
Кодиране на низове
Разширено ASCII
- има нужда да се представят други символи (нелатинска азбука, друга пунктоация, емотикони, ..)
- в
u8
се побират 256 различни стойности, ASCII кодирането заема само 128 - затова са се появили много (стотици) разширени таблици с кодове за различни случаи
- във всяка таблица от 128 до 255 се кодират различни символи
- пример:
latin-1
,windows-1251
, … - ако не знаем с коя таблица е кодиран низа, не знаем какви символи съдържа
Кодиране на низове
Unicode
Кодиране на низове
Unicode
- различните разширени ASCII таблици се оказват проблемни
Кодиране на низове
Unicode
- различните разширени ASCII таблици се оказват проблемни
- като решение е измислен Unicode стандарта
Кодиране на низове
Unicode
символ → ℕ
Кодиране на низове
Unicode
символ → ℕ
- голяма таблица която съдържа всички символи
Кодиране на низове
Unicode
символ → ℕ
- голяма таблица която съдържа всички символи
- на всеки символ съответства число - code point
Кодиране на низове
Chars
- типа
char
в Rust означава unicode code point
Кодиране на низове
Chars
- типа
char
в Rust означава unicode code point
Пример:
Code: U+044F
Glyph: я
Description: Cyrillic Small Letter Ya
println!("0x{:x}", 'я' as u32);
0x44f
fn main() { println!("0x{:x}", 'я' as u32); }
Кодиране на низове
- има различни варианти за кодиране на unicode code points до битове
Кодиране на низове
UTF-32
- всеки символ се кодира с 32-битово число - стойността на code point-а
Кодиране на низове
UTF-32
- Предимства:
- операцията "преброяване на символите" е константна
- както и намирането на n-тия символ
Кодиране на низове
UTF-32
- Предимства:
- операцията "преброяване на символите" е константна
- както и намирането на n-тия символ
- Недостатъци:
- заема много памет
Кодиране на низове
UTF-32
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
- всеки символ се кодира с едно или две 16-битови числа
- често се използва в Windows API-та
- и за вътрешна репрезентация на низове в някои езици (напр. JavaScript)
Кодиране на низове
UTF-16
- Предимства:
Кодиране на низове
UTF-16
- Предимства:
- азбуките на всички говорими езици се кодират с 16 бита
- почти "fixed length"
Кодиране на низове
UTF-16
- Предимства:
- азбуките на всички говорими езици се кодират с 16 бита
- почти "fixed length"
- Недостатъци:
- няма предимствата нито на utf-8, нито на utf-32
Кодиране на низове
UTF-16
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
- символите се кодират с 1, 2, 3 или 4 байта
- ASCII символите (0x00 - 0x7f) се кодират с един байт
- останалите символи се кодират с 2, 3 или 4 байта, като се използват само стойности от 0x80 - 0xff
- най-разпространения метод за кодиране
- низовете в Rust (
&str
,String
) са utf-8
Кодиране на низове
UTF-8
- Предимства:
- съвместим с ASCII - всеки ASCII низ е и utf-8 низ!
- не-ASCII символите не съдържат ASCII байтове (0x00 - 0x7f)!
- с две думи - код който очаква да работи с ASCII низ би приел utf-8 низ подобаващо
Кодиране на низове
UTF-8
- Предимства:
- съвместим с ASCII - всеки ASCII низ е и utf-8 низ!
- не-ASCII символите не съдържат ASCII байтове (0x00 - 0x7f)!
- с две думи - код който очаква да работи с ASCII низ би приел utf-8 низ подобаващо
- поток от байтове - няма разлика между big endian и little endian
- устойчив на корупция на данните
- и други
Кодиране на низове
UTF-8
- Предимства:
- съвместим с ASCII - всеки ASCII низ е и utf-8 низ!
- не-ASCII символите не съдържат ASCII байтове (0x00 - 0x7f)!
- с две думи - код който очаква да работи с ASCII низ би приел utf-8 низ подобаващо
- поток от байтове - няма разлика между big endian и little endian
- устойчив на корупция на данните
- и други
- Недостатъци:
- variable lenght encoding - преброяването на символите или намирането на n-тия символ изисква итерация по низа
Кодиране на низове
UTF-8
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 |
Низове
Заключение
Низове
Заключение
- Внимавайте с UTF-8! Обработката на низове изисква итерация през всички символи
Низове
Заключение
- Внимавайте с UTF-8! Обработката на низове изисква итерация през всички символи
- Вижте документацията на:
Документация
Документация
rustdoc
- инструмент за генериране на API документация
Документация
rustdoc
- инструмент за генериране на API документация- инсталира се заедно с Rust
Документация
rustdoc
- инструмент за генериране на API документация- инсталира се заедно с Rust
- най-лесно се използва през
cargo
Документация
rustdoc
- инструмент за генериране на API документация- инсталира се заедно с Rust
- най-лесно се използва през
cargo
cargo doc
Документация
rustdoc
Документация
rustdoc
- документира всички публично видими декларации
Документация
rustdoc
- документира всички публично видими декларации
- документира и всички библиотеки (crates) на който зависим
Документация
rustdoc
- документира всички публично видими декларации
- документира и всички библиотеки (crates) на който зависим
cargo doc --open
- генерира документацията и я отваря в уеб браузър
Документация
rustdoc
- документира всички публично видими декларации
- документира и всички библиотеки (crates) на който зависим
cargo doc --open
- генерира документацията и я отваря в уеб браузърcargo doc --no-deps
- ако dependency-тата се компилират твърде бавно
Документация
rustdoc
- документира всички публично видими декларации
- документира и всички библиотеки (crates) на който зависим
cargo doc --open
- генерира документацията и я отваря в уеб браузърcargo doc --no-deps
- ако dependency-тата се компилират твърде бавноcargo doc --document-private-items
- документира всички декларации
Документация
rustdoc
- документира всички публично видими декларации
- документира и всички библиотеки (crates) на който зависим
cargo doc --open
- генерира документацията и я отваря в уеб браузърcargo doc --no-deps
- ако dependency-тата се компилират твърде бавноcargo doc --document-private-items
- документира всички декларацииcargo doc --help
Документация
Док-коментари
Документация
Док-коментари
- коментар, който започва с три наклонени черти (
///
)
Документация
Док-коментари
- коментар, който започва с три наклонени черти (
///
) - служи за документация на следващата декларация
Документация
Док-коментари
- коментар, който започва с три наклонени черти (
///
) - служи за документация на следващата декларация
- поддържа форматиране с markdown
Документация
На структури
/// 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() {}
Документация
На структури
/// 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() {}
- първият ред е кратко описание, останалите са подробно
Документация
На полета
/// 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() {}
Документация
На функции и методи
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() {}
Документация
На модули
/// 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() {}
Документация
Вътрешни док-коментари
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() {}
Документация
Вътрешни док-коментари
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 поддържа блокове код. Чрез тях може да се добавят примери за използване на библиотеката
/// 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>;
Тестване на примери
"Защото единственото по-лошо от липсваща документация, е грешна документация"
/**
* Always returns true.
*/
public boolean isAvailable() {
return false;
}
Тестване на примери
Тестване на примери
- автоматично се добавят тестове за примери в документацията
Тестване на примери
- автоматично се добавят тестове за примери в документацията
- тества се, че примера се компилира
Тестване на примери
- автоматично се добавят тестове за примери в документацията
- тества се, че примера се компилира
- тества се, че кодът се изпълнява без грешка (напр.
assert
-ите не гърмят)
Тестване на примери
- автоматично се добавят тестове за примери в документацията
- тества се, че примера се компилира
- тества се, че кодът се изпълнява без грешка (напр.
assert
-ите не гърмят) - тестовете за примерите могат да се изпълнят с
cargo test
Тестване на примери
/// 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>;
Тестване на примери
/// 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
Тестване на примери
Как работи това?
Тестване на примери
Как работи това?
- всеки пример се компилира до изпълнимо binary
Тестване на примери
Как работи това?
- всеки пример се компилира до изпълнимо binary
- ако липсва main функция, rustdoc загражда кода с
main
Тестване на примери
Как работи това?
- всеки пример се компилира до изпълнимо binary
- ако липсва main функция, rustdoc загражда кода с
main
/// ```
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// ```
Тестване на примери
Как работи това?
- всеки пример се компилира до изпълнимо binary
- ако липсва main функция, rustdoc загражда кода с
main
/// ```
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// ```
/// ```
/// fn main() {
/// assert_eq!('1'.to_digit(10), Some(1));
/// assert_eq!('f'.to_digit(16), Some(15));
/// }
/// ```
Тестване на примери
Как работи това?
Тестване на примери
Как работи това?
- нашият crate е достъпен в примера като външна библиотека
Тестване на примери
Как работи това?
- нашият crate е достъпен в примера като външна библиотека
- трябва да се използва
use <my_crate_name>::
вместоuse crate::
Тестване на примери
Как работи това?
- нашият crate е достъпен в примера като външна библиотека
- трябва да се използва
use <my_crate_name>::
вместоuse crate::
- но това е начина по който потребителя ще ползва библиотеката така или иначе
Тестване на примери
Как работи това?
- нашият crate е достъпен в примера като външна библиотека
- трябва да се използва
use <my_crate_name>::
вместоuse crate::
- но това е начина по който потребителя ще ползва библиотеката така или иначе
/// ```
/// use my_crate_name::{Foo, Bar};
///
/// let foo = Foo::new();
/// let bar = Bar::from(foo);
/// ```
Тестване на примери
Скриване на редове
Ако ред в пример започва с "# " този ред няма да се покаже в документацията
/// # 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
- примери които които се компилират, но не се изпълняват
- примери които нито се компилират, нито се изпълняват
- и други специфики
Атрибути
Атрибути
#[derive(Debug)]
#[cfg(target_os = "macos")]
#[allow(unused_variables)]
#[warn(missing_docs)]
#[deny(unsafe_code)]
Атрибути
#[attribute]
- важи за елемента след него
Атрибути
#[attribute]
- важи за елемента след него#![attribute]
- важи за елемента в който се намира
Атрибути
#[attribute]
- важи за елемента след него#![attribute]
- важи за елемента в който се намира#[attribute(arg)]
Атрибути
#[attribute]
- важи за елемента след него#![attribute]
- важи за елемента в който се намира#[attribute(arg)]
#[attribute(arg1, arg2)]
Атрибути
#[attribute]
- важи за елемента след него#![attribute]
- важи за елемента в който се намира#[attribute(arg)]
#[attribute(arg1, arg2)]
#[attribute(key = val)]
Атрибути
Служат за различни неща
Атрибути
Служат за различни неща
- флагове към компилатора
Атрибути
Служат за различни неща
- флагове към компилатора
- conditional compilation
Атрибути
Служат за различни неща
- флагове към компилатора
- conditional compilation
- генериране на код в стил макроси
Атрибути
Служат за различни неща
- флагове към компилатора
- conditional compilation
- генериране на код в стил макроси
- и други
Атрибути
Ще говорим повече следващия път, но.. можем да използваме атрибути за да имплементираме някои често използвани трейтове за наш тип
#[derive(Debug)]
struct User {
name: String,
age: i32,
}
#![allow(dead_code)] fn main() { #[derive(Debug)] struct User { name: String, age: i32, } }
Атрибути
Ще говорим повече следващия път, но.. можем да използваме атрибути за да имплементираме някои често използвани трейтове за наш тип
#[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 създава примерен код с един тест
cargo new --lib example
Тестове
Ако си създадем проект - библиотека, cargo създава примерен код с един тест
cargo new --lib example
// 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]
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() {}
Тестове
- тестовите функции не приемат аргументи и не връщат нищо
- теста е минал успешно ако функцията завърши
- теста е фейлнал ако функцията "гръмне"
#[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
- в тестове е удобно да използваме
assert_*
макросите
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите - те проверяват дали условие е изпълнено, и ако не спират програмата
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите - те проверяват дали условие е изпълнено, и ако не спират програмата
assert!
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите - те проверяват дали условие е изпълнено, и ако не спират програмата
assert!
assert_eq!
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите - те проверяват дали условие е изпълнено, и ако не спират програмата
assert!
assert_eq!
assert_ne!
Тестове
Asserts
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!
показват и какви са стойностите, които сравняваме
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]
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(){}
Организация на тестовете
Практика е тестовете да стоят в отделен модул
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
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
- тестове, които покриват отделна малка компонента от кода
- например тестове за функционалността на определен модул
- удобно е тестовете да са подмодул на модула, който тестват
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 функционалността на модула
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
- тестове, които покриват функционалността на програмата или библиотеката като цяло
- намират се отделно от сорс кода, в папка tests
adder
├── Cargo.lock
├── Cargo.toml
├── src
│ └── lib.rs
└── tests
└── adder.rs
Организация на тестовете
Integration tests
- тестове, които покриват функционалността на програмата или библиотеката като цяло
- намират се отделно от сорс кода, в папка tests
adder
├── Cargo.lock
├── Cargo.toml
├── src
│ └── lib.rs
└── tests
└── adder.rs
- името на файла може да е произволно
Организация на тестовете
Integration tests
// tests/adder.rs
#[test]
fn adder_adds() {
assert_eq!(adder::add_two(2), 4);
}
Организация на тестовете
Integration tests
// tests/adder.rs
#[test]
fn adder_adds() {
assert_eq!(adder::add_two(2), 4);
}
- тестът се компилира отделно от библиотеката ни
Организация на тестовете
Integration tests
// tests/adder.rs
#[test]
fn adder_adds() {
assert_eq!(adder::add_two(2), 4);
}
- тестът се компилира отделно от библиотеката ни
- библиотеката ни може да се използва като външно dependency
Организация на тестовете
Integration tests
// tests/adder.rs
#[test]
fn adder_adds() {
assert_eq!(adder::add_two(2), 4);
}
- тестът се компилира отделно от библиотеката ни
- библиотеката ни може да се използва като външно dependency
- имаме достъп само до публичния интерфейс
Организация на тестовете
Integration tests
// tests/adder.rs
#[test]
fn adder_adds() {
assert_eq!(adder::add_two(2), 4);
}
- тестът се компилира отделно от библиотеката ни
- библиотеката ни може да се използва като външно dependency
- имаме достъп само до публичния интерфейс
cargo test
компилира и изпълнява всички файлове вtests/