Created
April 16, 2026 04:54
-
-
Save rdnvndr/ec86d649b951ce739e8f6d49feff7897 to your computer and use it in GitHub Desktop.
RUST MEMENTO
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ================================================================================ | |
| RUST MEMENTO | |
| ================================================================================ | |
| 1. ПЕРЕМЕННЫЕ, ТИПЫ, ВЫВОД | |
| ================================================================================ | |
| let x = 5; // неизменяемая (по умолчанию) | |
| let mut y = 10; // изменяемая | |
| let z: i32 = 42; // с явным типом | |
| let _ignored = 99; // подчёркивание — не использовать, но не warning | |
| // Теневые переменные (shadowing) | |
| let w = 1; | |
| let w = w + 5; // новая переменная w, старая недоступна | |
| let w = String::from("теперь строка"); // тип тоже может измениться | |
| // Константы (всегда неизменяемы, тип обязателен, можно вычислять) | |
| const SECONDS_IN_HOUR: u32 = 3600; | |
| const ARRAY_SIZE: usize = 10; | |
| // Статические переменные (глобальные) | |
| static GLOBAL: i32 = 42; | |
| static mut MUT_GLOBAL: i32 = 100; // unsafe для чтения/записи | |
| // Вывод в консоль (макросы) | |
| print!("без перевода строки"); | |
| println!("с переводом строки"); | |
| println!("{} + {} = {}", 2, 3, 5); | |
| println!("{1} + {0} = {2}", 2, 3, 5); // по индексам | |
| println!("{a} + {b} = {c}", a=2, b=3, c=5); | |
| println!("{:?}", (1, "hello", true)); // Debug (для отладки) | |
| println!("{:#?}", large_struct); // красивое форматирование | |
| println!("{:b} {:x} {:o}", 255, 255, 255); // двоичн, шестн, восьм | |
| // Макрос для отладки (выводит файл, строку, значение) | |
| dbg!(&x); | |
| dbg!(y * 2); | |
| 2. ЦЕЛОЧИСЛЕННЫЕ ТИПЫ (размеры и диапазоны) | |
| ================================================================================ | |
| // Знаковые (signed) | |
| i8: -128 .. 127 | |
| i16: -32768 .. 32767 | |
| i32: -2_147_483_648 .. 2_147_483_647 | |
| i64: -9_223_372_036_854_775_808 .. 9_223_372_036_854_775_807 | |
| i128: огромный | |
| isize: зависит от архитектуры (32/64 бита) — для индексов в коллекциях | |
| // Беззнаковые (unsigned) | |
| u8: 0 .. 255 | |
| u16: 0 .. 65535 | |
| u32: 0 .. 4_294_967_295 | |
| u64: 0 .. 18_446_744_073_709_551_615 | |
| u128: огромный | |
| usize: зависит от архитектуры — для размеров и индексов | |
| // Литералы | |
| let a = 0xFF; // шестнадцатеричные | |
| let b = 0o77; // восьмеричные | |
| let c = 0b1111; // двоичные | |
| let d = 1_000_000; // визуальный разделитель | |
| let e = b'A'; // байтовый литерал (u8) | |
| // Методы для чисел | |
| let x = -42; | |
| println!("{}", x.abs()); | |
| let y = 5u8; | |
| println!("{}", y.checked_add(250)); // Some(255) или None | |
| println!("{}", y.saturating_add(250)); // 255 (насыщение) | |
| println!("{}", 10_i32.pow(3)); // 1000 | |
| 3. ВЕЩЕСТВЕННЫЕ ТИПЫ | |
| ================================================================================ | |
| f32: 32-битное, одинарная точность | |
| f64: 64-битное, двойная точность (по умолчанию) | |
| let pi: f64 = 3.1415926535; | |
| let exp = 2.5e-3; // 0.0025 | |
| let inf = f64::INFINITY; | |
| let nan = f64::NAN; | |
| // Проверки | |
| println!("{}", pi.is_nan()); | |
| println!("{}", pi.is_infinite()); | |
| println!("{}", pi.is_finite()); | |
| // Методы | |
| let a = 3.0; | |
| let b = 4.0; | |
| println!("{}", a.sqrt()); | |
| println!("{}", a.powi(2)); // целая степень | |
| println!("{}", a.powf(0.5)); // вещественная степень | |
| println!("{}", a.hypot(b)); // sqrt(a² + b²) | |
| 4. ЛОГИЧЕСКИЙ ТИП И СИМВОЛЫ | |
| ================================================================================ | |
| let t = true; | |
| let f: bool = false; | |
| // Символы (Unicode, 4 байта) | |
| let c = 'я'; | |
| let omega = 'Ω'; | |
| let smile = '😊'; | |
| let code = '\u{1F600}'; // Unicode escape | |
| // Методы символов | |
| println!("{}", c.is_alphabetic()); | |
| println!("{}", c.is_numeric()); | |
| println!("{}", c.to_ascii_uppercase()); | |
| 5. КОРТЕЖИ (TUPLES) | |
| ================================================================================ | |
| let tup: (i32, f64, char) = (42, 3.14, 'R'); | |
| let (a, b, c) = tup; // деструктуризация | |
| let first = tup.0; // доступ по индексу | |
| let second = tup.1; | |
| // Кортеж из одного элемента (нужна запятая) | |
| let singleton = (5,); | |
| // Пустой кортеж (unit) | |
| let unit = (); // тип () — используется как "ничего", например в Result<(), Error> | |
| 6. МАССИВЫ (ARRAYS) — фиксированная длина | |
| ================================================================================ | |
| let arr1 = [1, 2, 3, 4, 5]; | |
| let arr2: [i32; 5] = [1, 2, 3, 4, 5]; | |
| let zeros = [0; 100]; // 100 нулей | |
| let first = arr1[0]; // доступ (паника при выходе за границы) | |
| let maybe = arr1.get(10); // Option<&i32> — безопасный доступ | |
| // Итерация | |
| for elem in &arr1 { | |
| println!("{}", elem); | |
| } | |
| for elem in arr1.iter() { ... } | |
| for (i, elem) in arr1.iter().enumerate() { ... } | |
| 7. СРЕЗЫ (SLICES) — ссылка на часть коллекции | |
| ================================================================================ | |
| let arr = [10, 20, 30, 40, 50]; | |
| let slice1: &[i32] = &arr[1..4]; // [20, 30, 40] | |
| let slice2 = &arr[..3]; // [10, 20, 30] | |
| let slice3 = &arr[2..]; // [30, 40, 50] | |
| let slice4 = &arr[..]; // весь массив как срез | |
| // Срезы строк (самый частый случай) | |
| let s = String::from("hello world"); | |
| let hello = &s[0..5]; // &str | |
| let world = &s[6..11]; | |
| // ВНИМАНИЕ: срезы по байтам, не по символам! | |
| // &s[0..1] на "привет" — паника, т.к. символы UTF-8 многобайтные | |
| 8. СТРОКИ (ДВЕ СУЩНОСТИ) | |
| ================================================================================ | |
| // 1. &str — строковый срез (неизменяемый, часть String или литерал) | |
| let literal: &str = "Hello"; | |
| let part: &str = &some_string[0..5]; | |
| // 2. String — изменяемая строка в куче | |
| let mut s = String::new(); | |
| let s2 = String::from("hello"); | |
| let s3 = "world".to_string(); | |
| let s4 = "rust".to_owned(); | |
| // Методы String | |
| let mut s = String::from("hello"); | |
| s.push_str(" world"); // добавление &str | |
| s.push('!'); // добавление символа | |
| s += "!!!"; // оператор (требует &str) | |
| s.insert(5, ','); // вставка символа на позицию | |
| s.remove(5); // удаление символа | |
| s.pop(); // удаление последнего символа | |
| let len = s.len(); // длина в байтах | |
| let is_empty = s.is_empty(); | |
| // Конкатенация (format! — без перемещения владения) | |
| let a = "Hello"; | |
| let b = "World"; | |
| let c = format!("{} {}!", a, b); // a и b остаются доступны | |
| // Преобразования | |
| let num = 42; | |
| let num_str = num.to_string(); | |
| let parsed: i32 = "42".parse().unwrap(); | |
| let parsed2 = "42".parse::<i32>().unwrap(); | |
| // Итерация по символам (а не байтам!) | |
| for ch in "привет".chars() { | |
| println!("{}", ch); | |
| } | |
| for byte in "hello".bytes() { | |
| println!("{}", byte); | |
| } | |
| 9. ВЛАДЕНИЕ (OWNERSHIP) — подробно | |
| ================================================================================ | |
| ПРАВИЛА: | |
| 1. Каждое значение имеет одну переменную — владельца. | |
| 2. Владелец может быть только один. | |
| 3. Когда владелец выходит из области видимости, значение удаляется (drop). | |
| // Перемещение (move) | |
| let s1 = String::from("hello"); | |
| let s2 = s1; // s1 перемещён в s2, s1 больше не действителен | |
| // println!("{}", s1); // ОШИБКА! | |
| // Копирование (copy) — только для типов, реализующих Copy (стековые) | |
| let x = 5; | |
| let y = x; // x копируется, оба действительны | |
| // Копируемые типы: все целые, bool, char, f32/f64, кортежи из копируемых | |
| // Клонирование (clone) — явное глубокое копирование | |
| let s1 = String::from("hello"); | |
| let s2 = s1.clone(); // оба действительны, но дорого | |
| // Функции и владение | |
| fn take(s: String) { ... } // забирает владение, s уничтожится после вызова | |
| fn make() -> String { ... } // возвращает владение | |
| let s = String::from("hello"); | |
| take(s); // s больше не действителен | |
| // println!("{}", s); // ОШИБКА! | |
| 10. ЗАИМСТВОВАНИЕ (BORROWING) | |
| ================================================================================ | |
| // Неизменяемое заимствование (&T) — можно сколько угодно | |
| fn calc_len(s: &String) -> usize { // берёт &String, не забирает владение | |
| s.len() | |
| } | |
| let s = String::from("hello"); | |
| let len = calc_len(&s); // передаём ссылку | |
| println!("{} {}", s, len); // s всё ещё действителен | |
| // Изменяемое заимствование (&mut T) — можно только одно | |
| fn modify(s: &mut String) { | |
| s.push_str(" world"); | |
| } | |
| let mut s = String::from("hello"); | |
| modify(&mut s); // передаём изменяемую ссылку | |
| // ПРАВИЛА БОРРОУИНГА: | |
| // - Нельзя иметь изменяемую ссылку, если есть неизменяемые | |
| // - Нельзя иметь изменяемую ссылку и использовать оригинал | |
| let mut s = String::from("hello"); | |
| let r1 = &s; // OK | |
| let r2 = &s; // OK | |
| // let r3 = &mut s; // ОШИБКА! нельзя, т.к. есть неизменяемые | |
| println!("{} {}", r1, r2); | |
| let mut s = String::from("hello"); | |
| let r1 = &mut s; // OK | |
| // let r2 = &s; // ОШИБКА! нельзя неизменяемую, когда есть изменяемая | |
| // println!("{}", s); // ОШИБКА! нельзя использовать оригинал | |
| // Ссылки не могут жить дольше, чем данные, на которые указывают | |
| 11. ВРЕМЯ ЖИЗНИ (LIFETIMES) | |
| ================================================================================ | |
| // АННОТАЦИИ: 'a, 'b, 'static и т.д. | |
| fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { | |
| if x.len() > y.len() { x } else { y } | |
| } | |
| // Структуры со ссылками | |
| struct Excerpt<'a> { | |
| part: &'a str, | |
| } | |
| let novel = String::from("Call me Ishmael..."); | |
| let first_sentence = novel.split('.').next().expect("no ."); | |
| let excerpt = Excerpt { part: first_sentence }; | |
| // 'static — живёт всю программу (строковые литералы) | |
| let s: &'static str = "I live forever"; | |
| // Элизия (elision) — правила, когда аннотации можно опускать | |
| fn first_word(s: &str) -> &str { // компилятор сам добавит 'a | |
| &s[..1] | |
| } | |
| 12. ВЕКТОРЫ (VEC<T>) | |
| ================================================================================ | |
| let mut v: Vec<i32> = Vec::new(); | |
| let v2 = vec![1, 2, 3]; // макрос vec! | |
| // Добавление и удаление | |
| v.push(5); | |
| v.push(6); | |
| v.pop(); // Option<T> | |
| v.insert(1, 10); // на позицию 1 вставить 10 | |
| v.remove(0); // удалить индекс 0 | |
| // Доступ | |
| let third = &v[2]; // паника при выходе | |
| let third_opt = v.get(2); // Option<&i32> | |
| let mut_third = &mut v[2]; | |
| // Итерация | |
| for i in &v { println!("{}", i); } | |
| for i in &mut v { *i += 10; } // изменение через разыменование | |
| for i in v.iter().enumerate() { ... } | |
| // Ёмкость (capacity) и размер (len) | |
| let mut v = Vec::with_capacity(100); // заранее выделить память | |
| v.push(1); | |
| println!("len: {}, cap: {}", v.len(), v.capacity()); | |
| v.shrink_to_fit(); // уменьшить capacity до len | |
| // Преобразование из итератора | |
| let v: Vec<i32> = (1..10).collect(); | |
| 13. ХЭШ-ТАБЛИЦЫ (HASHMAP) | |
| ================================================================================ | |
| use std::collections::HashMap; | |
| let mut scores = HashMap::new(); | |
| scores.insert(String::from("Blue"), 10); | |
| scores.insert(String::from("Yellow"), 50); | |
| // entry + or_insert (идиоматично) | |
| scores.entry(String::from("Blue")).or_insert(25); // не изменит, т.к. есть | |
| scores.entry(String::from("Red")).or_insert(30); // вставит | |
| // Обновление значения | |
| let text = "hello world wonderful world"; | |
| let mut map = HashMap::new(); | |
| for word in text.split_whitespace() { | |
| let count = map.entry(word).or_insert(0); | |
| *count += 1; | |
| } | |
| // Итерация | |
| for (key, value) in &scores { | |
| println!("{}: {}", key, value); | |
| } | |
| // Владение | |
| let key = String::from("Blue"); | |
| scores.insert(key, 10); // key перемещён в map, использовать нельзя | |
| // Альтернативы: BTreeMap (отсортированный) | |
| 14. ПЕРЕЧИСЛЕНИЯ (ENUM) — мощь Rust | |
| ================================================================================ | |
| enum IpAddr { | |
| V4(u8, u8, u8, u8), | |
| V6(String), | |
| } | |
| let home = IpAddr::V4(127, 0, 0, 1); | |
| let loopback = IpAddr::V6(String::from("::1")); | |
| impl IpAddr { | |
| fn call(&self) { ... } | |
| } | |
| // Option<T> — встроенный (не нужно Null) | |
| enum Option<T> { | |
| Some(T), | |
| None, | |
| } | |
| let some_num = Some(5); | |
| let some_str = Some("text"); | |
| let absent: Option<i32> = None; | |
| // Result<T, E> — обработка ошибок | |
| enum Result<T, E> { | |
| Ok(T), | |
| Err(E), | |
| } | |
| let f = File::open("hello.txt"); // Result<File, std::io::Error> | |
| 15. MATCH — исчерпывающее сопоставление | |
| ================================================================================ | |
| let value = 3; | |
| match value { | |
| 1 => println!("один"), | |
| 2 | 3 | 5 | 7 => println!("простое число"), | |
| 4..=10 => println!("от 4 до 10"), | |
| _ => println!("что-то ещё"), | |
| } | |
| // match с Option | |
| fn plus_one(x: Option<i32>) -> Option<i32> { | |
| match x { | |
| None => None, | |
| Some(i) => Some(i + 1), | |
| } | |
| } | |
| // match с Result | |
| match File::open("file.txt") { | |
| Ok(file) => println!("открыто: {:?}", file), | |
| Err(e) => println!("ошибка: {}", e), | |
| } | |
| // if let — когда нужна только одна ветка | |
| let config_max = Some(3u8); | |
| if let Some(max) = config_max { | |
| println!("максимум {}", max); | |
| } | |
| // с else | |
| if let Some(max) = config_max { | |
| println!("{}", max); | |
| } else { | |
| println!("нет значения"); | |
| } | |
| // while let | |
| let mut stack = Vec::new(); | |
| stack.push(1); | |
| stack.push(2); | |
| while let Some(top) = stack.pop() { | |
| println!("{}", top); | |
| } | |
| 16. СТРУКТУРЫ (STRUCT) — методы и ассоциированные функции | |
| ================================================================================ | |
| struct Rectangle { | |
| width: u32, | |
| height: u32, | |
| } | |
| // Методы (первый параметр self, &self, &mut self) | |
| impl Rectangle { | |
| fn area(&self) -> u32 { | |
| self.width * self.height | |
| } | |
| fn can_hold(&self, other: &Rectangle) -> bool { | |
| self.width > other.width && self.height > other.height | |
| } | |
| fn set_width(&mut self, w: u32) { | |
| self.width = w; | |
| } | |
| // Ассоциированная функция (конструктор, без self) | |
| fn square(size: u32) -> Self { | |
| Self { width: size, height: size } | |
| } | |
| } | |
| let rect = Rectangle { width: 30, height: 50 }; | |
| println!("{}", rect.area()); | |
| let square = Rectangle::square(20); | |
| // Кортежные структуры | |
| struct Color(u8, u8, u8); | |
| let black = Color(0, 0, 0); | |
| println!("{}", black.0); | |
| // Unit-структура (для реализации trait, без данных) | |
| struct Marker; | |
| 17. ОБОБЩЕНИЯ (GENERICS) | |
| ================================================================================ | |
| struct Point<T> { | |
| x: T, | |
| y: T, | |
| } | |
| let int_point = Point { x: 5, y: 10 }; | |
| let float_point = Point { x: 1.0, y: 4.0 }; | |
| struct Mixed<T, U> { | |
| x: T, | |
| y: U, | |
| } | |
| impl<T> Point<T> { | |
| fn x(&self) -> &T { &self.x } | |
| } | |
| impl Point<f64> { // только для f64 | |
| fn distance_from_origin(&self) -> f64 { | |
| (self.x.powi(2) + self.y.powi(2)).sqrt() | |
| } | |
| } | |
| // В функциях | |
| fn largest<T: PartialOrd>(list: &[T]) -> &T { | |
| let mut largest = &list[0]; | |
| for item in list { | |
| if item > largest { largest = item; } | |
| } | |
| largest | |
| } | |
| 18. TRAIT'Ы — интерфейсы | |
| ================================================================================ | |
| trait Summary { | |
| fn summarize(&self) -> String; | |
| fn default_impl(&self) -> String { // реализация по умолчанию | |
| String::from("(default)") | |
| } | |
| } | |
| struct NewsArticle { | |
| headline: String, | |
| location: String, | |
| } | |
| impl Summary for NewsArticle { | |
| fn summarize(&self) -> String { | |
| format!("{}, by {}", self.headline, self.location) | |
| } | |
| } | |
| // Trait как параметр (синтаксис) | |
| fn notify(item: &impl Summary) { ... } | |
| fn notify2<T: Summary>(item: &T) { ... } // то же самое | |
| fn notify3<T: Summary + Display>(item: &T) { ... } // несколько | |
| fn notify4<T>(item: &T) where T: Summary + Display { ... } // где-клауза | |
| // Возврат типа, реализующего trait | |
| fn returns_summarizable() -> impl Summary { | |
| NewsArticle { ... } | |
| } | |
| // Ограничение: можно вернуть только один конкретный тип | |
| // Реализация внешнего trait для внешнего типа (сиротское правило) | |
| // Нужно хотя бы одно из них локальное | |
| 19. ОБРАБОТКА ОШИБОК (паника и Result) | |
| ================================================================================ | |
| // Паника (неустранимая) | |
| panic!("crash and burn"); | |
| let v = vec![1, 2, 3]; | |
| v[99]; // паника | |
| // Result (устранимая) | |
| use std::fs::File; | |
| use std::io::ErrorKind; | |
| let f = File::open("hello.txt"); | |
| let f = match f { | |
| Ok(file) => file, | |
| Err(error) => match error.kind() { | |
| ErrorKind::NotFound => match File::create("hello.txt") { | |
| Ok(fc) => fc, | |
| Err(e) => panic!("не могу создать: {:?}", e), | |
| }, | |
| other_error => panic!("другая ошибка: {:?}", other_error), | |
| }, | |
| }; | |
| // unwrap и expect (паника при Err) | |
| let f = File::open("hello.txt").unwrap(); | |
| let f = File::open("hello.txt").expect("не удалось открыть hello.txt"); | |
| // Оператор ? — распространение ошибки | |
| use std::io; | |
| use std::io::Read; | |
| fn read_username() -> Result<String, io::Error> { | |
| let mut f = File::open("username.txt")?; // если Err, вернёт его из функции | |
| let mut s = String::new(); | |
| f.read_to_string(&mut s)?; | |
| Ok(s) | |
| } | |
| // Короткая версия | |
| fn read_username2() -> Result<String, io::Error> { | |
| let mut s = String::new(); | |
| File::open("username.txt")?.read_to_string(&mut s)?; | |
| Ok(s) | |
| } | |
| // Ещё короче (стандартная библиотека) | |
| fn read_username3() -> Result<String, io::Error> { | |
| fs::read_to_string("username.txt") | |
| } | |
| // ? работает с Option тоже | |
| fn last_char_of_first_line(text: &str) -> Option<char> { | |
| text.lines().next()?.chars().last() | |
| } | |
| 20. МАКРОСЫ (МАКРОСЫ ПОТОКОВ ТОКЕНОВ) | |
| ================================================================================ | |
| // Макрос без аргументов | |
| macro_rules! hello { | |
| () => { | |
| println!("Hello!"); | |
| }; | |
| } | |
| // С аргументами | |
| macro_rules! say { | |
| ($name:expr) => { | |
| println!("Hello, {}!", $name); | |
| }; | |
| (hello) => { | |
| println!("Hello special!"); | |
| }; | |
| } | |
| // Повторения | |
| macro_rules! vec_of_strings { | |
| ($($x:expr),*) => { | |
| vec![$($x.to_string()),*] | |
| }; | |
| } | |
| let v = vec_of_strings!("a", "b", "c"); | |
| // Макрос с разными паттернами | |
| macro_rules! calculate { | |
| (eval $e:expr) => {{ | |
| let val: usize = $e; | |
| println!("{} = {}", stringify!($e), val); | |
| }}; | |
| } | |
| calculate!(eval 2 + 3 * 5); | |
| 21. ЗАКРЫТИЯ (CLOSURES) | |
| ================================================================================ | |
| let add_one = |x: i32| -> i32 { x + 1 }; | |
| let add_one_short = |x| x + 1; | |
| let add = |a, b| a + b; | |
| // Захват окружения | |
| let x = 4; | |
| let equal_to_x = |z| z == x; // захват x (не владея, если только move) | |
| let y = 4; | |
| assert!(equal_to_x(y)); | |
| // move — передача владения в замыкание | |
| let x = vec![1, 2, 3]; | |
| let equal_to_x = move |z| z == x; | |
| // println!("{:?}", x); // ОШИБКА! x перемещён в замыкание | |
| // Применение: итераторы | |
| let v = vec![1, 2, 3]; | |
| let doubled: Vec<_> = v.iter().map(|x| x * 2).collect(); | |
| 22. ИТЕРАТОРЫ | |
| ================================================================================ | |
| let v = vec![1, 2, 3, 4, 5]; | |
| // Ленивые итераторы | |
| let iter = v.iter(); // &T | |
| let iter_mut = v.iter_mut(); // &mut T | |
| let into_iter = v.into_iter(); // T (потребляет коллекцию) | |
| // Адаптеры | |
| let doubled: Vec<_> = v.iter().map(|x| x * 2).collect(); | |
| let evens: Vec<_> = v.iter().filter(|x| *x % 2 == 0).collect(); | |
| let first_even = v.iter().find(|x| *x % 2 == 0); | |
| let any_greater = v.iter().any(|x| *x > 10); | |
| let all_positive = v.iter().all(|x| *x > 0); | |
| let sum: i32 = v.iter().sum(); | |
| let product: i32 = v.iter().product(); | |
| // fold (аккумулятор) | |
| let sum = v.iter().fold(0, |acc, x| acc + x); | |
| // zip | |
| let v1 = vec![1, 2]; | |
| let v2 = vec![3, 4]; | |
| let zipped: Vec<_> = v1.iter().zip(v2.iter()).map(|(a, b)| a + b).collect(); | |
| // chain | |
| let concatenated: Vec<_> = v1.iter().chain(v2.iter()).collect(); | |
| 23. УМНЫЕ УКАЗАТЕЛИ | |
| ================================================================================ | |
| // Box<T> — указатель на кучу | |
| let b = Box::new(5); | |
| // Рекурсивные типы | |
| enum List { | |
| Cons(i32, Box<List>), | |
| Nil, | |
| } | |
| use List::{Cons, Nil}; | |
| let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); | |
| // Rc<T> — подсчёт ссылок (один поток) | |
| use std::rc::Rc; | |
| let a = Rc::new(List::Cons(5, Rc::new(List::Nil))); | |
| let b = List::Cons(3, Rc::clone(&a)); | |
| let c = List::Cons(4, Rc::clone(&a)); | |
| println!("count = {}", Rc::strong_count(&a)); | |
| // RefCell<T> — внутренняя изменяемость (проверки в runtime) | |
| use std::cell::RefCell; | |
| let x = RefCell::new(5); | |
| *x.borrow_mut() = 6; // изменяем через неизменяемую переменную | |
| let y = x.borrow(); // неизменяемое заимствование | |
| // Комбинация Rc<RefCell<T>> — множественное владение с изменяемостью | |
| let shared = Rc::new(RefCell::new(5)); | |
| let a = Rc::clone(&shared); | |
| let b = Rc::clone(&shared); | |
| *a.borrow_mut() = 10; | |
| // Arc<T> — атомарный Rc (для потоков) | |
| use std::sync::Arc; | |
| let shared = Arc::new(42); | |
| // Mutex<T> — взаимное исключение | |
| use std::sync::Mutex; | |
| let m = Mutex::new(5); | |
| { | |
| let mut num = m.lock().unwrap(); | |
| *num = 6; | |
| } | |
| 24. ПОТОКИ (THREADS) | |
| ================================================================================ | |
| use std::thread; | |
| use std::time::Duration; | |
| // Запуск потока | |
| let handle = thread::spawn(|| { | |
| for i in 1..10 { | |
| println!("поток: {}", i); | |
| thread::sleep(Duration::from_millis(1)); | |
| } | |
| }); | |
| for i in 1..5 { | |
| println!("главный: {}", i); | |
| thread::sleep(Duration::from_millis(1)); | |
| } | |
| handle.join().unwrap(); // дождаться завершения | |
| // move для захвата владения | |
| let v = vec![1, 2, 3]; | |
| let handle = thread::spawn(move || { | |
| println!("{:?}", v); | |
| }); | |
| handle.join().unwrap(); | |
| // Передача данных между потоками (каналы mpsc) | |
| use std::sync::mpsc; | |
| let (tx, rx) = mpsc::channel(); | |
| thread::spawn(move || { | |
| tx.send(42).unwrap(); | |
| }); | |
| let received = rx.recv().unwrap(); // блокируется | |
| println!("получено: {}", received); | |
| // Несколько продюсеров | |
| let (tx, rx) = mpsc::channel(); | |
| let tx1 = tx.clone(); | |
| thread::spawn(move || { tx1.send(1).unwrap(); }); | |
| thread::spawn(move || { tx.send(2).unwrap(); }); | |
| for received in rx { println!("{}", received); } | |
| 25. МОДУЛИ И ПРОСТРАНСТВА ИМЁН | |
| ================================================================================ | |
| // Файл src/lib.rs или src/main.rs | |
| mod front_of_house { | |
| pub mod hosting { | |
| pub fn add_to_waitlist() {} | |
| fn seat_at_table() {} | |
| } | |
| mod serving { | |
| fn take_order() {} | |
| } | |
| } | |
| use crate::front_of_house::hosting; | |
| // или | |
| use front_of_house::hosting::add_to_waitlist; | |
| pub fn eat_at_restaurant() { | |
| hosting::add_to_waitlist(); | |
| } | |
| // re-export | |
| pub use self::front_of_house::hosting; | |
| // вложенные пути | |
| use std::{cmp::Ordering, io}; | |
| // алиасы | |
| use std::fmt::Result as FmtResult; | |
| // прелюдия (std::prelude) — автоматически импортируется | |
| // Структура проекта | |
| /* | |
| my_project/ | |
| Cargo.toml | |
| src/ | |
| main.rs (точка входа для бинарного крейта) | |
| lib.rs (точка входа для библиотечного крейта) | |
| front_of_house.rs | |
| back_of_house.rs | |
| */ | |
| 26. ТЕСТИРОВАНИЕ | |
| ================================================================================ | |
| #[cfg(test)] | |
| mod tests { | |
| #[test] | |
| fn it_works() { | |
| assert_eq!(2 + 2, 4); | |
| } | |
| #[test] | |
| fn failing() { | |
| panic!("этот тест упадёт"); | |
| } | |
| #[test] | |
| fn with_result() -> Result<(), String> { | |
| if 2 + 2 == 4 { Ok(()) } else { Err(String::from("не 4")) } | |
| } | |
| #[test] | |
| #[should_panic(expected = "разделение на ноль")] | |
| fn divide_by_zero() { | |
| panic!("разделение на ноль"); // пройдёт | |
| } | |
| #[test] | |
| #[ignore] | |
| fn expensive_test() { ... } | |
| } | |
| // Интеграционные тесты (в tests/ папке) | |
| // tests/integration_test.rs | |
| use my_crate; | |
| #[test] | |
| fn it_adds() { ... } | |
| // Документационные тесты (внутри /// комментариев) | |
| /// Добавляет два числа. | |
| /// ``` | |
| /// assert_eq!(my_crate::add(2, 2), 4); | |
| /// ``` | |
| pub fn add(a: i32, b: i32) -> i32 { a + b } | |
| 27. КОМАНДЫ CARGO (ПОВСЕДНЕВНЫЕ) | |
| ================================================================================ | |
| cargo new app # новый бинарный проект | |
| cargo new --lib lib # новый библиотечный проект | |
| cargo build # отладочная сборка (target/debug/) | |
| cargo build --release # релизная сборка (target/release/) | |
| cargo run # сборка + запуск | |
| cargo check # быстрая проверка без генерации кода | |
| cargo test # запуск всех тестов | |
| cargo test -- --nocapture # с выводом println! в тестах | |
| cargo doc --open # документация проекта | |
| cargo update # обновление зависимостей | |
| cargo add serde # добавить зависимость (требуется cargo-edit) | |
| cargo fmt # форматирование кода (rustfmt) | |
| cargo clippy # линтер (много полезных советов) | |
| 28. ПОЛЕЗНЫЕ КОМБИНАЦИИ И ПАТТЕРНЫ | |
| ================================================================================ | |
| // Игнорирование значений в match | |
| let x = Some(5); | |
| match x { | |
| Some(_) => println!("есть значение"), | |
| None => (), | |
| } | |
| // .. для пропуска остальных полей | |
| struct Point { x: i32, y: i32, z: i32 } | |
| let p = Point { x: 1, y: 2, z: 3 }; | |
| match p { | |
| Point { x, .. } => println!("x = {}", x), | |
| } | |
| // Рефутинг (ref и ref mut) | |
| let x = Some(5); | |
| match x { | |
| Some(ref r) => println!("ссылка на {}", r), // r: &i32 | |
| None => (), | |
| } | |
| // Guard в match | |
| match x { | |
| Some(v) if v < 5 => println!("меньше 5"), | |
| Some(v) => println!("{}", v), | |
| None => (), | |
| } | |
| // @-привязка | |
| match x { | |
| Some(v @ 1..=5) => println!("от 1 до 5: {}", v), | |
| _ => (), | |
| } | |
| ================================================================================ | |
| ССЫЛКИ И РЕСУРСЫ | |
| ================================================================================ | |
| - Официальная книга: https://doc.rust-lang.org/book/ | |
| - Rust by Example: https://doc.rust-lang.org/rust-by-example/ | |
| - Std docs: https://doc.rust-lang.org/std/ | |
| - Crates.io: https://crates.io/ | |
| - Rust Playground: https://play.rust-lang.org/ | |
| ================================================================================ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment