Эта статья — перевод оригинальной статьи 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 нет ссылки на страницу документации, вы, вероятно, можете перейти к ней следующим образом:
![](https://habrastorage.org/getpro/habr/upload_files/79d/d14/6da/79dd146da4e4d9f197258b5b5c0b1900.png)
![](https://habrastorage.org/getpro/habr/upload_files/3c9/927/a7d/3c9927a7dd958c6179e70216fde5b4f8.png)
Асинхронность
Безусловно, две самые запутанные темы — это 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 снова спасает положение.
Заключение
Этот список на самом деле короче, чем я ожидал. Я надеюсь, что это вам поможет.