Пример 1. Переменные, печать в консольку ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------ fn main() { // главная функция, точка входа let x : u32 = 1; // переменная x типа u32 с инициализацией println!("x = {}", x); // ! - значит это макрос, печатаем значение x let y : u32 = x; // в переменную y кладём x println!("y = {}", y); // печатаем y println!("x = {}", x); // снова печатаем x } ------------------------------------------------------------------------------------- Программа успешно компилируется, запускаем: ~/rust $ ./e001 x = 1 y = 1 x = 1 Всё как ожидалось, никаких фокусов. Пример 2. Структуры, данные в стеке, владение ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Делаем абсолютно то же самое, но не с целыми числами, а со структурами ------------------------------------------------------------------------------------- #[derive(Debug)] // нужно для печати struct S { // структура с двумя полями (тип) a : u32, b : u32, } fn main() { let s1 : S = S{a : 1, b : 2}; // создаём экземпляр S с именем s1 println!("s1 = {:?}", s1); // печатаем его let s2 : S = s1; // в экземпляр s2 кладём s1 println!("s2 = {:?}", s2); // печатаем s2 println!("s1 = {:?}", s1); // а теперь снова печатаем s1 } ------------------------------------------------------------------------------------- Компилируем --> e002.rs:13:24 | 9 | let s1 : S = S{a : 1, b : 2}; | -- move occurs because `s1` has type `S`, which does not implement the `Copy` trait 10 | println!("s1 = {:?}", s1); 11 | let s2 : S = s1; | -- value moved here 12 | println!("s2 = {:?}", s2); 13 | println!("s1 = {:?}", s1); | ^^ value borrowed here after move error: aborting due to previous error Вот тебе драсьте! Сюрприз - это даже не компилируется! Что, чёрт возьми, происходит? Я не могу второй раз напечатать переменную? Что за бред и почему оно так? Примерно так охреневает любой, кто пока не знаком с концепцией "владения". Как мы видим, компилятор отметил 3 строчки, 9-ую, 11-ую и 13 Начнём с середины, то есть с 11-ой - 'value moved here'. Это означает, что значением владела переменная s1, после __присваивания__ им стала владеть переменная s2, а переменной s1 как бы не стало, она больше не обладает значением, про что и пишет компилятор - "значение было перемещено здесь". Тут есть интересный языковой момент - в русском языке слово "присвоить" вообще-то означает "взять себе", "отобрать", "сделать своим" - ну вот тут именно это и проиходит - переменная s2 "отобрала" значение у s1, отметка для строчки 13 - 'value borrowed here after move' Тут немного странно выглядит слово 'borrowed', то есть значение заимствовано; про заимствование будет дальше, а тут имела место именно передача владения... ну да ладно. А, наверное, макрос println! написан так, что он заимствует аргументы, то есть туда передаются ссылки... Теперь вопрос - но ведь с u32 такого не было, что тут-то "не так"? А на этот вопрос нам (как бы) отвечает пометка к строчке 9. ' move occurs because `s1` has type `S`, which does not implement the `Copy` trait ' Как бы отвечает, потому что нихрена не понятно, о чём это вообще... Передача владения произошла потому, что тип S не реализует это вот самое Copy trait... ннндас... :) Ну, вобщем, trait - это "типаж". Название, имхо, не самое удачное - это немного похоже на "виртуальный метод" из класс-ориентированного ООП, но существенное отличие в том, что этот типаж/метод как бы сам по себе, он не определён в каком-то базовом классе (классов в смысле C++/Java/Python в Rust попросту нет) и его можно реализовать для любой своей структуры, если это надо и вообще имеет смысл. (см. также Пример 39, про два способа полиформизма) Возможно, более удачным термином будет "способность/умение", то есть "type S does not implement the `Copy` trait" переводим как "тип S не умеет делать Copy", вполне логично. Ну, а с u32 всё хорошо просто потому, что этот типаж/метод/трейт/способность/умение для него реализован в стандартной библиотеке В некоторых ЯП (Go например, в котором тоже нет "классов", к слову) такой (ну или отчасти похожий) механизм называется "интерфейсом". Пример 3. Фокус-покус, выведение типажей автоматом ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Добавим в предыдущий пример немножко магии ------------------------------------------------------------------------------------- #[derive(Debug,Copy,Clone)] // <<< магия тут struct S { a : u32, b : u32, } fn main() { let s1 : S = S{a : 1, b : 2}; println!("s1 = {:?}", s1); let s2 : S = s1; println!("s2 = {:?}", s2); println!("s1 = {:?}", s1); } ------------------------------------------------------------------------------------- Теперь типаж Copy у нас "выводится" автоматически, и, о чудо, оно компилируется и работает: @dexp ~/rust $ ./e003 s1 = S { a: 1, b: 2 } s2 = S { a: 1, b: 2 } s1 = S { a: 1, b: 2 } Всё, как и было бы в ЯП без концпеции "владения". Замечание - типаж Copy реализуют не все стандартные/библиотечные типы просто потому, что это полное копирование данных, то есть зачастую бесполезная трата времени. В нашем случае был вообще наш собственный тип, Copy мы сами не делали, так что его нет, но, как видим, его можно derive. Пример 4. Передача владения в функцию ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ https://habr.com/ru/post/278779/ "Опыт большинства программистов не подготовит к тому, что объект point вдруг становится недоступным после вызова функции" ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn print_S(s : S) { println!("s1 = {:?}", s); } fn main() { let s1 : S = S{a : 1, b : 2}; print_S(s1); println!("s1 = {:?}", s1); } ------------------------------------------------------------------------------------- Компилятор скажет на это практически то же самое error[E0382]: borrow of moved value: `s1` --> e004.rs:15:24 | 13 | let s1 : S = S{a : 1, b : 2}; | -- move occurs because `s1` has type `S`, which does not implement the `Copy` trait 14 | print_S(s1); | -- value moved here 15 | println!("s1 = {:?}", s1); | ^^ value borrowed here after move error: aborting due to previous error Отличие лишь в том, что передача владения произошла не в момент присваивания одной переменной в другую, а при передаче аргумента функции - после этого владельцем становится... ну, как бы формальный параметр функции. Важное замечание - владелец ОБЯЗАН уничтожить переменную. Грубо говоря, это происходит по достижении конца блока, то есть закрывающей фигурной скобочки. И тут нужно сказать, что вместо сборки мусора Rust использует RAII и решения о деаллокации принимаются на стадии _компиляции_, а не во время исполнения, так что все эти дела программу никак не тормозят, в отличие от GC. В данном случае просто уничтожается стековый кадр вызываемой функции, в который был... черт(!!!), скопирован(!!!) экземпляр структуры (но владение всё-таки было перемещено, у нас нет реализации типажа Copy в данном случае) см. также https://doc.rust-lang.org/stable/rust-by-example/scope/move.html и https://tourofrust.com/46_ru.html " Во время передачи (move) память владельца из стека копирует в стек параметров вызываемой функции. " блин, это путает!!!! у типа S нет трейта Copy, но, сука, "копирует"!!! а если мы нарисуем #[derive(Copy,Clone)], то что изменится? оно так же сделает копию в стеке, но типа разрешит потом использовать первый экземпляр?... https://stackoverflow.com/questions/36230710/how-does-rust-move-stack-variables-that-are-not-copyable "Non-Copy types are physically moved exactly like Copy types are: with a memcpy." "There is no difference between Copy and non-Copy types when it comes to moving physically" это надо так понимать, под капотом всегда для таких типов Copy, но если у нас нет #[derive(Copy,Clone)], то снаружи это move (передача владения), а если у нас есть #[derive(Copy,Clone)], то это типа честный Copy... блять... :)) Пример 5. А если я не хочу делать копию (в стеке)? Заимствование ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Вот тут мы передаём экзмпляр структуры в функцию по ссылке: ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn print_S(s : &S) { println!("s1 = {:?}", s); } fn main() { let s1 : S = S{a : 1, b : 2}; print_S(&s1); println!("s1 = {:?}", s1); } ------------------------------------------------------------------------------------- Нужно в сигнатуре функции перед обозначением типа поставить &, и такой же значочек поставить при вызове функции перед аргументом. Компилятор практически доволен, только педантично ругается на стиль: warning: function `print_S` should have a snake case name --> e005.rs:8:4 | 8 | fn print_S(s : &S) { | ^^^^^^^ help: convert the identifier to snake case: `print_s` | = note: #[warn(non_snake_case)] on by default Запускаем, видим то, что и задумали: @dexp ~/rust $ ./e005 s1 = S { a: 1, b: 2 } s1 = S { a: 1, b: 2 } Таким образом, если значение передаётся по ссылке, передачи владения не происходит, вместо этого происходит заимствование на время. Пример 6. А давайте поменяем значение переменной. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ну, она же, блин, переменная всё-таки. Ок, пишем ------------------------------------------------------------------------------------- fn main() { let x : u32 = 1; println!("s1 = {}", x); x = 2; println!("s1 = {}", x); } ------------------------------------------------------------------------------------- Компилируем и... ОППАНЬКИ... опять шаблон чуть не треснул @dexp ~/rust $ rustc e006.rs -C prefer-dynamic error[E0384]: cannot assign twice to immutable variable `x` --> e006.rs:5:2 | 3 | let x : u32 = 1; | - | | | first assignment to `x` | help: make this binding mutable: `mut x` 4 | println!("s1 = {}", x); 5 | x = 2; | ^^^^^ cannot assign twice to immutable variable Но тут понятнее, чем с владением/заимствованием. Дело в том, что если не указано явно, переменная является неизменяемой. Хочешь менять - укажи явно. Обратите внимание на строчку, где help - компилятор Rust во многих случаях (но не всегда) даёт точную подсказку, как исправить ситуацию. Пример 7. Изменяемая (мутабельная) переменная ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- fn main() { let mut x : u32 = 1; println!("s1 = {}", x); x = 2; println!("s1 = {}", x); } ------------------------------------------------------------------------------------- Ну, вот теперь получили, что хотели: @dexp ~/rust $ ./e007 s1 = 1 s1 = 2 Пример 8. Изменение значения переменной в функции (с обломом) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn change_a_in_s(s : &S) { s.a = 27; } fn main() { let s1 : S = S{a : 1, b : 2}; println!("s1 = {:?}", s1); change_a_in_s(&s1); println!("s1 = {:?}", s1); } ------------------------------------------------------------------------------------- И опять компилятор не даст нам просто так это сделать: error[E0594]: cannot assign to `s.a` which is behind a `&` reference --> e008.rs:9:2 | 8 | fn change_a_in_s(s : &S) { | -- help: consider changing this to be a mutable reference: `&mut S` 9 | s.a = 27; | ^^^^^^^^ `s` is a `&` reference, so the data it refers to cannot be written И вот надо уже давно сказать, что компилятор Rust просто прекрасен тем, что он не только указывает не недопустимые вещи, но и весьма однозначно намекает, что нужно сделать - в данном случае предлагает сделать ссылку изменяемой (мутабельной). Ну, правильнее сказать, что не сама ссылка изменяемая, а то, на что она показывает, можно поменять. Ну ок, делаем что говорят: Пример 9. Изменение значения переменной в функции (мутабельная ссылка) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn change_a_in_s(s : &mut S) { s.a = 27; } fn main() { let mut s1 : S = S{a : 1, b : 2}; println!("s1 = {:?}", s1); change_a_in_s(&mut s1); println!("s1 = {:?}", s1); } ------------------------------------------------------------------------------------- То есть чтобы изменять что-то внутри функции, нужно чтобы * сама переменная была mut * в сигнатуре функции должно быть &mut type * при вызове функции должно быть &mut var И тогда работает: dexp ~/rust $ ./e009 s1 = S { a: 1, b: 2 } s1 = S { a: 27, b: 2 } Пример 10. Чем дальше в лес... (много ссылок) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn change_a_in_s(s : &mut S) { s.a = 27; } fn print_a_in_s(s: &S) { println!("s.a = {}", s.a); } fn main() { let mut s : S = S{a : 1, b : 2}; let r = &s; change_a_in_s(&mut s); print_a_in_s(r); } ------------------------------------------------------------------------------------- На эти с виду безобидные манипуляции компилятор заругается: error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable --> e010.rs:19:16 | 18 | let r = &s; | -- immutable borrow occurs here 19 | change_a_in_s(&mut s); | ^^^^^^ mutable borrow occurs here 20 | print_a_in_s(r); | - immutable borrow later used here Из чего мы узнаём, что _одновременно_ не могут существовать как мутабельные ссылки, так и не мутабельные. В самом деле, смотрите - функция print_a_in_s() не хочет менять содержимое экземпляра, она просто его печатает, а r мы объявили аккурат перед вызовом change_a_in_s() - то есть по задумке s никак не должна меняться до вызова print_a_in_s(), а оно очевидным образом меняется, чего компилятор не допускает Вот если бы мы написали вот так, ------------------------------------------------------------------------------------- fn main() { let mut s : S = S{a : 1, b : 2}; change_a_in_s(&mut s); let r = &s; print_a_in_s(r); } ------------------------------------------------------------------------------------- то всё было бы хорошо, поскольку тут немутабельная ссылка объявлена чуть позже; другими словами, в первом случае области программы, где должны быть "живыми" обе ссылки, ПЕРЕСЕКАЮТСЯ (область, где нужно передать мутабельную ссылку в функцию, вложена в область, где нужна немутабельная ссылка r), а во втором случае эти области не пересекаются, сначала нужна мутабельная, а уже потом немутабельная. Общее правило, которым руководствуется компилятор, таково: В один момент времени на один объект могут ссылаться * либо строго ОДНА мутабельная ссылка/заимствование * либо сколько угодно немутабельных ссылок/заимствований И ещё раз надо подчеркнуть, что этот анализ делается на стадии компиляции, то есть на скорость работы программы это абсолютно никак не влияет. Только вот блин... если я пишу однопоточную программу, нахера мне все эти "прелести"?!?... Пример 11. Данные в "куче", умные указатели ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn print_s(s: &S) { println!("s = {:?}", s); } fn main() { let mut s : Box = Box::new(S{a : 1, b : 2}); print_s(&s); s.a = 4; s.b = 7; print_s(&s); } ------------------------------------------------------------------------------------- это вчистую аналог примерно такой программы на Си: ------------------------------------------------------------------------------------- #include #include #include typedef u_int32_t u32; struct S { u32 a; u32 b; }; void print_s(struct S *s) { printf("s = {.a = %u, .b = %u}\n", s->a, s->b); } int main(void) { struct S *s = calloc(1, sizeof(struct S)); print_s(s); s->a = 4; s->b = 7; print_s(s); } ------------------------------------------------------------------------------------- Тут отметим лишь, что в обеих случаях (и Rust, и C) указатель 's' лежит в/на Украине... тьфу.. стеке, а под саму структуру выделена память в куче и этот s туда, собственно, показывает. Разница (синтаксическая) в том, что в Rust обращение к полям делается одинаково (через точку), вне зависимости, что это, просто структура, ссылка на неё или же, как в примере, smart pointer Box. А вот теперь пример, который демонстрирует, как программа на Rust освобождает ресурсы, память под которые выделена в куче. Пример 12. Передача в функцию чего-то, что содержит указатель в кучу ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn print_s(s: Box) { println!("s = {:?}", s); } fn main() { let mut s : Box = Box::new(S{a : 1, b : 2}); print_s(s); let r = &mut s; r.a = 4; r.b = 7; print_s(s); } ------------------------------------------------------------------------------------- Увидим уже знакомое error[E0382]: borrow of moved value: `s` --> e012.rs:21:10 | 19 | let mut s : Box = Box::new(S{a : 1, b : 2}); | ----- move occurs because `s` has type `std::boxed::Box`, which does not implement the `Copy` trait 20 | print_s(s); | - value moved here 21 | let r = &mut s; | ^^^^^^ value borrowed here after move Но! По сравнению со случаем, когда переменные хранятся исключительно в стеке, тут есть весьма значительное отличие. Обратите внимание, что мы передаём s в print_s() не по ссылке, а это значит, что владельцем значения становится формальный параметр этой функции. Как только этот параметр перестаёт быть "живым", то есть по выходу из функции, владелец обязан похерить то, чем он владеет. Сам указатель, понятное дело, в стеке и память, занимая им, "освободится" простым увеличением регистра с указателем вершины стека, но у нас ещё есть данные в куче. Тык вот - эта память будет отдана менеджеру памяти, то есть указатель на неё станет того, нечестным. Если это написать на Си, то будет что-то такое: ------------------------------------------------------------------------------------- #include #include #include typedef u_int32_t u32; struct S { u32 a; u32 b; }; void print_s(struct S *s) { printf("s = {.a = %u, .b = %u}\n", s->a, s->b); free(s); // <<< вот именно это Rust делает автоматически!!! } int main(void) { struct S *s = calloc(1, sizeof(struct S)); print_s(s); s->a = 4; s->b = 7; print_s(s); } ------------------------------------------------------------------------------------- И что мы получим после запуска? Да-да... @dexp ~/rust $ ./a.out s = {.a = 0, .b = 0} s = {.a = 4, .b = 7} *** Error in `./a.out': double free or corruption (fasttop): 0x0000000000e17010 *** Аварийный останов Разумеется, если убрать второй вызов print_s(), то всё как бы "нормально", но это только потому, что мы после записи по висячему указателю сразу завершаемся, и просто не доходим до того чудесного момента, когда этот corruption скажется самым неожданным образом. Понятное дело, что в данном случае "сам дурак", но Rust такого в принципе не пропустит в силу концепции "владения". Пример 13. Делаем как положено ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn print_s(s: &Box) { println!("s = {:?}", s); } fn change_s(s: &mut Box) { s.a = 8; s.b = 9; } fn main() { let mut s : Box = Box::new(S{a : 1, b : 2}); print_s(&s); change_s(&mut s); print_s(&s); } ------------------------------------------------------------------------------------- Пример 14. Передача владения из функции наружу ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ А если я хочу что-то выделить в куче внутри функции и отдать это наружу? Попытка номер 1: ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, } fn new_s() -> &Box { Box::new(S{a : 0, b : 0}) } fn print_s(s: &Box) { println!("s = {:?}", s); } fn change_s(s: &mut Box) { s.a = 8; s.b = 9; } fn main() { let mut s = new_s(); print_s(&s); change_s(&mut s); print_s(&s); } ------------------------------------------------------------------------------------- но увы... error[E0106]: missing lifetime specifier --> e014.rs:8:15 | 8 | fn new_s() -> &Box { | ^ help: consider giving it a 'static lifetime: `&'static` | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from а просто не надо было ссылку у возвращаемого значения указывать в сигнатуре new_s(): fn new_s() -> &Box // так неправильно fn new_s() -> Box // так правильно (Box это и так какбы указатель, зачем мне ещё ссылка на него) Честно говоря, я бмп, насколько это по "растомански" или наоборот, но работает как задумано и в примерах ровно так и делается https://doc.rust-lang.org/stable/rust-by-example/std/box.html И да... насчёт 'missing lifetime specifier' Это вообще ___***мозговыносящая***___ и на данный момент абсолютно непонятная "фича" Rust. Я имею ввиду явное указание времён жизни для ссылок Пока абсолютно ясно только одно - это касается только ссылок и ничего больше. Но раз заикнулся, то ... Пример 14, который вовсе не пример, а недоумевание по поводу явного указания времён жизни. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Вот кусок из (рабочего) кода , который без явного аннотирования времен жизни ни в какую не хотел собираться, это просто две структуры ------------------------------------------------------------------------------------- #[derive(Debug)] struct Edsm<'a> { name : &'a str, timers : Vec, intrs : Vec, } #[derive(Debug)] struct Message<'a> { src : &'a Edsm<'a>, dst : &'a Edsm<'a>, code : u32, } ------------------------------------------------------------------------------------- Вот эти вот 'a (штрих-а) повсюду - это оно и есть и ВАЩЕ непонятно, зачем это надо иногда указывать. Я их насовал тупо по подсказкам от компилятора, за что ему спасибо :) Если я хоть как-то понял различные объяснения этой штуки, то, на примере структуры Message (которая содержит 2 ссылки на структуры другого типа) это означает примерно следующее экземпляр Message не может жить дольше, чем ссылки src и dst, а те, в свою очередь, не могут жить дольше, чем то, на что они ссылаются; или наоборот - ссылки src и dst должны жить не меньше, чем экземпляр Message и, далее, экземпляры Edsm должны жить не меньше, чем ссылки src и dst. но, блин, это же и так понятно!... непонятно, нахера это как-то обозначать?... почему компилятор обычно сам домысливает, но тут он почему-то не может этого сделать? отложим до лучших времён попытки понять, где тут собака зарыта, а пока Пример 15. Вектор из стандартной библиотеки ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ А вот хочу теперь массив структур в куче! Сказано - сделано! (глянули сюды https://doc.rust-lang.org/std/vec/struct.Vec.html#method.capacity) 1------------------------------------------------------------------------------------ #[derive(Debug)] struct S { a : u32, b : u32, } fn main() { let mut a : Vec = Vec::with_capacity(10); println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); a[0] = S{ a : 4, b : 9}; } ------------------------------------------------------------------------------------- Запускаем! @dexp ~/rust $ ./e015 a [] len 0 cap 10 thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', /rustc/eae3437dfe991621e8afdc82734f4a172d7ddf9b/src/libcore/slice/mod.rs:2687:14 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. OOPS... как-то неожиданно... я же заказал 10 элементов и думал, что он сразу место под них аллоцирует, ан нет... Написано же про метод with_capacity: " The vector will be able to hold exactly capacity elements without reallocating. If capacity is 0, the vector will not allocate. " Из чего вроде как следует, что если capacity больше нуля, то will allocate. Ладно, блин. 2------------------------------------------------------------------------------------ #[derive(Debug)] struct S { a : u32, b : u32, } fn main() { let mut a : Vec = Vec::with_capacity(10); println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); a.resize(10, 0); println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); a[0] = S{ a : 4, b : 9}; println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); } ------------------------------------------------------------------------------------- Теперь вообще не собирается: error[E0599]: no method named `resize` found for type `std::vec::Vec` in the current scope --> e015.rs:11:4 | 11 | a.resize(10, 0); | ^^^^^^ | = note: the method `resize` exists but the following trait bounds were not satisfied: `S : std::clone::Clone` error: aborting due to previous error У S не реализован типаж Clone... ага, используем уже известную магию, раз ему так надо: ------------------------------------------------------------------------------------- #[derive(Debug,Clone,Copy)] struct S { a : u32, b : u32, } fn main() { let mut a : Vec = Vec::with_capacity(10); println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); a.resize(10, S {a : 0, b : 0}); println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); a[0] = S{ a : 4, b : 9}; println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); } ------------------------------------------------------------------------------------- Теперь все как задумано: a [] len 0 cap 10 a [S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }] len 10 cap 10 a [S { a: 4, b: 9 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }] len 10 cap 10 Пробуем метод push ------------------------------------------------------------------------------------- #[derive(Debug,Clone,Copy)] struct S { a : u32, b : u32, } fn main() { let n = 3; let mut a : Vec = Vec::with_capacity(n); println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); a.resize(n, S {a : 0, b : 0}); println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); a[0] = S{ a : 4, b : 9}; println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); a.push(S{ a : 1, b : 2}); println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); } ------------------------------------------------------------------------------------- От оно как... @dexp ~/rust $ ./e015 a [] len 0 cap 3 a [S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }] len 3 cap 3 a [S { a: 4, b: 9 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }] len 3 cap 3 a [S { a: 4, b: 9 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 1, b: 2 }] len 4 cap 6 то есть push делается по индексу len + 1, это раз, и два - в данном случае не влезало, емкость увеличилась в два раза, с 3 до шести. ок, присваиваем по индексу 5: a [] len 0 cap 3 a [S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }] len 3 cap 3 a [S { a: 4, b: 9 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }] len 3 cap 3 a [S { a: 4, b: 9 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 1, b: 2 }] len 4 cap 6 thread 'main' panicked at 'index out of bounds: the len is 4 but the index is 5', /rustc/eae3437dfe991621e8afdc82734f4a172d7ddf9b/src/libcore/slice/mod.rs:2687:14 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. На мой взгляд, какая-то странная семантика ёмкость равна 6, но почему-то при прямом присваивании оно смотрит не на capacity, а на len... что, resize() нужно делать всегда? или прямое присваивание как бы не надо использовать? только push и подобное? Аналогичное поведение (panic), если обращаться по чтению, но это уже не удивительно. Пробуем макрос vec! ------------------------------------------------------------------------------------- #[derive(Debug,Clone,Copy)] struct S { a : u32, b : u32, } fn main() { let a : Vec = vec![S{a : 0, b : 0}; 3]; println!("a {:?} len {} cap {}", a, a.len(), a.capacity()); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e016 a [S { a: 0, b: 0 }, S { a: 0, b: 0 }, S { a: 0, b: 0 }] len 3 cap 3 Ну вот, теперь как хотелось, сразу имеем обнулённый массив из трёх элементов. Примеры 17 и 18. Модули ~~~~~~~~~~~~~~~~~~~~~~~ file e017.rs: ------------------------------------------------------------------------------------- fn add(x : u32, y : u32) -> u32 { x + y } ------------------------------------------------------------------------------------- file e018.rs: ------------------------------------------------------------------------------------- mod e017; fn main() { let a = e017::add(5,7); println!("a = {}", a); } ------------------------------------------------------------------------------------- Компилируем @dexp ~/rust $ rustc e018.rs -C prefer-dynamic error[E0603]: function `add` is private --> e018.rs:5:16 | 5 | let a = e017::add(5,7); | ^^^ Почему-то в данном случае чёткой подсказки не получили, но мы знаем что надо делать. Надо перед fn в e017.rs написать pub и тогда будет работать. @dexp ~/rust $ ./e018 a = 12 Что делает mod x? Оно делает mod x {}, а внутрь скобок вставляет содержимое файла x.rs Можно явно импортировать из модуля что надо и при этом переобозвать: ------------------------------------------------------------------------------------- mod e017; use e017::add as sum; fn main() { let a = sum(5,7); println!("a = {}", a); } ------------------------------------------------------------------------------------- Пример 19, который опять не пример, а продолжение осмысливания явных указаний времён жизни ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ А вот!!! https://blog.thoughtram.io/lifetimes-in-rust/ Здесь как раз есть то, что мне мозг вынесло, напоминаю, это были ссылки на структуры в структурах #[derive(Debug)] struct Edsm<'a> { name : &'a str, timers : Vec, intrs : Vec, } #[derive(Debug)] struct Message<'a> { src : &'a Edsm<'a>, dst : &'a Edsm<'a>, code : u32, } Больше всего непонятно, зачем в Message писать 'a и после значка ссылки, и после имени другой структуры, да еще и в угловых скобочках. В тексте по ссылке есть вот чего... " struct Config { ... } struct App<'a> { config: &'a Config } One last thing to note here, if App was borrowed in another type, that type will have to define its lifetime parameters as well: struct Platform<'a> { app: App<'a> } " Есть структура, она вкладывается в другую, и эта другая, в свою очередь, вкладывается в третью - и вот тогда нужно писать явно время жизни. Но... у меня-то 2-х этажная конструкция, а не 3-х! Хотя... в Edsm есть два вектора - может быть, поэтому?... И ещё цитата " You might be wondering: Why can’t the compiler simply expand our types with lifetimes just like it does with functions? // вот и я тоже wondering - почему? Good question! Turns out, earlier versions of the compiler actually did exactly that. However, developers found that part confusing and preferred it to know exactly when one value borrows something from another. " Ах вот оно что, раньше не так было. Тут явно что-то не так и "это жужужу не спроста" :) Пример 20. Использование libc, unsafe и жесть с приведением к void* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Можно использовать API ОС как на Си, но блин... на Си проще ;) ------------------------------------------------------------------------------------- use std::ffi::CString; use std::os::raw::c_void as c_void; fn main() { let fname = CString::new("e020.rs").expect("failed"); let fd;// : i32 = -1; let r; let br; let n : usize = 4096; let buf : Vec = vec![0; n]; unsafe { fd = fl::open(fname.as_ptr(), fl::O_RDONLY); if -1 == fd { panic!(std::io::Error::last_os_error()); } let bptr : *mut c_void = buf.as_ptr() as *mut c_void; br = fl::read(fd, bptr, n as isize); if -1 == br { panic!(std::io::Error::last_os_error()); } r = fl::close(fd) as isize; if -1 == r { panic!(std::io::Error::last_os_error()); } } println!("{} bytes read", br); println!("-------------"); } mod fl { use std::os::raw::c_char as c_char; use std::os::raw::c_void as c_void; #[allow(dead_code)] pub const O_RDONLY: i32 = 0o0; #[allow(dead_code)] pub const O_WRONLY: i32 = 0o1; #[link(name = "c")] extern "C" { pub fn open(pathname : *const c_char, flags : i32) -> i32; pub fn read(fd : i32, buf : *const c_void, count : isize) -> isize; pub fn close(fd: i32) -> i32; } } ------------------------------------------------------------------------------------- Напечатать можно вот так ------------------------------------------------------------------------------------- ... println!("{} bytes read", br); let s = match str::from_utf8(&buf) { Ok(v) => v, Err(e) => panic!("Invalid UTF-8 sequence: {}", e), }; println!("result: {}", s) ... ------------------------------------------------------------------------------------- Не, так нули тоже печатаются, видно, если через less смотреть Можно сделать buf.truncate(br as usize); При этом buf должен быть mut. Тут ещё такая интересная вещь наблюдается fd объявлена как неизменяемая, но как же тогда fd = open()??? если сделеть mut, то компилятор сам просит "убери, не надо" потому что внутри unsafe? а нет, не поэтому Пример 21. Небольшое отступление ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- fn main() { let mut x; x = 5; println!("{}", x); } ------------------------------------------------------------------------------------- Компилятор говорит @dexp ~/rust $ rustc e021.rs -C prefer-dynamic warning: variable does not need to be mutable --> e021.rs:3:6 | 3 | let mut x; | ----^ | | | help: remove this `mut` | = note: #[warn(unused_mut)] on by default Значит, если при объявлении не назначили, а потом назначили только 1 раз, то не надо mut. Ну, вроде как логично. Пример 20, продолжение ~~~~~~~~~~~~~~~~~~~~~~ Если задать имя файла, которого нет, получаем при запуске dexp ~/rust $ ./e020 thread 'main' panicked at 'Box', e020.rs:16:4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. Как-то неинформативно... а где же last_os_error? типа "no such file or directory"? ------------------------------------------------------------------------------------- fd = fl::open(fname.as_ptr(), fl::O_RDONLY); if -1 == fd { println!("{:?}", std::io::Error::last_os_error()); panic!("failure"); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e020 Os { code: 2, kind: NotFound, message: "No such file or directory" } thread 'main' panicked at 'failure', e022.rs:18:4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. Вот так-то лучше! ------------------------------------------------------------------------------------- fd = fl::open(fname.as_ptr(), fl::O_RDONLY); if -1 == fd { panic!("open(): {:?}", std::io::Error::last_os_error()); } ------------------------------------------------------------------------------------- А так ещё лучше: @dexp ~/rust $ ./e020 thread 'main' panicked at 'open(): Os { code: 2, kind: NotFound, message: "No such file or directory" }', e022.rs:18:4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. Ну и совсем хорошо: ------------------------------------------------------------------------------------- fd = fl::open(fname.as_ptr(), fl::O_RDONLY); if -1 == fd { panic!("open('{:?}'): {:?}", fname.into_string(), std::io::Error::last_os_error()); } ------------------------------------------------------------------------------------- a@dexp ~/rust $ ./e020 thread 'main' panicked at 'open('Ok("e_020.rs")'): Os { code: 2, kind: NotFound, message: "No such file or directory" }', e022.rs:18:4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. Пример 22. sizeof(), упакованные структуры ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- use std::mem::size_of; struct NotPacked { _a : u8, _x : u32, _y : u32 } #[repr(C, packed)] struct Packed { _a : u8, _x : u32, _y : u32 } fn main() { println!("sizeof(u8) = {} byte", size_of::()); println!("sizeof(u16) = {} bytes", size_of::()); println!("sizeof(u32) = {} bytes", size_of::()); println!("sizeof(u64) = {} bytes", size_of::()); println!("sizeof(f32) = {} bytes", size_of::()); println!("sizeof(f64) = {} bytes", size_of::()); println!("sizeof(NotPacked) = {} bytes", size_of::()); println!("sizeof(Packed) = {} bytes", size_of::()); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e022 sizeof(u8) = 1 byte sizeof(u16) = 2 bytes sizeof(u32) = 4 bytes sizeof(u64) = 8 bytes sizeof(f32) = 4 bytes sizeof(f64) = 8 bytes sizeof(NotPacked) = 12 bytes sizeof(Packed) = 9 bytes Названия полей начинаются с подчёркивания, чтобы компилятор не жаловался таким вот образом warning: field is never used: `y` --> e022.rs:7:5 | 7 | y : u32 | ^^^^^^^ Вообще, это классная штука - а то бывает, понапихаешь в структуру всякого (я имею ввиду Си), что-то используешь, что-то нет, потому что забыл чего хотел и оставил, типа "пусть пока полежит" :) Пример 23. Методы, указатели на функцию (зря смешал, они ортогональны) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : u32, b : u32, f : fn(u32,u32) -> u32 } // методы для типа S impl S { // конструктор fn new() -> Box { Box::new(S { a : 0, b : 0, f : nop }) } fn calc(&self) -> u32 { (self.f)(self.a, self.b) } // деструктор НЕ НУЖЕН } fn nop(_x : u32, _y : u32) -> u32 { 0u32 } fn _add(x : u32, y : u32) -> u32 { x + y } fn sub(x : u32, y : u32) -> u32 { x - y } fn main() { let mut s1 : Box = Box::new(S{a : 7, b : 9, f : sub}); println!("s1 = {:?}", s1); s1.a *= 4; s1.b += 3; println!("... {}", s1.calc()); let s2 = S::new(); println!("... {}", s2.calc()); } ------------------------------------------------------------------------------------- Пример 24. Перечисления (enum), match (сопоставление с образцом) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Перечисления в Rust вовсе не такие, как в Си (набор чисел), каждый возможный вариант может представлять собой всё что угодно, кортеж, структуру и т.п., то есть этот тип даже сложнее, чем структура, это, скорее, возможные варианты структур (в широком смысле), потому что в каждый вариант перечисления можно добавить произвольные данные. см. https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html Наверное, это больше похоже на union (Си), точнее на структуру, в которой первым полем указан тип, а второе поле - union. А вот ------------------------------------------------------------------------------------- #[derive(Debug)] struct Edsm<'a> { name : &'a str, // timers : Vec, // intrs : Vec, } type Action = fn(src: &Edsm, dst: &Edsm) -> u32; enum Reflex { Action(Action), JumpTo(u32), } fn a1(src: &Edsm, dst: &Edsm) -> u32 { println!("Ok"); 7 } fn main() { let r1 = Reflex::Action(a1); let r2 = Reflex::JumpTo(3); let sm = Edsm{name : "aaa"}; match r1 { Reflex::Action(a) => {a(&sm, &sm); println!("A")}, Reflex::JumpTo(s) => println!("T"), } match r2 { Reflex::Action(a) => println!("A"), Reflex::JumpTo(s) => println!("T"), } } ------------------------------------------------------------------------------------- Пример 25. Опять не пример, а вообще __total__ __frustration__ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Есть у меня на Си такая вот штука ------------------------------------------------------------------------------------- struct state { // состояние конечного автомата char *name; action enter; action leave; struct reflex *reflex; // список (массив) реакций на события int nrefl; #define REFLEX_MATRXIX_NROWS 5 #define REFLEX_MATRXIX_NCOLS 16 struct reflex *reflex_matrix[REFLEX_MATRXIX_NROWS][REFLEX_MATRXIX_NCOLS]; }; struct reflex { // реакция на событие u32 ecode; // код события action action; // действие без выхода из состояния struct state *next_state; // переход в следующее состояние }; ------------------------------------------------------------------------------------- И получается, что вот так просто перенести эти назатейлиые структуры в Rust нихрена не получится - дело в том, что эти две структуры ссылаются друг на друга, что для Си (gcc) вообще не проблема, а тут... https://users.rust-lang.org/t/struct-pointing-on-each-other/15879 https://www.reddit.com/r/rust/comments/a3taps/newbie_how_can_i_reference_two_struct_in_each/ кароче, нельзя так в лоб сделать, надо то ли raw pointers использовать, то ли ещё что-то мутить, либо как-то видоизменять модели данных. да, кстати, тут ещё не только структуры друг на друга ссылаются, но и внутри первой есть ссылки из одного поля (матрица реакций) на другое (список всех реакций), и тут тоже бяда... https://stackoverflow.com/questions/27092273/how-to-make-a-struct-where-one-of-the-fields-refers-to-another-field https://users.rust-lang.org/t/struct-containing-reference-to-own-field/1894 https://stackoverflow.com/questions/30823880/struct-that-owns-some-data-and-a-reference-to-the-data https://www.reddit.com/r/rust/comments/5s09m7/struct_members_referencing_other_members/ Первое, что приходит в голову, это заменить указатели на индексы. Ну да ладно, это было вроде как лирическое отступление :) Пример 26. ВНЕЗАПНО! Вложенные структуры невозможны!!! (точно-точно-точно???) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- struct In1 { a : u32, b : u32, } struct In2 { a : u32, b : u32, } struct Out { x : In1, y : In2, } fn main() { let s : Out = Out { x : { a : 0, b : 0 }, y : { a : 0, b : 0 } }; } ------------------------------------------------------------------------------------- @dexp ~/rust $ rustc e026.rs error: expected type, found `0` --> e026.rs:20:10 | 20 | a : 0, | ^ expecting a type here because of type ascription | = note: #![feature(type_ascription)] lets you annotate an expression with a type: `: ` note: this expression expects an ascribed type after the colon --> e026.rs:20:6 | 20 | a : 0, | ^ = help: this might be indicative of a syntax error elsewhere error: expected type, found `0` --> e026.rs:24:10 | 24 | a : 0, | ^ expecting a type here because of type ascription | = note: #![feature(type_ascription)] lets you annotate an expression with a type: `: ` note: this expression expects an ascribed type after the colon --> e026.rs:24:6 | 24 | a : 0, | ^ = help: this might be indicative of a syntax error elsewhere error: aborting due to 2 previous errors ------------------------------------------------------------------------------------- struct In1 { a : u32, b : u32, } struct In2 { a : u32, b : u32, } struct Out { x : In1, y : In2, } fn main() { let s : Out; s.x.a = 0; s.x.b = 0; s.y.a = 0; s.y.b = 0; } ------------------------------------------------------------------------------------- error[E0381]: assign to part of possibly uninitialized variable: `s` --> e026.rs:35:5 | 35 | s.x.a = 0; | ^^^^^^^^^ use of possibly uninitialized `s.x` error: aborting due to previous error For more information about this error, try `rustc --explain E0381`. https://stackoverflow.com/questions/23629201/are-nested-structs-supported-in-rust https://github.com/rust-lang/rfcs/pull/2584 https://users.rust-lang.org/t/is-is-possible-to-make-nested-structs/7071 Пичалька... Примечание - наткнулся, когда хотел повторить структуры для работы с timerfd: ------------------------------------------------------------------------------------- struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds */ }; struct itimerspec { struct timespec it_interval; /* Interval for periodic timer */ struct timespec it_value; /* Initial expiration */ }; ------------------------------------------------------------------------------------- но, скорей всего, я что-то не то делал, ибо https://rust-unofficial.github.io/patterns/patterns/compose-structs.html такс... невозможно написать struct внутри struct, вот что невозможно. тогда что не так с "программой"?... а вот же как надо!!! ------------------------------------------------------------------------------------- #[derive(Debug)] struct In1 { a : u32, b : u32, } #[derive(Debug)] struct In2 { a : u32, b : u32, } #[derive(Debug)] struct Out { x : In1, y : In2, } fn main() { let i1 = In1 {a : 7, b : 8}; let i2 = In2 {a : 3, b : 4}; let o = Out {x : i1, y : i2}; println!("{:?}", o); } ------------------------------------------------------------------------------------- всё прекрасно вкладывается, но всё таки - как обратиться к полю вложенной структуры?... так println!("{}", o.x.a); никаких проблем вообще-то... кароче, я просто тупил, не написал в изначальном варианте типы для вложенных структур, надо вот так жеж!: ------------------------------------------------------------------------------------- fn main() { let s = Out { x : In1 { a : 0, b : 0, }, y : In2 { a : 0, b : 0, }, }; println!("{:?}", s); } ------------------------------------------------------------------------------------- и никаких проблем. Пример 27. Разыменование указателя ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- fn main() { let xp = Box::new(100); println!("{}", *xp); println!("{}", xp); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e027 100 100 Интересно дефки пляшут. Не знаю, оно так всегда или нет, но в данном случае, если не писать звёздочку, оно её само домысливает видимо. а вот https://stackoverflow.com/questions/27852613/why-does-printing-a-pointer-print-the-same-thing-as-printing-the-dereferenced-po и вот https://tourofrust.com/94_ru.html (но у меня нет точки...) Пример 28. Массивы ~~~~~~~~~~~~~~~~~~ type Array3 = [u32; 3]; type Array4 = [u32; 4]; fn func(a : &Array3) { println!("{:?}", a); } fn main() { let a : Array3 = [1, 2, 3]; let b : Array4 = [1, 2, 3, 4]; println!("{:?}", a); println!("{:?}", b); func(&a); // func(&b); } не помню, что я тут хотел "потрогать"... возможно, хотел увидеть, что массивы разной длины - это РАЗНЫЕ ТИПЫ, меж собой никак не совместимые. Пример 29. Ужас и кошмар с временами жизни ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- use std::collections::VecDeque; struct Sm<'a> { name : &'a str, } struct Msg<'a> { src : &'a Sm<'a>, dst : &'a Sm<'a>, code : u32, } fn sm_r(me : &Sm, mq : &VecDeque) { let m = Msg {src : me, dst: me, code : 0}; mq.push_back(m); } fn main() { let mut mq : VecDeque = VecDeque::with_capacity(8); let mut sm = Sm { name : "sm" }; sm_r(&sm, &mq); } ------------------------------------------------------------------------------------- @dexp ~/rust $ rustc e029.rs error[E0623]: lifetime mismatch --> e029.rs:18:18 | 16 | fn sm_r(me : &Sm, mq : &VecDeque) { | -- --- | | | these two types are declared with different lifetimes... 17 | let m = Msg {src : me, dst: me, code : 0}; 18 | mq.push_back(m); | ^ ...but data from `me` flows into `mq` here error: aborting due to previous error https://users.rust-lang.org/t/stuck-at-lifetimes/30028 Прикинемся, что мы ещё абсолютно ничего не знаем про времена жизни. ------------------------------------------------------------------------------------- use std::collections::VecDeque; struct Sm { name : &str, } struct Msg { src : &Sm, dst : &Sm, code : u32, } fn sm_r(me : &Sm, mq : &VecDeque) { let m = Msg {src : me, dst: me, code : 0}; mq.push_back(m); } fn main() { let mut mq : VecDeque = VecDeque::with_capacity(8); let mut sm = Sm { name : "sm" }; sm_r(&sm, &mq); } ------------------------------------------------------------------------------------- error[E0106]: missing lifetime specifier --> e029.rs:30:12 | 30 | name : &str, | ^ expected lifetime parameter error[E0106]: missing lifetime specifier --> e029.rs:34:11 | 34 | src : &Sm, | ^ expected lifetime parameter error[E0106]: missing lifetime specifier --> e029.rs:35:11 | 35 | dst : &Sm, | ^ expected lifetime parameter error: aborting due to 3 previous errors Ок... ------------------------------------------------------------------------------------- struct Sm { name : &'a str, } ------------------------------------------------------------------------------------- error[E0261]: use of undeclared lifetime name `'a` --> e029.rs:30:13 | 30 | name : &'a str, | ^^ undeclared lifetime Ладно... ------------------------------------------------------------------------------------- struct Sm<'a> { name : &'a str, } // ага, получи хехехе struct Msg<'a> { src : &'a Sm, dst : &'a Sm, code : u32, } ------------------------------------------------------------------------------------- И ХРЕН!!!! error[E0106]: missing lifetime specifier --> e029.rs:34:15 | 34 | src : &'a Sm, | ^^ expected lifetime parameter error[E0106]: missing lifetime specifier --> e029.rs:35:15 | 35 | dst : &'a Sm, | ^^ expected lifetime parameter Но это я уже проходил, надо вот так ------------------------------------------------------------------------------------- struct Sm<'a> { name : &'a str, } struct Msg<'a> { src : &'a Sm<'a>, dst : &'a Sm<'a>, code : u32, } ------------------------------------------------------------------------------------- но вертаемся к изначальному... error[E0623]: lifetime mismatch --> e029.rs:41:18 | 39 | fn sm_r(me : &Sm, mq : &VecDeque) { | -- --- | | | these two types are declared with different lifetimes... 40 | let m = Msg {src : me, dst: me, code : 0}; 41 | mq.push_back(m); | ^ ...but data from `me` flows into `mq` here error: aborting due to previous error и это при том, что вот это вот работает!!! ------------------------------------------------------------------------------------- use std::collections::VecDeque; use std::mem::size_of; #[derive(Debug)] struct Timer { interval : u32, fd : i32, } #[derive(Debug)] struct Edsm<'a> { name : &'a str, timers : Vec, intrs : Vec, } impl<'a> Edsm<'a> { fn onmsg(&self, msg : Message) { println!("i am '{}', got msg '{}' from '{}'", self.name, msg.code, msg.src.name); } } #[derive(Debug)] struct Message<'a> { src : &'a Edsm<'a>, dst : &'a Edsm<'a>, code : u32, } fn dequeue(mq : &mut VecDeque) { loop { let m = mq.pop_front(); match m { Some(m) => { let dst = m.dst; dst.onmsg(m); }, None => break, } } } fn main() { println!("sizeof(Edsm) = {}, sizeof(Message) = {}", size_of::(), size_of::()); let mut sm_a = Edsm {name : "sm_a", timers: Vec::new(), intrs: Vec::new()}; println!("{:?}", sm_a); let sm_b = Edsm {name : "sm_b", timers: Vec::new(), intrs: Vec::new()}; println!("{:?}", sm_b); let tm = Timer {interval : 1000, fd : 7}; sm_a.timers.push(tm); println!("{:?}", sm_a); let tm = Timer {interval : 2000, fd : 8}; sm_a.timers.push(tm); println!("{:?} {}", sm_a, sm_a.timers.len()); let mut mq : VecDeque = VecDeque::with_capacity(256); let m = Message {src : &sm_a, dst : &sm_a, code : 1}; mq.push_back(m); let m = Message {src : &sm_a, dst : &sm_a, code : 2}; mq.push_back(m); let m = Message {src : &sm_b, dst : &sm_a, code : 3}; mq.push_back(m); let m = Message {src : &sm_a, dst : &sm_b, code : 4}; mq.push_back(m); dequeue(&mut mq); } ------------------------------------------------------------------------------------- И что блин с этим делать-то?... пишем ------------------------------------------------------------------------------------- fn sm_r<'a>(me : &'a Sm, mq : &'a VecDeque>) { let m = Msg {src : me, dst: me, code : 0}; mq.push_back(m); } ------------------------------------------------------------------------------------- error[E0596]: cannot borrow `*mq` as mutable, as it is behind a `&` reference --> e029.rs:41:5 | 39 | fn sm_r<'a>(me : &'a Sm, mq : &'a VecDeque>) { | --------------------- help: consider changing this to be a mutable reference: `&'a mut VecDeque>` 40 | let m = Msg {src : me, dst: me, code : 0}; 41 | mq.push_back(m); | ^^ `mq` is a `&` reference, so the data it refers to cannot be borrowed as mutable error: aborting due to previous error Вот так работает ------------------------------------------------------------------------------------- use std::collections::VecDeque; #[derive(Debug)] struct Sm<'a> { name : &'a str, } #[derive(Debug)] struct Msg<'a> { src : &'a Sm<'a>, dst : &'a Sm<'a>, code : u32, } fn sm_r<'a>(me : &'a Sm, mq : &'a mut VecDeque>) { let m = Msg {src : me, dst: me, code : 0}; mq.push_back(m); println!("{:?}", mq); } fn main() { let mut mq : VecDeque = VecDeque::with_capacity(8); let sm = Sm { name : "sm" }; sm_r(&sm, &mut mq); } ------------------------------------------------------------------------------------- жесть какая-то... Пример 30. Показалось, что компилятор начинает просить <'a> когда в структурах есть Box/Vec ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Но нет: ------------------------------------------------------------------------------------- #[derive(Debug)] struct OnStack { x : u32, } impl OnStack { fn new() -> OnStack { OnStack {x : 7} } fn dump(&self) { println!("{:?}", self); } } #[derive(Debug)] struct S { x : u32, y : u32, } #[derive(Debug)] struct InHeap { x : u32, y : Vec, } impl InHeap { fn new() -> InHeap { InHeap { x : 8, y : Vec::new(), } } fn dump(&self) { println!("{:?}", self); } } fn main() { let a = OnStack::new(); a.dump(); let mut b = InHeap::new(); b.y.push(S{x: 4, y : 5}); b.y.push(S{x: 6, y : 7}); b.dump(); } ------------------------------------------------------------------------------------- а вот когда в структурах куча ссылок, да ещё одна ссылается на другую, тогда начинается трэш. Пример 31. Сырые указатели ~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[allow(dead_code)] struct State { name : String, } #[allow(dead_code)] pub struct EventSource { fd : i32, owner : *const Edsm, } #[allow(dead_code)] struct Edsm { name : String, states : Vec, io : Box, } fn main() { let mut sm = Edsm { // sm в стеке name: String::from("sm"), // name в куче states : Vec::new(), // state в куче io : Box::new(EventSource{ // io в куче fd : 0, owner : 0 as *const Edsm, }), }; // адрес sm let rp : *const Edsm = &sm; println!("{:?} sm addr", rp); sm.io.owner = rp; // адрес поля name (== адресу sm) let rp : *const String = &sm.name; println!("{:?} sm.name addr", rp); // адрес поля states let rp : *const Vec = &sm.states; println!("{:?} sm.states addr", rp); // адрес поля io let rp : *const Box = &sm.io; println!("{:?} sm.io addr", rp); // адрес самой строки в куче let rp : *const u8 = sm.name.as_ptr(); println!("{:?} name addr", rp); // адрес самого вектора в куче sm.states.push(State{name : String::from("S)")}); // до этого память ещё не была выделена let rp : *const State = sm.states.as_ptr(); println!("{:?} states addr", rp); // адрес самого "бокса" в куче: сначала разыменовать, потом взять ссылку (&*) let rp : *const EventSource = &*sm.io; println!("{:?} io addr", rp); } ------------------------------------------------------------------------------------- от какая красота! @dexp ~/rust $ ./e031 0x7ffc79ed2d70 sm addr 0x7ffc79ed2d70 sm.name addr 0x7ffc79ed2d88 sm.states addr 0x7ffc79ed2da0 sm.io addr 0x55f4568d4130 name addr 0x55f4568d46e0 states addr 0x55f4568d4150 io addr TEXT DATA HEAP STACK ? ну да, именно так https://www.geeksforgeeks.org/memory-layout-of-c-program/ Пример 32. Оператор '+' для массивов (поэлементное сложение) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- use std::ops::{Add}; #[derive(Debug)] struct Arr { a : [u32; 3], } impl Add for Arr { type Output = Self; fn add(self, other : Self) -> Self { Self { a : [self.a[0] + other.a[0], self.a[1] + other.a[1], self.a[2] + other.a[2]] } } } fn main() { let a : Arr = Arr{ a : [1,2,3] }; let b : Arr = Arr{ a : [4,5,6] }; println!("{:?}", a); println!("{:?}", b); let c = a + b; println!("{:?}", c); } ------------------------------------------------------------------------------------- запускаем : @dexp ~/rust $ ./e032 Arr { a: [1, 2, 3] } Arr { a: [4, 5, 6] } Arr { a: [5, 7, 9] } Ну, не совсем для массивов, массивы завёрнуты в структуру, для массивов как таковых не получилось сделать :( Пример 33. Макрос format!() ~~~~~~~~~~~~~~~~~~~~~~~~~~~ fn main() { let dir = "docs".to_string(); let name = "file".to_string(); let suff = "txt".to_string(); let path_name = format!("{}/{}.{}", dir, name, suff); println!("{}", path_name); } Это (почти) тоже самое, что println!(), но не на stdio, а в память (и без '\n' в конце) То есть это аналог sprintf() из libc! Пример 34. Если из метода вернуть ссылку на self, то... ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- struct S { a : u32, b : u32, } impl S { fn pa(&self) -> &Self { println!("a = {}", self.a); self } fn pb(&self) -> &Self { println!("b = {}", self.b); self } fn ps(&self) -> &Self { println!("a + b = {}", self.a + self.b); self } } fn main() { let s = S { a : 4, b : 10, }; s.pa().pb().ps().pb().pa(); } ------------------------------------------------------------------------------------- можно строить эти цепочки методов до посинения... собственно, "returning self is currently the de facto way of enabling method chaining in the Rust ecosystem" https://randompoison.github.io/posts/returning-self/ можно и так написать s .pa() .pb() .ps(); ( *** правда, автор по ссылке пишет, что как-то оно не очень комильфо, поскольку если метод должен что-то иное вернуть, то никакого method chaining не получится... да, сопсна, оно и не сильно надо, всегда можно по очереди вызвать. *** ) если хотим что-то менять внутри структуры, то ------------------------------------------------------------------------------------- struct S { a : u32, b : u32, } impl S { fn pa(&mut self) -> &mut Self { println!("a = {}", self.a); self } fn pb(&mut self) -> &mut Self { println!("b = {}", self.b); self } fn ps(&mut self) -> &mut Self { println!("a + b = {}", self.a + self.b); self } fn inca(&mut self) -> &mut Self { self.a += 1; self } } fn main() { let mut s = S { a : 4, b : 10, }; s.pa().pb().ps().pb().pa().inca().pa(); } ------------------------------------------------------------------------------------- гыыыы. S такая же, как и была: ------------------------------------------------------------------------------------- fn main() { let _ = S { a : 4, b : 10, }.pa().pb().ps().pb().pa().inca().pa().ps(); } ------------------------------------------------------------------------------------- Пример 35. Складываем метры с миллиметрами (копипаста из книги) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- use std::ops::Add; #[derive(Debug)] struct Millimeters(u32); #[derive(Debug)] struct Meters(u32); impl Add for Millimeters { type Output = Millimeters; fn add(self, other: Meters) -> Millimeters { Millimeters(self.0 + (other.0 * 1000)) } } fn main() { let x : Millimeters = Millimeters(20); let y : Meters = Meters(1); println!("x + y = {:?}", x + y); } ------------------------------------------------------------------------------------- Пример 36. Функция, возвращающая замыкание ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- fn make_counter() -> impl FnMut() -> i32 { let mut counter = 0; move || { counter += 1; counter } } fn main() { let mut cool_counter = make_counter(); let mut c = cool_counter(); println!("{}", c); c = cool_counter(); println!("{}", c); c = cool_counter(); println!("{}", c); } ------------------------------------------------------------------------------------- запускаем @dexp ~/rust $ ./e036 1 2 3 а два счётчика? ------------------------------------------------------------------------------------- fn make_counter() -> impl FnMut() -> i32 { let mut counter = 0; move || { counter += 1; counter} } fn main() { let mut c1 = make_counter(); let mut c = c1(); println!("{}", c); c = c1(); println!("{}", c); c = c1(); println!("{}", c); let mut c2 = make_counter(); c = c2(); println!("{}", c); c = c2(); println!("{}", c); c = c2(); println!("{}", c); } ------------------------------------------------------------------------------------- блин, это очень сильное колдунство :) @dexp ~/rust $ ./e036 1 2 3 1 2 3 Наверное, надо понимать это примерно так когда мы делаем c1 = make_counter(); создаётся нечто, некий "объект", в котором хранится тело замыкания, грубо говоря, некая функция и .... её "окружение", в данном случае это переменная counter когда делаем второй раз, создаётся точно такая же вторая штука... Счётчик с начальным значением: ------------------------------------------------------------------------------------- fn make_counter_from(start : i32) -> impl FnMut() -> i32 { let mut counter = start; move || { let cur = counter; counter += 1; cur } } fn main() { let mut c1 = make_counter_from(10); let mut c = c1(); println!("{}", c); c = c1(); println!("{}", c); c = c1(); println!("{}", c); let mut c2 = make_counter_from(20); c = c2(); println!("{}", c); c = c2(); println!("{}", c); c = c2(); println!("{}", c); } ------------------------------------------------------------------------------------- Запускаем @dexp ~/rust $ ./e036-a 10 11 12 20 21 22 А вот так ещё веселее! Возвращаем 2 замыкания ------------------------------------------------------------------------------------- fn make_counter_pair() -> (impl FnMut() -> i32, impl FnMut() -> i32) { let mut counter1 = 0; let mut counter2 = 0; (move || { counter1 += 1; counter1 }, move || { counter2 += 2; counter2 }) } fn main() { let (mut c1, mut c2) = make_counter_pair(); let mut c = c1(); println!("{}", c); c = c1(); println!("{}", c); c = c1(); println!("{}", c); c = c2(); println!("{}", c); c = c2(); println!("{}", c); c = c2(); println!("{}", c); } ------------------------------------------------------------------------------------- к слову... https://zetcode.com/python/python-closures/ "Python closures can be an alternate solution to small classes." https://www.programiz.com/python-programming/closure "When there are few methods (one method in most cases) to be implemented in a class, closures can provide an alternate and more elegant solution." а если так? ------------------------------------------------------------------------------------- fn make_2way_counter() -> (impl FnMut() -> i32, impl FnMut() -> i32) { let mut counter = 0; (move || { counter += 1; counter }, move || { counter -= 1; counter }) } fn main() { let (mut inc, mut dec) = make_2way_counter(); let mut c = inc(); println!("{}", c); c = inc(); println!("{}", c); c = inc(); println!("{}", c); c = dec(); println!("{}", c); c = dec(); println!("{}", c); c = dec(); println!("{}", c); } ------------------------------------------------------------------------------------- но нет: @dexp ~/rust $ ./e036-d 1 2 3 -1 -2 -3 в каждом из двух замыканий счётчик изначально 0. эх... а вот как можно, с аргументом у (одного) замыкания: ------------------------------------------------------------------------------------- fn make_counter() -> impl FnMut(i64) -> i64 { let mut counter = 0i64; move |inc| { counter += inc; counter } } // описание аргумента замыкания разнесено // тип указан в impl FnMut(i64) // имя указано в |inc| // с непривычки выглядит малость волшебно fn main() { let mut c1 = make_counter(); let mut c = c1(1); println!("{}", c); c = c1(2); println!("{}", c); c = c1(3); println!("{}", c); c = c1(-1); println!("{}", c); c = c1(-2); println!("{}", c); c = c1(-3); println!("{}", c); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e036-e 1 3 6 5 3 0 Пример 37. Итерация по вектору ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- fn main() { let mut v1 : Vec = Vec::new(); v1.push(2); v1.push(3); v1.push(4); v1.push(5); let v2 : Vec = v1.iter().map(|x| x * x).collect(); // передаём в map замыкание for n in v1 { println!("{}", n); } for n in v2 { println!("{}", n); } } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e037 2 3 4 5 4 9 16 25 Пример 38. Перечисление Option ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- fn main() { let arr = [0,1,2,3,4,5]; let a2 = arr[2]; println!("{}", a2); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e038 2 Делаем обращение "мимо" ------------------------------------------------------------------------------------- fn main() { let arr = [0,1,2,3,4,5]; let a6 = arr[6]; println!("{}", a6); } ------------------------------------------------------------------------------------- Просто не откомпилируется: dexp ~/rust $ rustc e038.rs -C prefer-dynamic error: index out of bounds: the len is 6 but the index is 6 --> e038.rs:4:14 | 4 | let a6 = arr[6]; | ^^^^^^ | = note: #[deny(const_err)] on by default error: aborting due to previous error Делаем срез (ссылку на весь массив) и используем метод get: ------------------------------------------------------------------------------------- fn main() { let arr = [0,1,2,3,4,5]; let slc = &arr; println!("{:?}", slc.get(6)); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e038 None То есть оно "работает", но получили мы... None вобчем ;) У типа Option есть метод unwrap(), ок, пишем ------------------------------------------------------------------------------------- fn main() { let arr = [0,1,2,3,4,5]; let slc = &arr; println!("{:?}", slc.get(6).unwrap()); } ------------------------------------------------------------------------------------- dexp ~/rust $ ./e038 thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:347:21 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. А если нельзя в панику впадать? Делаем match ------------------------------------------------------------------------------------- fn main() { let arr = [0,1,2,3,4,5]; let slc = &arr; match slc.get(6) { Some(a) => println!("{}", a), None => println!("Извините, мы облажались"), } } ------------------------------------------------------------------------------------- Запускаем @dexp ~/rust $ ./e038 Извините, мы облажались То, что надо! :) У Option куча всякоразных методов, типа .or_else и всяких таких прикольных, см. https://doc.rust-lang.org/std/option/enum.Option.html let last = *slice.get(5).unwrap_or(&-1); и всё такое... О, я понял - unwrap() достаёт из обертки Some() содержимое и паникует в случае None Пример 39. Типажи и статическая диспетчеризация. Типажи-методы и динамическая диспетчеризация ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a ------------------------------------------------------------------------------------- // типаж "МожетСказать" trait CanSay { // метод "сказать" fn say(&self); } struct A { x : u32, y : u32, } struct B<'a> { x : &'a str, y : &'a str, } struct C { x : u64, y : u64, } // реализация типажа для структуры А impl CanSay for A { fn say(&self) { println!("x + y = {}", self.x + self.y); } } // реализация типажа для структуры B impl CanSay for B<'_> { fn say(&self) { println!("x + y = {}", format!("{}{}", self.x, self.y)); } } // реализации типажа для структуры C нет // функцию можно вызывать для всех типов, которые реализуют типаж CanSay fn tell(o : T) { o.say(); } fn main() { let s1 = A {x : 3, y : 4}; let s2 = B {x : "Hello, ", y : "world!"}; let s3 = C {x : 23, y : 18}; // тут работает мономорфизация // для каждого типа (A и B) будет сгенерирована // отдельная версия кода и эти версии и будут вызваны // а скорее всего, они сюда просто вставятся tell(s1); tell(s2); // tell(s3); // это не откомпилруется, скажет /* @dexp ~/rust $ rustc e039-a.rs -C prefer-dynamic error[E0277]: the trait bound `C: CanSay` is not satisfied --> e039-a.rs:52:10 | 40 | fn tell(o : T) { | ------ required by this bound in `tell` ... 52 | tell(s3); | ^^ the trait `CanSay` is not implemented for `C` error: aborting due to previous error For more information about this error, try `rustc --explain E0277`. */ } ------------------------------------------------------------------------------------- Результат запуска @dexp ~/rust $ ./e039-a x + y = 7 x + y = Hello, world! для функции tell возможны вариации сигнатуры: ------------------------------------------------------------------------------------- // вариант 2 fn tell(o : T) where T: CanSay { o.say(); } // вариант 3 fn tell(o : impl CanSay) { o.say(); } ------------------------------------------------------------------------------------- А теперь диспетчеризация в runtime "Типажи-объекты, такие как &Foo или Box, это обычные переменные, хранящие значения любого типа, реализующего данный типаж. Конкретный тип типажа-объекта может быть определен только на этапе выполнения" b------------------------------------------------------------------------------------- trait CanSay { fn say(&self); } struct A { x : u32, y : u32, } struct B<'a> { x : &'a str, y : &'a str, } impl CanSay for A { fn say(&self) { println!("x + y = {}", self.x + self.y); } } impl CanSay for B<'_> { fn say(&self) { println!("x + y = {}", format!("{}{}", self.x, self.y)); } } // в функцию можно передать всё, что реализует типаж CanSay // то есть отличие от e039-a только в сигнатуре этой функции fn tell(o : &CanSay) { o.say(); } fn main() { let s1 = A {x : 3, y : 4}; let s2 = B {x : "Hello, ", y : "world!"}; tell(s1); tell(s2); } ------------------------------------------------------------------------------------- а нет: @dexp ~/rust $ rustc e039-b.rs warning: trait objects without an explicit `dyn` are deprecated --> e039-b.rs:31:14 | 31 | fn tell(o : &CanSay) { | ^^^^^^ help: use `dyn`: `dyn CanSay` | = note: `#[warn(bare_trait_objects)]` on by default error[E0308]: mismatched types --> e039-b.rs:40:10 | 40 | tell(s1); | ^^ | | | expected `&dyn CanSay`, found struct `A` | help: consider borrowing here: `&s1` | = note: expected reference `&dyn CanSay` found struct `A` error[E0308]: mismatched types --> e039-b.rs:41:10 | 41 | tell(s2); | ^^ | | | expected `&dyn CanSay`, found struct `B` | help: consider borrowing here: `&s2` | = note: expected reference `&dyn CanSay` found struct `B<'_>` error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0308`. c------------------------------------------------------------------------------------- trait CanSay { fn say(&self); } struct A { x : u32, y : u32, } struct B<'a> { x : &'a str, y : &'a str, } impl CanSay for A { fn say(&self) { println!("x + y = {}", self.x + self.y); } } impl CanSay for B<'_> { fn say(&self) { println!("x + y = {}", format!("{}{}", self.x, self.y)); } } // в функцию можно передать всё, что реализует типаж CanSay // то есть отличие от e039-a только в сигнатуре этой функции // нужно явно указать dyn, иначе не откомпилируется... fn tell(o : &dyn CanSay) { o.say(); } fn main() { let s1 = A {x : 3, y : 4}; let s2 = B {x : "Hello, ", y : "world!"}; tell(&s1); // ссылка! tell(&s2); // ссылка! } c------------------------------------------------------------------------------------- вот теперь ок: @dexp ~/rust $ ./e039-c x + y = 7 x + y = Hello, world! Работает это примерно следующим образом - в типаже-объекте содержится указатель на данные и указатель на таблицу виртуальных методов. Таким образом, имеем 2 вида параметрического полиморфизма * со статическим выбором методов на основе мономорфизации * с динамическим выбором методов на основе таблицы виртуальных методов а если сложено в Vec>, то ------------------------------------------------------------------------------------- let mut a : Vec> = Vec::new(); a.push(Box::new(A {x : 11, y : 22})); a.push(Box::new(B {x : "Goodby, ", y : "world!"})); for i in &a { tell(&**i); // вот жеж ты сука!!! методом тыка подобрал комбинацию закорючек } for ii in &a { tell(&**ii); } d------------------------------------------------------------------------------------- То же самое, но с Box (код выше прежний): d------------------------------------------------------------------------------------- // fn tell(o : Box) { // так тоже работает, только warning: trait objects without an explicit `dyn` are deprecated fn tell(o : Box) { o.say(); } fn main() { let s1 : Box = Box::new(A {x : 3, y : 4}); let s2 : Box = Box::new(B {x : "Hello, ", y : "world!"}); tell(s1); tell(s2); } d------------------------------------------------------------------------------------- если нужно в tell передавать ссылку на Box, то ------------------------------------------------------------------------------------- fn tell(o : &Box) { o.say(); } fn main() { let s1 : Box = Box::new(A {x : 3, y : 4}); let s2 : Box = Box::new(B {x : "Hello, ", y : "world!"}); let s1d : Box = s1; let s2d : Box = s2; tell(&s1d); tell(&s2d); tell(&s1d); tell(&s2d); println!(""); /* или так tell(&(s1 as Box)); tell(&(s2 as Box)); */ let mut a : Vec> = Vec::new(); a.push(Box::new(A {x : 11, y : 22})); a.push(Box::new(B {x : "Goodby, ", y : "world!"})); for i in &a { // Ok! tell(i); } for ii in &a { tell(ii); } } ------------------------------------------------------------------------------------- ==== После обновления перестало работать -C prefer-dynamic, не может найти stdlib, а ищет вот где open("/lib/x86_64-linux-gnu/tls/x86_64/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/tls/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/x86_64/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64-linux-gnu/tls/x86_64/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64-linux-gnu/tls/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64-linux-gnu/x86_64/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64-linux-gnu/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/lib/tls/x86_64/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/lib/tls/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/lib/x86_64/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/lib/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib/tls/x86_64/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib/tls/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib/x86_64/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib/libstd-6f77337c1826707d.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) Пример 40. Dropping by rebinding ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct I32 { val : Option } impl Drop for I32 { fn drop(&mut self) { println!("Some({}) dropped", self.val.unwrap()); } } fn main() { let mut i = I32{ val : Some(1) }; println!("i = {:?}", i); i = I32{ val : Some(2)}; println!("i = {:?}", i); i = I32{ val : Some(3)}; println!("i = {:?}", i); i = I32{ val : None}; i = I32{ val : None}; i = I32{ val : None}; } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e040 i = I32 { val: Some(1) } Some(1) dropped i = I32 { val: Some(2) } Some(2) dropped i = I32 { val: Some(3) } Some(3) dropped https://habr.com/ru/post/532660/ Так что инструкция a; становится аналогом drop(a) fn main() { let mut i = I32{ val : Some(1) }; println!("i = {:?}", i); i; и действительно... @dexp ~/rust $ rustc e040.rs -C prefer-dynamic warning: path statement drops value --> e040.rs:17:5 | 17 | i; | ^^ help: use `drop` to clarify the intent: `drop(i);` | = note: `#[warn(path_statements)]` on by default warning: 1 warning emitted Пример 41 (дурацкий). Оператор ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- fn is_even(n : u32) -> Result<(), String> { if 1 == n & 1 { let e = format!("{} is not even", n); Err(e) } else { Ok(()) } } fn check_number_v1(n : u32) -> bool { match is_even(n) { Ok(()) => { println!("ok"); true }, Err(e) => { println!("failed: {}", e); false } } } fn check_number_v2(n : u32) -> Result<(),String> { is_even(n)?; println!("ok, ok"); Ok(()) } fn main() { let mut n = 6; check_number_v1(n); n = 8; println!("{:?}", check_number_v2(n)); n = 7; println!("{:?}", check_number_v2(n)); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e041 ok ok, ok Ok(()) Err("7 is not even") Пример 42. Обобщенные типы (generic) в структурах ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ------------------------------------------------------------------------------------- #[derive(Debug)] struct S { a : X, b : X } #[derive(Debug)] struct Q<'a> { n1 : &'a str, n2 : &'a str } struct Nthings { n : N, s : S } fn main() { let s1 = S {a : 7, b : 8}; let s2 = S {a : "seven", b : "eight"}; println!("{:?}", s1); println!("{:?}", s2); // let s3 = S {a : 7, b : "eight"}; // expected integer, found `&str` let s4 = Nthings { n : 7, s : "поросят"}; println!("{} {}", s4.n, s4.s); let s5 = Nthings { n : "килограмма", s : 3}; println!("{} {}", s5.n, s5.s); let s6 = S { a : Q {n1 : "Ваня", n2 : "Петров"}, b : Q {n1 : "Коля", n2 : "Иванов"} }; println!("{:?}", s6); } ------------------------------------------------------------------------------------- @dexp ~/rust $ ./e042 S { a: 7, b: 8 } S { a: "seven", b: "eight" } 7 поросят килограмма 3 S { a: Q { n1: "Ваня", n2: "Петров" }, b: Q { n1: "Коля", n2: "Иванов" } Пример 43. Итератор ~~~~~~~~~~~~~~~~~~~ не очень понятно, что это за into_iter() такой... ------------------------------------------------------------------------------------- struct CounterUptoTen(u32); impl CounterUptoTen { fn new() -> CounterUptoTen { CounterUptoTen(0) } } impl Iterator for CounterUptoTen { type Item = u32; fn next(&mut self) -> Option { if self.0 < 10 { self.0 += 1; Some(self.0) } else { None } } } fn main() -> () { let mut c = CounterUptoTen::new(); loop { match c.next() { Some(x) => println!("{}", x), None => break } } let mut cc = CounterUptoTen::new(); while let Some(x) = cc.next() { println!("{}", x) }; let ccc = CounterUptoTen::new(); // note no mut for x in ccc.into_iter() { println!("{}", x) }; } -------------------------------------------------------------------------------------