В последние дни пытаюсь читать вторую редакцию Rust book-а. Но не потому, что сильно воспылал желанием попрограммировать на Rust-е, а потому, что предлагают принять участие в обсуждении Rust-а и C++ на грядущей C++ CoreHard Spring 2018. Вот и пытаюсь слегка погрузиться в тему.
По ходу чтения складывается ощущение, что второе издание писали с прицелом на дебилов веб-разработчиков, которые кроме Ruby и JavaScript-а ничего не видели. Плюс еще и очень сильно ощущается маркетинговая составляющая. Мол, Rust -- это чуть ли не лучшее, что появилось в области разработки софта, и с Rust-ом ваши волосы всегда будут... ;) Возможно, что я ошибаюсь, но года три назад, когда я впервые начал к Rust-у серьезно приглядываться, у меня не было такого ощущения.
Но в этот раз хочется заострить внимание на другом. Всегда, когда доводилось погружаться в Rust, ловил себя на том, что при чтении Rust-овского кода приходится заострять внимание буквально на каждом символе. И вот наткнулся на очередное подтверждение этого впечатления. Простая программка:
fn main() { let x = 1; let y = { x+1 }; println!("x={:?}, y={:?}", x, y); } |
Результат работы вполне ожидаемый: x=1, y=2
Добавляем всего одну точку с запятой:
fn main() { let x = 1; let y = { x+1; }; println!("x={:?}, y={:?}", x, y); } |
И получаем уже принципиально другой результат: x=1, y=()
Это потому, что блок кода -- это выражение (expression). И если последняя строка кода в таком выражении не имеет завершающей точки с запятой, то именно эта строка (т.е. выражение в этой строке) будет определять тип и результат всего блока кода. А вот если последняя строка завершается точкой запятой, то у всего блока кода типом и результатом будет специальный пустой тупл (или пустая структура, что в случае Rust-а, как я понял монопенисуально).
Не то, чтобы я хотел сказать, что эта частная особенность Rust-а -- это что-то такое из ряда вон. Но, как лишнее подтверждение того, что у создателей Rust-а весьма своеобразный взгляд на удобство синтаксиса -- это точно.
Ну и еще до кучи несколько примеров, которые заставляют пересматривать свои взгляды на то, что такое хорошо, а что такое плохо.
Например, в Rust-е есть специальные частные случаи для сокращения кода по инициализации структур (примеры взяты отсюда). Обычный способ создания и инициализации экземпляра структуры:
fn build_user(email: String, username: String) -> User { User { email: email, username: username, active: true, sign_in_count: 1, } } |
Кстати, обратите внимание на отсутствие точки запятой после объявления экземпляра User -- это важно (почему см.выше).
Но можно записать и так:
fn build_user(email: String, username: String) -> User { User { email, username, active: true, sign_in_count: 1, } } |
Оказывается, что если у вас в локальном контексте уже есть переменная или аргумент функции, имя которой совпадает с именем поля структуры, то можно просто записать имя переменной и компилятор Rust-а поймет, что имя переменная с этим именем должна использоваться для инициализации поля с таким же именем. Так, немножко волшебного порошка синтаксической сладкой пудры :)
Но и это еще не все. Допустим, у вас есть экземпляр структуры User с именем user1. И вы хотите создать еще один экземпляр такого же типа, но часть значений вам нужно взять из user1. Для этого, оказывается, так же есть специальный синтаксис:
let user2 = User { email: String::from("another@example.com"), username: String::from("anotherusername567"), ..user1 }; |
Вот эта магия -- ..user1 -- это оно и есть.
Ну и напоследок еще одна штука, которая попадает в категорию "это нужно просто запомнить, потому что понять это невозможно" :)
В Rust-е многие вещи, которые в C, C++ или в Java являются statement-ами, оказываются expression-ами. Т.е. выражениями, возвращающими значение. Например, в Rust-е if -- это expression. И match (который паттерн-матчинг) так же является expression-ом. Собственно, в этом нет ничего непривычного. Вот что реально рвет шаблон, так это то, что внутри expression-а могут быть control flow statement-ы. Например, continue.
loop { ... // Сколько-то кода. let guess: u32 = match guess.trim().parse { Ok(num) => num, Err(_) => continue }; ... // Еще много кода. } |
Т.е. мы хотим где-то внутри цикла определить константу, берем ее значение из результата match-а. Но в самом match-е мы делаем continue. Т.е. безусловный переход на самый конец цикла. Неплохой такой expression с такими неслабыми побочными эффектами :)
Опять же, не хочу сказать, что это все "какой-то полный трындец". Вовсе нет, в каждой избушке свои погремушки. Нужно просто привыкнуть, что здесь вот так. Но вот по ходу изучения языка такие непривычные мелочи изрядно доставляют.
Если кто-то думает, что я являюсь хейтером Rust-а, то вы ошибаетесь. На самом деле мне сейчас совершенно фиолетово. Что Rust, что Java, что C#, что Haskell какой-нибудь. Это все какие-то соседние миры, которые как-то сами по себе существуют и оказывают на меня, как на C++ника, какое-то косвенное воздействие. Ну и пускай себе существуют.
У меня самого к Rust-у на данный момент следующее отношение: за чужой счет я бы попробовал что-нибудь сделать на Rust-е. Думаю, это был бы интересный опыт. Но вот тратить свое время и свои деньги для разработки какого-то продукта на Rust-е... Вот это уже дудки.
Комментариев нет:
Отправить комментарий