Rust не вызвал у меня большого интереса, когда я впервые прочитал о нём. Это было около двух лет назад. Я работал веб-разработчиком, программировал в основном на JavaScript и подумал тогда, что Rust не для меня, потому что в тот момент он казался мне очень сложным.
А в начале этого года я решил начать изучать его. Что изменилось за это время? Я всё ещё веб-разработчик, но понимание того, что, освоив этот язык программирования, я смогу написать программу на Rust, скомпилировать её на WebAssembly и выполнить в браузере, было той искрой, которая зажгла мою мотивацию.
В этой статье я представлю Rust с точки зрения разработчика JavaScript, одновременно сравнивая эти два языка. Надеюсь, что после прочтения статьи вам тоже захочется освоить Rust!
Язык программирования Rust был создан компанией Mozilla, а его первая стабильная версия появилась на свет примерно в 2015 году. Hello, World
на Rust выглядит так:
fn main() {
println!("Hello, World!");
}
И совсем вроде не страшно. Можно даже сказать, что выглядит она почти как JavaScript, но это всего лишь программа hello world, а сама версия немного сложнее! Прежде чем переходить к функциональным средствам языка, давайте определим, какое место занимает Rust в окружении других языков программирования:
Между языками программирования существует чёткое разграничение:
Rust можно считать быстрым и безопасным одновременно, но на его освоение может уйти много времени и усилий, а время компиляции может быть большим даже для маленьких примеров.
Как и любой другой язык программирования, Rust обладает обширным функционалом, и я решил освятить в статье четыре темы, без которых невозможно изучить язык и начать на нём работать.
JavaScript — это динамически типизированный язык, позволяющий делать интересные штуки, например вычитать число 1
из строки wat
и получать неожиданные результаты. Это стало возможно благодаря слабой системе типов. Если вы попытаетесь выполнить простое сложение двух чисел на Rust, которые не относятся к одному типу, вы получите ошибку компиляции:
fn main() {
let a: i32 = 42;
let b: f64 1.0;
println!("{}", a + b); // ОШИБКА: a и b не относятся к одному типу.
}
Когда вы начнёте работать на Rust, вы будете получать много ошибок, и может так случиться, что первое время люто возненавидите компилятор:
Ну почти как эта собачка! Если вы постоянно воюете с компилятором Rust, не переживайте: мы все через это проходим.
В функциональных языках приходится много работать с неизменяемыми структурами. JavaScript программистов никто не заставляет работать с неизменяемостью, но популярные библиотеки, такие как Redux и Immutable.js, научили нас это делать. Сегодня есть ключевые слова let
и const
для объявления изменяемых и неизменяемых переменных соответственно.
На Rust для объявления переменных мы будем использовать лишь только let
, причём переменные эти будут неизменяемы по умолчанию. Если мы захотим использовать изменяемые данные, нам придётся добавить ключевое слово mut
в объявление:
fn main() {
let a: i32 = 42;
let b: f64 1.0;
println!("{}", a + b); // ОШИБКА: a и b не относятся к одному типу.
}
Это понятие, на мой взгляд, самое сложное для понимания, потому что оно сильно отличает Rust от других языков, с которыми я работал, но именно оно делает Rust быстрым и безопасным!
Когда вы присваиваете данные переменной, концепция владения подразумевает, что переменная владеет этими данными, причём у данных может быть только один владелец. Проиллюстрируем это примером:
fn main() {
let x = String::from("hello"); // x владеет строчкой "hello"
let a = x; // В этот момент a владеет строчкой "hello", и x больше не является валидным
do_something(x); // ОШИБКА: x больше не может использоваться!
}
В Rust нет значений null
и undefined
, поэтому мы не можем описать некую переменную, которая не имеет значения. В предыдущем примере, когда мы присвоили a
значение x
, мы переместили значение от x
к a
, то есть теперь x
не имеет валидного значения. То же самое происходит с функциями:
fn main() {
let x = String::from("hello");
do_something(x);
do_other_thing(x); // ОШИБКА: x больше не может использоваться!
}
fn do_something(s: String) {
// Что-то выполняется с s
}
Когда мы вызываем метод do_something
, то перемещаем значение из x
в s
, то есть в аргумент, полученный функцией. После выполнения функции мы возвращаемся к main
, а x
больше не имеет валидного значения.
Такое поведение не всегда желательно, но в Rust мы можем заимствовать, ведь здесь существует понятие заимствования! Если вам не хочется перемещать значение от одной переменной к другой, воспользуйтесь ссылкой:
fn main() {
let x = String::from("hello");
do_something(&x);
do_other_thing(&x); // Теперь это нормально, потому что мы не перемещаем значение
}
fn do_something(s: &String) {
// Что-то выполняется с s
}
Когда мы имеем дело с владением и заимствованием, Rust хочет, чтобы мы играли по правилам, поэтому он предупредит, если мы попытаемся сделать что-то не то:
Если при изучении владения и заимствования вам что-то непонятно, сбивает с толку или вы запутались, ничего страшного — это нормально! Ведь вы приступаете к теме управления памятью, а это непростая тема. Я рекомендую посмотреть вот это видео, чтобы подробнее узнать об этом.
Rust не является объектно-ориентированным языком, но у него есть некоторые функциональные средства, которые могут имитировать поведение, характерное для такого рода языков. Когда мы работаем с классами в JavaScript, то имеем дело с данными и методами в одном и том же месте. В Rust мы будем отделять представление данных от методов, которые с ними работают. Вот как это происходит:
struct Dog {
name: String,
score: i32
}
impl Dog {
fn say_something(self: &Dog) {
println!("Hey, my name is {}... I mean WOOF!", self.name);
}
}
fn main() {
let dog = Dog { name: String::from("Boira"), score: 13 };
dog.say_something();
}
Структура struct Dog
очень похожа на объект JavaScript, но она отличается от него. Структура — это форма каких-то данных, которые будут иметь два именованных поля: name
и score
. Ниже структуры struct
располагается блок реализации (сокращённо impl
). Вот так мы можем объявлять методы, которые будут работать с данными. И заметьте: если понадобится связать функцию с этими данными, нам нужно будет передать self
в качестве первого аргумента. Напоминает Python, не находите?
Опуская значение self
, мы объявляем метод, который не связан с какими-то конкретными данными. Можно провести аналогию со статическим методом в классе JavaScript.
Первым делом нужно установить Rust. Нет ничего проще: заходите на сайт https://rustup.rs и загружаете официальный установщик набора инструментальных средств. Это то же, что и nvm, который обычно используется с JavaScript.
Затем вам понадобятся библиотеки — не начинать же всё совсем с нуля. Поэтому точно так же, как мы обзаводимся пакетами Node на JavaScript, мы будем поступать и с пакетами Rust. Зайдите на crates.io, официальное хранилище крейтов, чтобы подробнее разузнать о пакетах на Rust.
Rust универсален, поэтому существует множество ситуаций, где его можно использовать. Есть и сообщество, которое не покладая рук работает, отслеживая их на разных сайтах:
А если вы занимаетесь веб-разработкой, можно сказать, что вам повезло! Вы можете создавать программы, компилировать их и использовать всё это вместе с тем кодом, который у вас на JavaScript. WebAssembly — вот технология, которая сделала это реальным, и её можно использовать прямо сейчас во всех современных браузерах.
Если хотите её опробовать, рекомендую почитать официальную книгу с документацией по Rust и WebAssembly.
Rust — это нереально крутой язык, который стоит освоить, ведь с его помощью столько всего можно сделать! Если вы веб-разработчик, как и я, то вам будет очень интересно читать о WebAssembly, и я надеюсь, что смогу сделать ещё статьи об этом.
Если вы хотите приступить к освоению Rust, рекомендую начать с этого официального ресурса и попробовать написать имеющиеся программы на JavaScript с помощью Rust. Как и во многом другом, практика — это ключ к успеху!
В заключение отметим, что эта статья написана по мотивам доклада, представленного автором на семинаре разработчиков JS Coders meetup event. Со слайдами вы можете ознакомиться здесь.
Перевод статьи David Morcillo: Rust for JS developers
Комментарии