Тема: Вивчення Rust
Вирішив таки повивчати Rust, побачивши це.
Треба ж кудись рухатися, правильно?
Спробую сюди писати думки і враження. Це стосується не стільки Rust, скільки взагалі концепцій, які я вивчатиму - гадаю, вони для Rust не унікальні.
Отже, в двох словах. Rust - пам'яте- і нитевобезпечна мова з абстракціями нульової вартості. Тобто немає ніяких збиральників сміття, неявних параметрів, виключних ситуацій, зате є багатий інструментарій, покликаний це все замінити.
Тепер - від чого в мене фрустрації. Перше - Result::unwrap(). Коли функція може повернути значення або помилку, Rust використовує тип Result, який "обгортає" результат (чи помилку), що часто призводить до конструкцій вигляду "змінна = якщо результат нормальний, то вміст "обгортки", інакше припинити програму з повідомленням про помилку". Мовою Rust це записується так:
x = match func() { // match - розширений аналог switch
Ok(value) => value, //якщо все гаразд, то match поверне value
Error(message) => panic!(message), //якщо помилка, програма завершиться з повідомленням про неї
}
Оскільки ця конструкція є загальним місцем, її загнали у функцію unwrap, і вся попередня конструкція записується просто
x = func().unwrap();
Ці unwrap мене поки засмучують. Я навіть пробував ставити їх у наступний рядок, сприймаючи як "перевірити на помилку":
x = func()
.unwrap();
але трохи розібравшися, таки зрозумів, що це неправильно: основне призначення unwrap - це "розгорнути" конверт-Result, а що при цьому відбувається падіння - це альтернативна гілка.
Друге - це Builder-и. В Rust, як я вже писав, немає типових значень параметрів функцій. Отже, для створення складного об'єкта потрібно вказати всі його параметри, а це дуже незручно. Вихід такий: будується примітивний об'єкт Builder з типовими значеннями параметрів, деякі замінюються на потрібні нам, після чого викликається метод build(). Наприклад:
let car = CarFactory::orderCar() //повертає carBuilder з типовими параметрами
.horses(200) //встановлює цьому carBuilder параметр horses
.color(Red) //встановлює color
.build() //викликає повний конструктор Car з усіма параметрами, повертає Result
.unwrap(); //дістає нашу нову машину з фабричного пакування
Функції horses і color встановлюють тільки два значення, решта лишаються типовими. Що мене тут засмучує:
- функція orderCar за логікою має повертати машину, але повертає тільки її "креслення" (CarBuilder), які ще треба "побудувати" (build). Трохи незвично.
- в цьому ланцюжку функцій (принаймні, мені зараз) легко загубити, з чим ми працюємо. Перші три функції повертають один і той самий CarBuilder, build повертає Result і unwrap - Car. Мені здається, мало б певний сенс додати якесь позначення хоча б для тих функцій, які повертають об'єкт, для якого викликані (&self), щоб бачити, який фрагмент ланцюжка працює над одним об'єктом, а де об'єкт вже інший.
І так, така конструкція ефективна, оскільки функції (зокрема, конструктори) Builder-ів є по суті простими setter-ами, які компілятор інлайнить у код, а оптимізатор прибирає зайве присвоювання типового значення.
І це я ще про те, скільки видів дужок використовується, не розповідав...