Rust

“Технологии прошлого спасут будущее от себя самого”, — так Грэйдон Хор, создатель Rust, пояснял, чего хотел достичь.

Одна их ключевых черт Rust — использование технологий, хорошо знакомых академической среде, но редко применяемых в современных языках программирования. Старые, надёжные и иногда забытые технологии, но, прежде всего, крайне хорошо работающие.

Эти технологии используются в основном для одной цели: безопасность.

Звучит скучно? Нет, если вы спросите у сообщества. Согласно опросу StackOverflow, с 2016 года впечатляющие 86,1% разработчиков на Rust предпочитают этот язык остальным, что делает его самым любимым языком уже пятый год подряд.

Бытует мнение, что разработчики программного обеспечения — одни из самых прогрессивных людей на этой планете. Однако Rust является полной противоположностью мантре “действуй быстро и всё ломай”. Тем не менее, любой разработчик почти наверняка изучит концепции, о которых никогда не слышал ранее.

От новизны системного программирования для некоторых разработчиков над алгебраическими типами данных до собственного подхода Rust к безопасности памяти — каждый может найти что-то новое и крайне полезное для изучения. Столько причин полюбить Rust!

Более безопасный доступ к памяти без сборки мусора

Безопасное и эффективное управление памятью компьютера является одной из самых сложных задач для любого языка программирования. В Python, например, есть сборщик мусора, который постоянно ищет неиспользуемую память и очищает её в процессе работы программы. 

В других языках, таких как C и C++, программисты должны явно выделять и освобождать память в процессе работы. Поскольку проблемы, связанные с памятью, устраняются до запуска программы, этот подход лучше с точки зрения оптимизации производительности. 

С другой стороны, память — это ещё одна вещь, о которой разработчикам приходится думать постоянно. Это одна из причин, по которым написание программы на C занимает больше времени, чем на Python, даже если результат в итоге одинаков.

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

По сути, владение — это совокупность трёх правил:

  1. У каждого значения в Rust есть переменная, называемая владельцем. 
  2. Единовременно может быть только один владелец.
  3. Когда владелец выходит за пределы области видимости, значение теряется, а память освобождается.

Простой пример присвоения вектора в Rust:

fn main() {     
    let a = vec![1, 2, 3];     
    let b = a;                 
    println!("a: {:?}", b); 
}

На второй строчке создаётся вектор [1, 2, 3] с владельцем a, затем владение переходит к b. Поскольку верный владелец вызывается в операторе печати, при выполнении программа компилирует и возвращает ожидаемый результат:

a: [1, 2, 3]

С другой стороны, можно попробовать вызвать вектор с предыдущим владельцем, a:

fn main() {
    let a = vec![1, 2, 3];
    let b = a;
    println!("a: {:?}", b, a);
}

В этом случае компилятор выбросит ошибку, потому что a был отброшен в третьей строчке. Здесь намного больше тонкостей, но основная идея такова.

Python же будет работать во втором случае. Его сборщик мусора отбросит a только после последнего вызова, что удобно для разработчика, но невыгодно с точки зрения объёма памяти.

В C всё будет несколько сложнее: вам нужно выделить место в памяти для a, затем поставить указатель на вектор, потом выделить больше места в памяти для b, перенаправить b на a, и, наконец, освободить пространство, занимаемое a и b, когда вы закончите.

С этой точки зрения подход Rust к работе с памятью — это компромисс между скоростью разработки и производительностью. Хотя писать на нём не так легко, как на Python, Rust не настолько неуклюж, как C, если вы хорошо понимаете концепцию владения.

С другой стороны, его эффективность поразительна: команде разработчиков Tilde, например, удалось сократить использование памяти на 90 процентов после того, как они переписали часть JavaHTTP на Rust.

Приятная статическая типизация

Между сторонниками динамической и статической типизаций существует длительная вражда. Хотя создавать программное обеспечение проще на языках с динамической типизацией, код довольно быстро становится трудно поддерживать. И это одна из причин, почему код на Python может быть довольно сложным в обслуживании, в сравнении с тем же C.

С другой стороны, необходимость объявлять тип каждой переменной в C раздражает. Если вы когда-либо пытались использовать double в функции, которая возвращает тип float в C, вы поймёте, о чем речь.

Rust выбирает срединный путь: это система статического типа, но от разработчика требуется определять типы только аргументов высшего уровня, таких как аргументы функции и константы. Внутри тел функций допускается вывод типов в стиле Python.

Одна особо полезная фича Rust — наличие типа None. Она позволяет обрабатывать исключения на этапе компиляции, следовательно, программа гарантированно работает без сбоев для конечного пользователя. Взгляните на этот пример, где мы можем получить полное имя человека независимо от того, есть ли у него второе имя:

fn get_full_name(fname: &str, mname: Option<&str>, lname: &str) -> String { 
    match mname {
        Some(n) => format!("{} {} {}", fname, n, lname),
        None => format!("{} {}", fname, lname),
    } 
}

