Эта статья — перевод оригинальной статьи Michael Salim "Rust from 0 to 80% for JavaScript Developers"
Также я веду телеграм канал “Frontend по-флотски”, где рассказываю про интересные вещи из мира разработки интерфейсов.
Вступление
Это список тем, которые помогут вам быстро понять Rust, если вы разработчик JavaScript. Есть много учебников, которые начинаются с нуля. Но если вы уже знаете что-то еще, почему бы не сравнить их?
Это различия, на которые я хотел бы сослаться перед тем, как приступить к Rust, вкратце.
Предупреждение!
Я очень далек от того, чтобы хорошо разбираться в Rust. Документация по Rust обширна, поэтому, если вам нужны подробности, поищите их в Google. Эта книга также является хорошей отправной точкой для изучения Rust. Моя цель — перечислить важные вещи, чтобы вы (и я в будущем) могли пропустить общие концепции программирования и сосредоточиться на различиях, основанных на уже известных вам знаниях.
Типы
Rust — типизированный язык, поэтому он ближе к TypeScript. У вас будет гораздо лучший опыт, если вы уже знаете TS.
По большей части синтаксис похож (variable_name: Type). Ура!
snake_case
Ага, не обойти.
Что это за символ?
1. Вопросительный знак (?)
Вы можете встретить (?) после вызова функции, например: my_function()?;.
Нет, это не optional chaning. Это магия обработки ошибок для функций, которые могут дать сбой (функции с ошибкой). Вы часто будете видеть это при работе с асинхронными функциями. Подробнее об этом позже.
2. Восклицательный знак на функциях (!)
Пример: println!("{:?}", my_variable);
Это указывает на то, что это макрос. В JS нет ничего подобного. Макросы — это способ написания кода, который пишет другой код. Вы можете думать об этом как о пользовательском синтаксическом сахаре. Просто используйте его, если пример функции показывает это.
3. Символ (&)
Пример: &my_var
Это способ получить ссылку. Вы встречали это, если использовали языки низкого уровня, такие как C. Подробнее позже.
Синтаксис
Точка с запятой (;) в конце каждой строки обязательна.
Исключение: точка с запятой (;) не обязательна в последней строке функции. В данном случае это сокращение для возврата этой строки.
-
Синтаксис функций немного отличается. Не ахти какое дело.
fn foo(num: i32) -> i32 { 3 // See no.2 // or // return 3; }
Синтаксис декоратора также отличается. Его также называют атрибутами.
Что это за ключевые слова?
struct
Это объект JSON. (Хорошо, может быть, сложнее, но см. документы для этого).
type Person = {
firstName: string;
lastName: string;
};
struct Person {
first_name: String,
last_name: String,
}
trait
impl
Реализация trait. Ближе всего к этому у нас есть классы. Это связь между trait
и типом. я не использовал его
enum
В некотором смысле очень похоже на enum в Typescript. Но вы можете хранить в нем данные. Это довольно изящная и довольно важная концепция для понимания асинхронности.
Console.log
Не так просто, к сожалению. Больше похоже на printf из других языков
println!("{:?}", my_variable);
Library/Dependencies
Используйте Cargo.toml вместо package.json. Вам надо будет добавить их вручную (вместо использования такой команды, как yarn add)
Пример:
[dependencies]
chrono = "0.4"
egg-mode = "0.16.0"
Импорт
В Rust есть модули. Они сильно отличается от JS, но в основном:
Это что-то вроде пространств имен. Вот разбивка по импорту зависимости
use rocket::serde::{json::Json, Deserialize, Serialize};
use
- используется вместоimport
rocket
- это название пакета
::
- доступ к модулю
serde
- это название модуля
{json::Json, Deserialize, Serialize}
- штуки, которые нужно импортировать
Еще немного синтаксиса:
use chrono::prelude::*;
use rusqlite::Result;
Импортирование из локальных файлов
Лучшее объяснение: https://doc.rust-lang.org/rust-by-example/mod/split.html
Используйте mod
для пути/файла, который вы хотите импортировать, чтобы компилятор включил модуль.
Затем используйте use
для импорта. Примечание: mod
также автоматически импортирует его. В этом случае вам понадобится префикс crate
.
Пример:
use crate::your_file_or_module;
Примечание: mod.rs — это специальное имя файла, которое действует как index.js
.
Const vs let
В JavaScript вы бы использовали const большую часть времени, потому что она неизменяемая.
В Rust лучше использовать let. Он не переопределяемый по умолчанию. Если вы хотите, чтобы он был изменяемым, используйте ключевое слово mut. const зарезервированы для фактических констант (поэтому вы не можете вычислить значение из другой переменной)
let immutable_variable = ...;
let mut mutable_variable = ...;
const MY_CONSTANT = "CONSTANT";
Документация библиотек
Если в репозитории Github нет ссылки на страницу документации, вы, вероятно, можете перейти к ней следующим образом:
Асинхронность
Безусловно, две самые запутанные темы — это futures и ownership. Я бы порекомендовал прочитать более полную документацию для них. Сначала поговорим о Future.
Future похоже на Promise. В отличие от JS, в Rust есть тип результата promise/future который называется Result. Он также принимает тип ошибки в дженериках (хотелось бы, чтобы JS имел это). Вы также можете использовать Result без future.
Вызов Future
Стандартной библиотеки обычно не хватает, поэтому вам нужно будет импортировать что-то еще (например, bluebird для JS). Вам нужен исполнитель, чтобы управлять Future. Я рекомендую использовать tokio и читать их документацию.
.await чтобы подождать функцию
async_function().await;
Интересный синтаксис, да? На самом деле он довольно приятный, так как вам не нужно оборачивать его скобками, как в JS.
Управление Result
Это еще один важный момент. Rust безопасен, поэтому вам нужно будет обработать все ошибки. Да, обязательно все случаи ошибок в отличие от JS!
В Result enum
есть Ok
и Err
. Если Future успешно, оно возвращает Ok
, иначе Err
.
Наиболее полный способ обработки обоих случаев:
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
В приведенном выше примере используется синтаксис match, который тоже хорош.
Но он довольно многословный, поэтому есть 2 распространенных способа сократить его:
Используя
.unwrap()
Пример: let my_value = async_function().await.unwrap();
Он получает значение успеха или кидает ошибку, если Err
Используйте этот способ только тогда, когда вы уверены, что это не приведет к ошибке или только в тестовой среде.
Используя
?
синтаксис
Это передает ошибку вверх. Таким образом, ваша функция также должна иметь возможность возвращать ошибку (либо Result, либо Option).
Глянь этот пример и его эквивалент
Ownership и References
Слышали о borrow checker? Здесь мне особо нечего сказать. Это самая сложная вещь в этом списке, поскольку она уникальна для Rust. И если вы никогда раньше не работали со ссылками, эта тема может быть немного сложной.
К счастью, книга о Rust снова спасает положение.
Заключение
Этот список на самом деле короче, чем я ожидал. Я надеюсь, что это вам поможет.
Комментарии (13)
ushankax
19.05.2022 09:32+2Кстати, книга есть и на русском: https://doc.rust-lang.ru/book/
Статья сводится к тому, что JS и Rust не похожи, в расте много принципиально других вещей, поэтому почитайте книгу )
afanasiyz
19.05.2022 10:39+1Да, я ещё бы порекомендовал вот этот цикл задач, они привязаны к учебнику и на практике позволяют то, что только что прочитано применить - https://github.com/rust-lang/rustlings
DarkEld3r
19.05.2022 10:59+7println!("{:?}", my_variable);
Можно вот так:
println!("{my_variable:?}");
Вам надо будет добавить их вручную (вместо использования такой команды, как yarn add)
Можно использовать
cargo add
(установивcargo-edit
).
SergeiMinaev
19.05.2022 13:40+1точка с запятой (;) не обязательна в последней строке функции. В данном случае это сокращение для возврата этой строки
"Возврат строки" - как-то странно звучит. Точка с запятой меняет поведение. Она ставится после инструкций (statement) и не ставится после выражений (expression). Выражения возвращают значение, а инструкции - нет.
"abc"
- выражение,let a = "abc";
- инструкция. Поэтому,"abc"
в расте равноценноreturn "abc"
. Точка с запятой подавляет возвращение значения, поэтому если написать"abc";
, то значение выражения"abc"
возвращено не будет.
Кстати, получается, что точка с запятой после return является исключением, ведь она не мешает возвращению значения.mayorovp
19.05.2022 14:03+1Кстати, получается, что точка с запятой после return является исключением, ведь она не мешает возвращению значения.
Исключением тут является не точка с запятой после return, а сам оператор return (кстати, он не один такой). Проще всего это увидеть на следующем примере:
fn foo() -> i32 { let bar = { return 5; }; }
Здесь оператор return возвращает значение из функции foo, но это ничуть не мешает переменной bar принять тип
()
PROgrammer_JARvis
19.05.2022 22:49+1Не совсем:
Тип выражения{ return 5; }
--!
(он же never-type).Другое дело, что never-type, формально, приводим к любому типу и, действительно,
fn foo() -> i32 { let bar: () = { return 1; }; }
также скомпилируется.
eee
19.05.2022 13:43-1Наверно самый бесполезный (даже вредный) способ учить Rust )
JavaScript-разработчику его знания не помогут практически ничем в освоении Rust-а.
orekh
20.05.2022 14:06+2Ну нет. Преувеличиваете. Если знаешь один язык, то следующие изучаются проще.
Другое дело, что статья не помогает решать трудности возникающие с переходом. Думаю, что привыкший скриптухе сразу столкнется с вопросами типа как положить в вектор объекты разных типов и работать с ними, как замыкание положить в объект, как добавить поле в объект после создания... ну подобное, что может заставить переписать первые программы несколько раз чтобы они лишь заработали.
amarao
19.05.2022 13:55+4Вот теперь я перестал понимать Rust. Вроде, начали проклёвываться зёрна, и я даже чуть-чуть освоился с арифметрикой на трейтах, но вот после вашего введения Rust выглядит странным, загадочным и непонятным.
Koyanisqatsi
Скорее "Rust с 0 до 0% для JavaScript разработчиков".
dopusteam
Я даже немного в минус ушёл