Те(к)стови работи
низове, документация, тестване
20 октомври 2022
Административни неща
- първото домашно е пуснато
- https://fmi.rust-lang.bg/announcements/2
- срок - до четвъртък 27.10.2021 17:00
Преговор
Енумерации
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), } }
Преговор
Енумерации
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), } }
- повечето типове (struct, list, tuple) са "контейнери" -- и един пакет данни, и друг
- енумерациите позволяват изразяване на "екслузивност" -- или един пакет данни, или друг
Преговор
Енумерации
При някои други езици бихме написали нещо подобно
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") };
Преговор
Енумерации
При някои други езици бихме написали нещо подобно
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") };
Но това има недостатъци:
Преговор
Енумерации
При някои други езици бихме написали нещо подобно
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") };
Но това има недостатъци:
- не е ясно как трябва да се използва структурата - трябва да се чете документация
Преговор
Енумерации
При някои други езици бихме написали нещо подобно
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") };
Но това има недостатъци:
- не е ясно как трябва да се използва структурата - трябва да се чете документация
- възможно е да създадем невалидна инстанция на
IpAddr
:IpAddr { v4: NULL, v6: NULL }
IpAddr { v4: (127, 0, 0, 1), v6: String::from("::1") }
Преговор
Енумерации
При Rust можем да окажем такива инварианти в типовата система
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
Преговор
Енумерации
При Rust можем да окажем такива инварианти в типовата система
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
- make unreacheable states unrepresentable
Преговор
Енумерации
При Rust можем да окажем такива инварианти в типовата система
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
- make unreacheable states unrepresentable
- (направи недостижимите състояния неизразяеми)
Преговор
Енумерации
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); }
Преговор
Енумерации
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); // ?
Преговор
Енумерации
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)
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 и представяне на липсваща стойност
enum Option<T> {
Some(T),
None,
}
fn main() { enum Option{ Some(T), None, } }
let five = Some(5);
let none: Option<i32> = None;
fn main() { let five = Some(5); let none: Option= 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!"); } }
Преговор
Итерация
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
- един от по-ранните стандарти за кодиране
- 128 символа, кодирани с числа от 0 до 127
- 32 контролни символа
- 96 - латинската азбука, арабските цифри и най-често използваната пунктуация
Кодиране на низове
ASCII
- един от по-ранните стандарти за кодиране
- 128 символа, кодирани с числа от 0 до 127
- 32 контролни символа
- 96 - латинската азбука, арабските цифри и най-често използваната пунктуация
- недостатък - много ограничен набор от символи
Кодиране на низове
Кодови таблици
Кодиране на низове
Кодови таблици
- разширение на ASCII
Кодиране на низове
Кодови таблици
- разширение на ASCII
- 256 символа, кодирани с числа от 0 до 255
- 0 до 127 - ascii
- 128 до 255 - други символи (зависи от таблицата)
Кодиране на низове
Кодови таблици
- разширение на ASCII
- 256 символа, кодирани с числа от 0 до 255
- 0 до 127 - ascii
- 128 до 255 - други символи (зависи от таблицата)
- появили са се много (стотици) разширени таблици с кодове за различни случаи
Кодиране на низове
Кодови таблици
- разширение на ASCII
- 256 символа, кодирани с числа от 0 до 255
- 0 до 127 - ascii
- 128 до 255 - други символи (зависи от таблицата)
- появили са се много (стотици) разширени таблици с кодове за различни случаи
- например: latin-1, windows-1251, cp437, …
Кодиране на низове
Кодови таблици
Недостатъци:
Кодиране на низове
Кодови таблици
Недостатъци:
- много ограничен набор от символи в една таблица - трябва да се използват различни таблици
Кодиране на низове
Кодови таблици
Недостатъци:
- много ограничен набор от символи в една таблица - трябва да се използват различни таблици
- ако не знаем с коя таблица е кодиран низа, не знаем какви символи съдържа
Кодиране на низове
Кодови таблици
Недостатъци:
- много ограничен набор от символи в една таблица - трябва да се използват различни таблици
- ако не знаем с коя таблица е кодиран низа, не знаем какви символи съдържа
- никой не казва с коя таблица е кодиран низа
Кодиране на низове
Unicode
Кодиране на низове
Unicode
- стандарт, създаден да оправи цялата тази бъркотия
Кодиране на низове
Unicode
- стандарт, създаден да оправи цялата тази бъркотия
- голяма таблица която съдържа всички символи
Кодиране на низове
Unicode
- стандарт, създаден да оправи цялата тази бъркотия
- голяма таблица която съдържа всички символи
- дефинира съответствие от число (code point) до символ
Кодиране на низове
Unicode
- стандарт, създаден да оправи цялата тази бъркотия
- голяма таблица която съдържа всички символи
- дефинира съответствие от число (code point) до символ
- ℕ → символ
Кодиране на низове
Unicode
- стандарт, създаден да оправи цялата тази бъркотия
- голяма таблица която съдържа всички символи
- дефинира съответствие от число (code point) до символ
- ℕ → символ
- не дефинира как да се кодира числото (code point-а) като битове
Кодиране на низове
Chars
- типа
char
в Rust означава "unicode scalar value" - (което е като "code point", но по-различно :). "Any Unicode code point except high-surrogate and low-surrogate code points. In other words, the ranges of integers 0 to D7FF16 and E00016 to 10FFFF16 inclusive")
Кодиране на низове
Chars
- типа
char
в Rust означава "unicode scalar value" - (което е като "code point", но по-различно :). "Any Unicode code point except high-surrogate and low-surrogate code points. In other words, the ranges of integers 0 to D7FF16 and E00016 to 10FFFF16 inclusive")
- пример:
Code: U+044F
Glyph: я
Description: Cyrillic Small Letter Ya
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
- всеки символ се кодира с 32-битово число - стойността на code point-а
Кодиране на низове
UTF-32
- всеки символ се кодира с 32-битово число - стойността на code point-а
- все едно
Vec<char>
Кодиране на низове
UTF-16
Кодиране на низове
UTF-16
- всеки символ се кодира с едно или две 16-битови числа
Кодиране на низове
UTF-16
- всеки символ се кодира с едно или две 16-битови числа
- използва се в Windows API-та
Кодиране на низове
UTF-16
- всеки символ се кодира с едно или две 16-битови числа
- използва се в Windows API-та
- и за вътрешна репрезентация на низове в някои езици (напр. Java, JavaScript)
Кодиране на низове
UTF-8
Кодиране на низове
UTF-8
- символите се кодират с 1, 2, 3 или 4 байта
Кодиране на низове
UTF-8
- символите се кодират с 1, 2, 3 или 4 байта
- най-разпространения метод за кодиране
Кодиране на низове
UTF-8
- символите се кодират с 1, 2, 3 или 4 байта
- най-разпространения метод за кодиране
- низовете в Rust (
&str
,String
) са 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
Предимства:
- съвместим с ASCII
Кодиране на низове
UTF-8
Предимства:
- съвместим с ASCII
- всеки ASCII низ е и utf-8 низ
Кодиране на низове
UTF-8
Предимства:
- съвместим с ASCII
- всеки ASCII низ е и utf-8 низ
- не-ASCII символите не съдържат ASCII байтове (0x00 - 0x7f)
Кодиране на низове
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 низ подобаващо
- поток от байтове - няма значение от endianness
Кодиране на низове
UTF-8
Предимства:
- съвместим с ASCII
- всеки ASCII низ е и utf-8 низ
- не-ASCII символите не съдържат ASCII байтове (0x00 - 0x7f)
- с две думи - код който очаква да работи с ASCII низ би приел utf-8 низ подобаващо
- поток от байтове - няма значение от endianness
- устойчив на повреда на данните
Кодиране на низове
UTF-8
Предимства:
- съвместим с ASCII
- всеки ASCII низ е и utf-8 низ
- не-ASCII символите не съдържат ASCII байтове (0x00 - 0x7f)
- с две думи - код който очаква да работи с ASCII низ би приел utf-8 низ подобаващо
- поток от байтове - няма значение от endianness
- устойчив на повреда на данните
- и други
Кодиране на низове
UTF-8
Предимства:
- съвместим с ASCII
- всеки ASCII низ е и utf-8 низ
- не-ASCII символите не съдържат ASCII байтове (0x00 - 0x7f)
- с две думи - код който очаква да работи с ASCII низ би приел utf-8 низ подобаващо
- поток от байтове - няма значение от endianness
- устойчив на повреда на данните
- и други
Недостатъци: - variable lenght encoding - преброяването на символите или намирането на n-тия символ изисква итерация по низа
Низове
Итерация
// 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); }
Низове
Итерация
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()
връща дължината на низ в брой байтове
let hi = "Здравей! 😊";
println!("{}", hi.len());
println!("{}", hi.chars().count());
20 10
fn main() { let hi = "Здравей! 😊"; println!("{}", hi.len()); println!("{}", hi.chars().count()); }
Низове
Индексация
При взимане на резен от низ се оказват брой байтове
let sub_hi = &"Здравей! 😊"[0..6];
println!("{:?}", sub_hi);
"Здр"
fn main() { let sub_hi = &"Здравей! 😊"[0..6]; println!("{:?}", sub_hi); }
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); }
Низове
Заключение
Низове
Заключение
- Внимавайте с UTF-8! Обработката на низове изисква итерация през всички символи
Низове
Заключение
- Внимавайте с UTF-8! Обработката на низове изисква итерация през всички символи
- Тествайте си домашните с кирилица
Низове
Заключение
- Внимавайте с UTF-8! Обработката на низове изисква итерация през всички символи
- Тествайте си домашните с кирилица
- Вижте документацията на:
Документация
Документация
Док-коментари
Документация
Док-коментари
- коментар, който започва с три наклонени черти (
///
)
Документация
Док-коментари
- коментар, който започва с три наклонени черти (
///
) - служи за документация на следващата декларация
Документация
Док-коментари
- коментар, който започва с три наклонени черти (
///
) - служи за документация на следващата декларация
- поддържа форматиране с 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> {
/// 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() {}
Документация
Вътрешни док-коментари
Коментари, които започват с //!
, документират елемента в който се намират.
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() {}
Документация
Документация
rustdoc
- инструмент за генериране на API документация
Документация
rustdoc
- инструмент за генериране на API документация- инсталира се заедно с Rust
Документация
rustdoc
- инструмент за генериране на API документация- инсталира се заедно с Rust
- най-лесно се използва през
cargo
Документация
rustdoc
- инструмент за генериране на API документация- инсталира се заедно с Rust
- най-лесно се използва през
cargo
cargo doc
Документация
rustdoc
- инструмент за генериране на API документация- инсталира се заедно с Rust
- най-лесно се използва през
cargo
cargo doc
cargo doc --open
Документация
Тестване на примери
/**
* 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
Документация
Тестване на примери
- блоковете код в документацията се използват за примери
Документация
Тестване на примери
- блоковете код в документацията се използват за примери
- чрез тестването им гарантираме, че примерите се поддържат синхронизирани с промени по кода
Документация
Тестване на примери
- блоковете код в документацията се използват за примери
- чрез тестването им гарантираме, че примерите се поддържат синхронизирани с промени по кода
- блоковете код не се използват за тестване
Документация
Тестване на примери
- блоковете код в документацията се използват за примери
- чрез тестването им гарантираме, че примерите се поддържат синхронизирани с промени по кода
- блоковете код не се използват за тестване
- за целта има отделни тестове
Документация
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 създава примерен код с един тест
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() {}
Panic
Panic
- грешка, от която програмата ни не може да се възстанови
Panic
- грешка, от която програмата ни не може да се възстанови
- unwind-ва стека, и терминира текущата нишка
Panic
- грешка, от която програмата ни не може да се възстанови
- unwind-ва стека, и терминира текущата нишка
- при паника в главната нишка се убива цялата програма и се изписва съобщение за грешка
Panic
- грешка, от която програмата ни не може да се възстанови
- unwind-ва стека, и терминира текущата нишка
- при паника в главната нишка се убива цялата програма и се изписва съобщение за грешка
- не са exceptions, не се използват за error handling
Panic
Примери
panic!("something terrible happened")
assert_eq!(1, 2)
None.unwrap()
Panic
Макроси
Panic
Макроси
panic!("съобщение")
Panic
Макроси
panic!("съобщение")
todo!()
,unimplemented!()
Panic
Макроси
panic!("съобщение")
todo!()
,unimplemented!()
unreachable!()
Panic
Макроси
panic!("съобщение")
todo!()
,unimplemented!()
unreachable!()
assert_*
,debug_assert_*
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите - те проверяват дали условие е изпълнено, и ако не вдигат паника
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите - те проверяват дали условие е изпълнено, и ако не вдигат паника
assert!
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите - те проверяват дали условие е изпълнено, и ако не вдигат паника
assert!
assert_eq!
Тестове
Asserts
- в тестове е удобно да използваме
assert_*
макросите - те проверяват дали условие е изпълнено, и ако не вдигат паника
assert!
assert_eq!
assert_ne!
Тестове
Asserts
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!
показват и какви са стойностите, които сравняваме
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]
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
Организация на тестовете
Практика е тестовете да стоят в отделен модул
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
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
- по принцип: тестове, които покриват отделна малка компонента от кода
- в rust: тестове, които покриват функционалността на един модул
Организация на тестовете
Unit tests
- по принцип: тестове, които покриват отделна малка компонента от кода
- в rust: тестове, които покриват функционалността на един модул
- поставят се в подмодул на модула, който тестват
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
- по принцип: тестове, които покриват функционалността на програмата или библиотеката като цяло
- в rust: тестове, които се намират отделно от сорс кода, в папка
tests
Организация на тестовете
Integration tests
- по принцип: тестове, които покриват функционалността на програмата или библиотеката като цяло
- в rust: тестове, които се намират отделно от сорс кода, в папка
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/