JavaScript

Rust не вызвал у меня большого интереса, когда я впервые прочитал о нём. Это было около двух лет назад. Я работал веб-разработчиком, программировал в основном на JavaScript и подумал тогда, что Rust не для меня, потому что в тот момент он казался мне очень сложным.

А в начале этого года я решил начать изучать его. Что изменилось за это время? Я всё ещё веб-разработчик, но понимание того, что, освоив этот язык программирования, я смогу написать программу на Rust, скомпилировать её на WebAssembly и выполнить в браузере, было той искрой, которая зажгла мою мотивацию.

В этой статье я представлю Rust с точки зрения разработчика JavaScript, одновременно сравнивая эти два языка. Надеюсь, что после прочтения статьи вам тоже захочется освоить Rust!

Что такое Rust?

Язык программирования Rust был создан компанией Mozilla, а его первая стабильная версия появилась на свет примерно в 2015 году. Hello, World на Rust выглядит так:

fn main() {
    println!("Hello, World!");
}

И совсем вроде не страшно. Можно даже сказать, что выглядит она почти как JavaScript, но это всего лишь программа hello world, а сама версия немного сложнее! Прежде чем переходить к функциональным средствам языка, давайте определим, какое место занимает Rust в окружении других языков программирования:

Между языками программирования существует чёткое разграничение:

  • Низкоуровневые: это такие языки, как C++. Они позволяют иметь доступ к управлению памятью, считаются низкоуровневыми, и они очень быстрые. Но и безопасность их тоже на низком уровне, потому что, имея доступ к памяти, очень легко можно понаделать ошибок, и такое случается!
  • Высокоуровневые: с другой стороны, существуют такие языки, как JavaScript. Они не позволяют иметь такой детализированный доступ к памяти (здесь есть сборщик мусора, который делает всё за нас) и считаются безопасными языками, поэтому иногда они могут быть медленными.

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?

Первым делом нужно установить Rust. Нет ничего проще: заходите на сайт https://rustup.rs и загружаете официальный установщик набора инструментальных средств. Это то же, что и nvm, который обычно используется с JavaScript.

Затем вам понадобятся библиотеки  —  не начинать же всё совсем с нуля. Поэтому точно так же, как мы обзаводимся пакетами Node на JavaScript, мы будем поступать и с пакетами Rust. Зайдите на crates.io, официальное хранилище крейтов, чтобы подробнее разузнать о пакетах на Rust.

Rust универсален, поэтому существует множество ситуаций, где его можно использовать. Есть и сообщество, которое не покладая рук работает, отслеживая их на разных сайтах:

  • www.arewewebyet.org: несмотря на то, что платформы не достигли пока такой зрелости, как Ruby on Rails, кое-что сделать с их помощью вы бы могли! Рекомендую обратить внимание на платформу Rocket, если вы желаете заняться веб-разработкой. Вы даже можете создавать GraphQL API с помощью Juniper!
  • www.arewegameyet.com: полностью освоив управление памятью, можно переходить к созданию игр, Rust отлично для этого подходит! Если вас манит разработка игр, рекомендую игровой движок Amethyst.
  • www.arewelearningyet.com: машинное обучение. Это ещё одна тема, которая сейчас очень популярна. Экосистема Rust пока ещё не укомплектована полностью и прямо сейчас не может на равных конкурировать с Python в том, что касается машинного обучения, но если вам интересна эта тема, зайдите на сайт!

А если вы занимаетесь веб-разработкой, можно сказать, что вам повезло! Вы можете создавать программы, компилировать их и использовать всё это вместе с тем кодом, который у вас на JavaScript. WebAssembly — вот технология, которая сделала это реальным, и её можно использовать прямо сейчас во всех современных браузерах.

Если хотите её опробовать, рекомендую почитать официальную книгу с документацией по Rust и WebAssembly.

Заключение

Rust — это нереально крутой язык, который стоит освоить, ведь с его помощью столько всего можно сделать! Если вы веб-разработчик, как и я, то вам будет очень интересно читать о WebAssembly, и я надеюсь, что смогу сделать ещё статьи об этом.

Если вы хотите приступить к освоению Rust, рекомендую начать с этого официального ресурса и попробовать написать имеющиеся программы на JavaScript с помощью Rust. Как и во многом другом, практика — это ключ к успеху!

В заключение отметим, что эта статья написана по мотивам доклада, представленного автором на семинаре разработчиков JS Coders meetup event. Со слайдами вы можете ознакомиться здесь.

Читайте также:


Перевод статьи David Morcillo: Rust for JS developers

Предыдущая статьяСпособы публикации библиотеки JavaScript: CDN, NPM, GitHub
Следующая статьяУглубление в параметры ядра. Часть 1: загрузочные параметры