fn main() {
    println!("{}", get_full_name("Ronald", None, "McDonald"));
    println!("{}", get_full_name("Dwight", Some("P."), "Eisenhower"));
}

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

Изящный подход к системному программированию

В то время как Python — язык программирования общего назначения, Rust, как и C, определённо предназначен для системного программирования. И хотя Rust не идеальный язык для создания приложений для конечных пользователей, он прекрасно подходит для разработки частей ПО, предоставляющих сервисы другим программам.

Таким образом, эффективность заложена в основу Rust. Лучшей демонстрацией этого являются абстракции с нулевой стоимостью, интерпретирующие код при минимальном использовании памяти. Как сказал Бьёрн Страуструп, создатель C++: “Вы не платите за то, что не используете. Вы используете то, что уже написано в коде”.

Например, рассмотрим добавление всех целых чисел до 1000 в Python:

sum(range(1000))

Этот код выполняет 1000 итераций и дополнений при каждом запуске — это значительно замедляет его выполнение. Сравним с Rust:

(0..1000).sum()

Этот код компилируется до постоянной 499500. Использование памяти сокращается в 1000 раз.

Хотя эти абстракции существуют и в C, Rust также активно их использует, а одна из его целей — добавить в язык как можно больше абстракций с нулевой стоимостью. С этой точки зрения, Rust — это C следующего уровня.

C существует более 40 лет, и Rust стремится к тому же, уделяя особое внимание обратной совместимости. Даже сейчас всё ещё можно запустить код, написанный на Rust 1.0. И точно так же написанный сегодня код можно будет запустить двадцать лет спустя. Rust не ржавеет!

Небольшое, но восхитительное сообщество

Учитывая особое внимание Rust к безопасности и самодостаточности, неудивительно, что Dropbox переписал большую часть основной структуры на Rust. Разработчики из Mozilla, первого большого спонсора этого языка, написали на нём жизненно важные части Firefox. Microsoft признал C и C++ не безопасными для критически важного ПО и всё больше инвестирует в Rust.

Так происходит не только в крупных корпорациях — любовь к Rust свойственна и отдельным разработчикам. Несмотря на то, что его используют только 5% разработчиков, они относятся к языку с огромным энтузиазмом.

И тому есть причины. Хорошо продуманы не только спецификация языка и компилятор. У Rust есть rustup для установки и управления пакетами инструментальных средств. Есть Cargo, инструмент командной строки, поставляемый с каждой установкой Rust и помогающий управлять зависимостями, запускать тесты и создавать документацию. 

Существует crates.io, где пользователи могут делиться библиотеками, и docs.rs , где они документируются. Есть проверки компилятора от Clippy и автоматическое форматирование от rustfmt.

Помимо этого, существуют официальные и неофициальные чаты, субреддиты, пользовательские форумы, вопросы на StackOverflow и конференции по всему миру. Чего ещё желать в сообществе, которое ставит во главу угла дружелюбие?

Минусы: необходимость бежать, прежде чем вы научитесь ходить

Одна из демотивирующих вещей в Rust — высокий порог входа. В то время как для начала продуктивной работы с большинством языков требуется 1–2 дня, для Rust скорее всего потребуется одна или две недели.

Это связано с необходимостью осваивать множество новых концепций, не используемых другими языками, а также с тем фактом, что во время компиляции возникает множество ошибок. Все исключения потребуется обрабатывать сразу, вы не сможете просто написать работающий код и добавить их позже, как в том же Python.

Вдобавок, поскольку Rust всё ещё довольно нов, в нём есть не все библиотеки, которые могут понадобиться. Помимо официальной документации и различных вопросов на StackOverflow, существует не так много руководств.

Хорошая новость заключается в том, что как только вы изучите концепции и скомпилируете программу, всё пойдёт как по маслу. Кроме того, благодаря обратной совместимости, программы будут работать и через двадцать лет.

Учитывая устойчивость кода и тот факт, что Rust поддерживается рядом крупных корпораций, стоит потратить на его изучение одну-две недели, не взирая на недостатки.

Итоги: осваивайте без страха

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

Для каждого развивающегося языка, вероятно, найдётся место: Go всё больше и больше заполняет пространство Python и Java, Julia наступает на пятки Python в науке о данных, а Rust растёт в области Python и C++. Что делает Rust особенным, так это впечатляющее сообщество, инновационные функции и тот факт, что он рассчитан на многолетнюю работу.

Конечно, многое ещё предстоит сделать, и только часть работы может быть выполнена на Rust. У современных языков есть все шансы закрепиться на некоторое время, даже если в ближайшие годы появятся другие новинки. Но если бы пришлось делать ставки на один язык, то Rust был бы беспроигрышным вариантом.

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

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Rhea Moutafis: Thought you loved Python? Wait until you meet Rust