Раст язык программирования. Rust: начинаем работать с новым языком программирования от специалистов компании Mozilla

09.10.2021 Android



На сегодняшний день синтаксис Rust поддерживается в vim и emacs с помощью поставляемых вместе с компилятором синтаксических файлов.
Имеются также синтаксические пакеты для популярного проприетарного редактора Sublime Text 2 и свободного редактора Kate. Поддержки Rust в IDE пока нет. Поддержка отладчиков, судя по всему, тоже отсутствует.

Вместе с компилятором rustc поставляются следующие утилиты:
> rustdoc - утилита для автоматической генерации документации из исходного кода наподобие Doxygen;
> rustpkg - менеджер пакетов, позволяющий легко устанавливать дополнительные пакеты и библиотеки;
> rusti - так называемая REPL-утилита (read-eval-print-loop). По сути это тестовый интерпретатор, который принимает выражение на Rust из командной строки, компилирует его во внутреннее представление LLVM, выполняет и выводит результат;
> rust - универсальная утилита, запускающая другие утилиты или компилятор в зависимости от параметров. У меня она так и не заработала.

Вся доступная документация по языку собрана на официальном сайте www.rust-lang.org. Имеется подробное руководство (http://static.rust-lang.org/doc/tutorial.html) - исчерпывающая формальная документация по всем нюансам синтаксиса, модели памяти, системе времени выполнения и т.п., а также документация по встроенной библиотеке core и стандартной библиотеке std. Вся документация англоязычная. На русском языке актуальных материалов нет, а пара имеющихся обзорных статей уже успели сильно устареть.

Идеология и синтаксис


Rust относится к Си-подобным языкам, использующим фигурные скобки для выделения блоков кода. Язык является «мультипарадигменным», т.е. позволяет писать код в императивно-процедурной, объектно-ориентированной, конкурентной или функциональной манере. Rust компилируется в нативный бинарный код на любой поддерживаемой платформе (использует LLVM в качестве бекэнда). В теории код на Rust не должен уступать в скорости коду на C/C++. Rust позиционируется как системный язык, однако в нем нет встроенной поддержи блоков кода на ассемблере как в «истинных» системных языках С, С++ или D.

Модель памяти Rust изначально не допускает появления нулевых или «висячих» указателей и переполнений буфера. Имеется опциональный сборщик мусора, работающий только в пределах одной нити кода. У языка есть встроенная поддержка легковесной многозадачности и коммуникаций между нитями с помощью обмена сообщениями. Разделяемой памяти (shared memory) в Rust не существует в принципе. Все переменные подразделяются на стековые, переменные кучи для данного потока, и переменные так называемой «обменной» кучи, которые могут читаться всеми потоками, но не могут ими изменяться. Это автоматически исключает «заклинивание» (deadlock), которое считается бичом многопоточного программирования. ABI языка совместим с Си, поэтому программы на Rust могут компоноваться с библиотеками, написанными на Си без дополнительных оберток. Для нужд низкоуровневого системного программирования и для обеспечения совместимости с Си в языке есть особый «небезопасный» режим без проверки корректности указателей. По своей идеологии Rust ближе всего к языку Go. Так же, как и в Go, основной акцент сделан на простоте многопоточного программирования и скорости разработки масштабных приложений, а синтаксис местами так же непривычен и в чем-то удивителен. В то же время Rust не настолько минималистичен, как Go, и претендует на роль системного языка.

Синтаксис Rust большей частью заимствован из С и С++ с примесью идей из языков Go, C#, Haskell, Python и Ruby. Не буду исчерпывающе описывать синтаксис языка, а остановлюсь только на наиболее интересных концепциях.

Rust - новый экспериментальный язык программирования, разрабатываемый Mozilla. Язык компилируемый и мультипарадигмальный, позиционируется как альтернатива С/С++, что уже само по себе интересно, так как даже претендентов на конкуренцию не так уж и много. Можно вспомнить D Вальтера Брайта или Go от Google.
В Rust поддерживаются функицональное, параллельное, процедурное и объектно-ориентированное программирование, т.е. почти весь спектр реально используемых в прикладном программировании парадигм.

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

Первое впечатление

Синтаксис языка строится в традиционном си-подобном стиле (что не может не радовать, так как это уже стандарт де-факто). Естественно, всем известные ошибки дизайна С/С++ учтены.
Традиционный Hello World выглядит так:
use std; fn main(args: ) { std::io::println("hello world from " + args + "!"); }

Пример чуть посложнее - функция расчета факториала:

Fn fac(n: int) -> int { let result = 1, i = 1; while i <= n { result *= i; i += 1; } ret result; }

Как видно из примера, функции объявляются в «функциональном» стиле (такой стиль имеет некоторые преимущества перед традиционным «int fac(int n)»). Видим автоматический вывод типов (ключевое слово let), отсутствие круглых скобок у аргумента while (аналогично Go). Еще сразу бросается в глаза компактность ключевых слов. Создатели Rust дейтсвительно целенаправленно сделали все ключевые слова как можно более короткими, и, скажу честно, мне это нравится.

Мелкие, но интересные синтаксические особенности

  • В числовые константы можно вставлять подчеркивания. Удобная штука, сейчас эту возможность добавляют во многие новые языки.
    0xffff_ffff_ffff_ffff_ffff_ffff
  • Двоичные константы. Конечно, настоящий программист должен преобразовывать bin в hex в уме, но ведь так удобнее! 0b1111_1111_1001_0000
  • Тела любых операторов (даже состоящие из единственного выражения) должны быть обязательно заключены в фигурные скобки. К примеру, в Си можно было написать if(x>0) foo(); , в Rust нужно обязательно поставить фигурнные скобки вокруг foo()
  • Зато аргументы операторов if, while и подобных не нужно заключать в кругные скобки
  • во многих случаях блоки кода могут рассматриваться как выражения. В частности, возможно например такое:
    let x = if the_stars_align() { 4 } else if something_else() { 3 } else { 0 };
  • синтаксис объявления функций - сначала ключевое слово fn, затем список аргументов, тип аргумента указывается после имени, затем, если функция возвращает значение - стрелочка "->" и тип возвращаемого значения
  • аналогичным образом объявляются переменные: ключевое слово let, имя переменной, после переменной можно через двоеточие уточнить тип, и затем - присвоить начальное значение.
    let count: int = 5;
  • по умолчанию все переменные неизменяемые; для объявления изменяемых переменных используется ключевое слово mutable.
  • имена базовых типов - самые компактные из всех, которые мне встречались: i8, i16, i32, i64, u8, u16, u32, u64,f32, f64
  • как уже было сказано выше, поддерживается автоматический вывод типов
В языке присутствую встроенные средства отладки программ:
Ключевое слово fail завершает текущий процесс
Ключевое слово log выводит любое выражение языка в лог (например, в stderr)
Ключевое слово assert проверяет выражение, и если оно ложно, завершает текущий процесс
Ключевое слово note позволяет вывести дополнительную инфорацию в случае аварийного завершения процесса.

Типы данных

Rust, подобно Go, поддерживает структурную типизацию (хотя, по утверждению авторов, языки развивались независимо, так что это влияние их общих предшественников - Alef, Limbo и т.д.). Что такое структурная типизация? Например, у вас в каком-то файле объявлена структура (или, в терминологии Rust, «запись»)
type point = {x: float, y: float};
Вы можете объявить кучу переменных и функций с типами аргументов «point». Затем, где-нибудь в другом месте, вы можете объявить какую-нибудь другую структуру, например
type MySuperPoint = {x: float, y: float};
и переменные этого типа будут полностью совместимы с переменными типа point.

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

Структуры в Rust называются «записи» (record). Также имеются кортежи - это те же записи, но с безымянными полями. Элементы кортежа, в отличие от элементов записи, не могут быть изменяемыми.

Имеются вектора - в чем-то подобные обычным массивам, а в чем-то - типу std::vector из stl. При инициализации списком используются квадратные скобки, а не фигурные как в С/С++

Let myvec = ;

Вектор, тем ни менее - динамическая структура данных, в частности, вектора поддерживают конкатенацию.

Let v: mutable = ; v += ;

Есть шаблоны. Их синтаксис вполне логичен, без нагромождений «template» из С++. Поддерживаются шаблоны функций и типов данных.

Fn for_rev(v: [T], act: block(T)) { let i = std::vec::len(v); while i > 0u { i -= 1u; act(v[i]); } } type circular_buf = {start: uint, end: uint, buf: };

Язык поддерживает так называемые теги . Это не что иное, как union из Си, с дополнительным полем - кодом используемого варианта (то есть нечто общее между объединением и перечислением). Или, с точки зрения теории - алгебраический тип данных.

Tag shape { circle(point, float); rectangle(point, point); }

В простейшем случае тег идентичен перечислению:

Tag animal { dog; cat; } let a: animal = dog; a = cat;
В более сложных случаях каждый элемент «перечисления» - самостоятельная структура, имеющая свой «конструктор».
Еще интересный пример - рекурсивная структура, с помощью которой задается объект типа «список»:
tag list { nil; cons(T, @list); } let a: list = cons(10, @cons(12, @nil));
Теги могут участвовать в выражениях сопоставления с образцом, которые могут быть достаточно сложными.
alt x { cons(a, @cons(b, _)) { process_pair(a,b); } cons(10, _) { process_ten(); } _ { fail; } }

Сопоставление с образцом (pattern matching)

Для начала можно рассматривать паттерн матчинг как улучшенный switch. Используется ключевое слово alt, после которого следует анализируемое выражение, а затем в теле оператора - паттерны и действия в случае совпадения с паттернами.
alt my_number { 0 { std::io::println("zero"); } 1 | 2 { std::io::println("one or two"); } 3 to 10 { std::io::println("three to ten"); } _ { std::io::println("something else"); } }
В качестве «паттеронов» можно использовать не только константы (как в Си), но и более сложные выражения - переменные, кортежи, диапазоны, типы, символы-заполнители (placeholders, "_"). Можно прописывать дополнительные условия с помощью оператора when, следующего сразу за паттерном. Существует специальный вариант оператора для матчинга типов. Такое возможно, поскольку в языке присутствует универсальный вариантный тип any , объекты которого могут содержать значения любого типа.

Указатели. Кроме обычных «сишных» указателей, в Rust поддерживаются специальные «умные» указатели со встроенным подсчетом ссылок - разделяемые (Shared boxes) и уникальные (Unique boxes). Они в чем-то подобны shared_ptr и unique_ptr из С++. Они имеют свой синтаксис: @ для разделяемых и ~ для уникальных. Для уникальных указателей вместо копирования существует специальная операция - перемещение:
let x = ~10; let y <- x;
после такого перемещения указатель x деинициализируется.

Замыкания, частичное применение, итераторы

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

1. Ключевое слово lambda используется для объявления вложенной функции или функционального типа данных.

Fn make_plus_function(x: int) -> lambda(int) -> int { lambda(y: int) -> int { x + y } } let plus_two = make_plus_function(2); assert plus_two(3) == 5;

В этом примере мы имеем функцию make_plus_function, принимающую один аргумент «x» типа int и возвращающую функцию типа «int->int» (здесь lambda - ключевое слово). В теле функции описывается эта самая фунция. Немного сбивает с толку отсутствие оператора «return», впрочем, для ФП это обычное дело.

2. Ключевое слово block используется для объявления функционального типа - аргумента функции, в качестве которого можно подставить нечто, похожее на блок обычного кода.
fn map_int(f: block(int) -> int, vec: ) -> { let result = ; for i in vec { result += ; } ret result; } map_int({|x| x + 1 }, );

Здесь мы имеем функцию, на вход которой подается блок - по сути лямбда-функция типа «int->int», и вектор типа int (о синтаксисе векторов далее). Сам «блок» в вызывающем коде записыавется с помощью несколько необычного синтаксиса {|x| x + 1 }. Лично мне больше нравятся лямбды в C#, символ | упорно воспринимается как битовое ИЛИ (которое, кстати, в Rust также есть, как и все старые добные сишные операции).

3. Частичное применение - это создание функции на основе другой функции с большим количеством аргументов путем указания значений некоторых аргументов этой другой функции. Для этого используется ключевое слово bind и символ-заполнитель "_":

Let daynum = bind std::vec::position(_, ["mo", "tu", "we", "do", "fr", "sa", "su"])

Чтобы было понятнее, скажу сразу, что такое можно сделать на обычном Си путем создания простейшей обертки, как-то так:
const char* daynum (int i) { const char *s ={"mo", "tu", "we", "do", "fr", "sa", "su"}; return s[i]; }

Но частичное применение - это функциональный стиль, а не процедурный (кстати, из приведенного примера неясно, как сделать частичное применение, чтобы получить функцию без аргументов)

Еще пример: объявляется функция add с двумя аргументами int, возвращающая int. Далее объявляется функциональный тип single_param_fn, имеющий один аргумент int и возвращающий int. С помощью bind объявляются два функциональных объекта add4 и add5, построенные на основе функции add, у которой частично заданы аргументы.

Fn add(x: int, y: int) -> int { ret x + y; } type single_param_fn = fn(int) -> int; let add4: single_param_fn = bind add(4, _); let add5: single_param_fn = bind add(_, 5);

Функциональные объекты можно вызывать также, как и обычные функции.
assert (add(4,5) == add4(5)); assert (add(4,5) == add5(4));

4. Чистые функции и предикаты
Чистые (pure) функции - это функции, не имеющие побочных эффектов (в том числе не вызывающие никаких других функций, кроме чистых). Такие функции выдяляются ключевым словом pure.
pure fn lt_42(x: int) -> bool { ret (x < 42); }
Предикаты - это чистые (pure) функции, возвращающие тип bool. Такие функции могут использоваться в системе typestate (см. дальше), то есть вызываться на этапе компиляции для различных статических проверок.

Синтаксические макросы
Планируемая фича, но очень полезная. В Rust она пока на стадии начальной разработки.
std::io::println(#fmt("%s is %d", "the answer", 42));
Выражение, аналогичное сишному printf, но выполняющееся во время компиляции (соответственно, все ошибки аргументов выявляются на стадии компиляции). К сожалению, материалов по синтаксическим макросам крайне мало, да и сами они находятся в стадии разработки, но есть надежда что получится что-то типа макросов Nemerle .
Кстати, в отличие от того же Nemerle, решение выделить макросы синтаксически с помощью символа # считаю очень грамотным: макрос - это сущность, очень сильно отличающаяся от функции, и я считаю важным с первого взгляда видеть, где в коде вызываются функции, а где - макросы.

Атрибуты

Концепция, похожая на атрибуты C# (и даже со схожим синтаксисом). За это разработчикам отдельное спасибо. Как и следовало ожидать, атрибуты добавляют метаинформацию к той сущности, которую они аннотируют,
# fn register_win_service() { /* ... */ }
Придуман еще один вариант синтаксиса атрибутов - та же строка, но с точкой с запятой в конце, аннотирует текущий контекст. То есть то, что соответствует ближайшим фигурным скобкам, охватывающим такой атрибут.
fn register_win_service() { #; /* ... */ }

Параллельные вычисления

Пожалуй, одна из наиблее интересных частей языка. При этом в tutorial на данный момент не описана вообще:)
Программа на Rust состоит из «дерева задач». Каждая задача имеет функцию входа, собственный стек, средства взаимодействия с другими задачами - каналы для исходящей информации и порты для входящей, и владеет некоторой частью объектов в динамической куче.
Множество задач Rust могут существовать в рамках одного процесса операционной системы. Задачи Rust «легковесные»: каждая задача потребляет меньше памяти чем процесс ОС, и переключение между ними осуществляется быстрее чем переключение между процессами ОС (тут, вероятно, имеются в виду все-же «потоки»).

Задача состоит как минимум из одной функции без аргументов. Запуск задачи осуществляется с помощью функции spawn. Каждая задача может иметь каналы, с помощью которых она передает инфорацию другим задачам. Канал - это специальный шаблонный тип chan, параметризируемый типом данных канала. Например, chan - канал для передачи беззнаковых байтов.
Для передачи в канал используется функция send, первым аргументом которой является канал, а вторым - значение для передачи. Фактически эта функция помещает значение во внутренний буфер канала.
Для приема данных используются порты. Порт - это шаблонный тип port, параметризируемый типом данных порта: port - порт для приема беззнаковых байтов.
Для чтения из портов используется функция recv, аргументом которой является порт, а возвращаемым значением - данные из порта. Чтение блокирует задачу, т.е. если порт пуст, задача переходит в состояние ожидания до тех пор, пока другая задача не отправит на связанный с портом канал данные.
Связывание каналов с портами происходит очень просто - путем инициализации канала портом с помощью ключевого слова chan:
let reqport = port();
let reqchan = chan(reqport);
Несколько каналов могут быть подключены к одному порту, но не наоборот - один канал не может быть подключен одновременно к нескольким портам.

Typestate

Общепринятого перевода на русский понятия «typestate» я так и не нашел, поэтому буду называть это «состояния типов». Суть этой фичи в том, что кроме обычного контроля типов, принятого в статической типизации, возможны дополнительные контекстные проверки на этапе компиляции.
В том или ином виде состояния типов знакомы всем программистам - по сообщениям компилятора «переменная используется без инициализации». Компилятор определяет места, где переменная, в которую ни разу не было записи, используется для чтения, и выдает предупреждение. В более общем виде эта идея выглядит так: у каждого объекта есть набор состояний, которые он может принимать. В каждом состоянии для этого объекта определены допустимые и недопустимые операции. И компилятор может выполнять проверки - допустима ли конкретная операция над объектом в том или ином месте программы. Важно, что эти проверки выполняются на этапе компиляции.

Например, если у нас есть объект типа «файл», то у него может быть состояние «закрыт» и «открыт». И операция чтения из файла недопустима, если файл закрыт. В современных языках обычно функция чтения или бросает исключение, или возвращает код ошибки. Система состояний типов могла бы выявить такую ошибку на этапе компиляции - подобно тому, как компилятор определяет, что операция чтения переменной происходит до любой возможной операции записи, он мог бы определить, что метод «Read», допустимый в состоянии «файл открыт», вызывается до метода «Open», переводящего объект в это состояние.

В Rust существует понятие «предикаты» - специальные функции, не имеющие побочных эффектов и возвращающие тип bool. Такие функции могут использоваться компилятором для вызова на этапе компиляции с целью статических проверок тех или иных условий.

Ограничения (constraints) - это специальные проверки, которые могут выполняться на этапе компиляции. Для этого используется ключевое слово check.
pure fn is_less_than(int a, int b) -< bool { ret a < b; } fn test() { let x: int = 10; let y: int = 20; check is_less_than(x,y); }
Предикаты могут «навешиваться» на входные параметры функций таким вот способом:
fn test(int x, int y) : is_less_than(x,y) { ... }

Информации по typestate крайне мало, так что многие моменты пока непонятны, но концепция в любом случае интересная.

На этом все. Вполне возможно, что я все-же пропустил какие-то интересные моменты, но статья и так раздулась. При желании можно уже сейчас собрать компилятор Rust и попробовать поиграться с различными примерами. Информация по сборке приведена на



Здравствуйте, уважаемые читатели!

Жизнь не стоит на месте, и вот в «O"Reilly» задумались об издании первой фундаментальной книги о языке программирования Rust:

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

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

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

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

Барьер при изучении Rust

Я познакомился с Rust не с первой попытки. При изучении этого языка существует несколько барьеров, в частности:

  1. Язык стремительно меняется . В Rust нет «великодушного пожизненного диктатора». Язык развивается благодаря вкладу участников основной команды и сообщества.
  2. Учитывая первый пункт, учебные материалы по Rust очень скудны . Есть руководство , другая официальная документация и сайт Rust by Example – это отличные ресурсы. Однако Rust гораздо сложнее. Зачастую приходится штудировать RFC, блоги и даже комментарии на Github, чтобы отыскать нужную информацию, причем если даже эта информация появилась буквально вчера, все равно в ней нет полной уверенности. Жду хорошей, авторитетной книги по Rust, хотя, готов поспорить, она будет объемистой.
  3. Система владения и механизм проверки заимствования в Rust может смутить новичков . Для обеспечения безопасности памяти без сборки мусора в Rust задействуется затейливая система заимствования и владения. Зачастую она отпугивает неофитов.
  4. Компилятор Rust очень строгий . Я называю Rust дисциплинирующим языком. Все, что не является для компилятора Rust вполне очевидным, вы должны указывать самостоятельно, причем о некоторых своих намерениях вы и сами поначалу не догадываетесь. Этот барьер обучения, вкупе со всеми остальными, зачастую приводит к тому, что первое впечатление от Rust оказывается обескураживающим.

Достоинства

У Rust много достоинств. Некоторые из них уникальны.

Безопасность памяти без сборки мусора

Пожалуй, это наиважнейшее достижение Rust. В низкоуровневых языках программирования, допускающих непосредственную манипуляцию с памятью, такие ошибки, как использование освобожденной памяти (use-after-free) или утечки памяти во время исполнения довольно накладны. В современном языке C++ возможности борьбы с такими вещами улучшились, однако они предполагают строгую техническую дисциплину (читай: программисты продолжают выполнять небезопасные операции). Соответственно, на мой взгляд, в целом С++ не может принципиально и надежно решить эту проблему.

Действительно, Rust-программисты могут писать небезопасный код в блоке unsafe, но (1) это делается осознанно и (2) блоки unsafe могут составлять лишь очень малую долю всей базы кода, при этом они строго контролируются.
Сборщик мусора - самый обычный инструмент, обеспечивающий безопасность памяти. Если вы ладите с GC, то вариантов у вас не мало. Однако система владения, применяемая в Rust, обеспечивает не только безопасность памяти, но и безопасность данных и ресурсов (см. ниже)

RAII и ресурсы

RAII (получение ресурса есть инициализация) - странный термин, но хорошо передает заложенную в нем идею. В Википедии читаем, что RAII работает с объектами, выделяемыми в стеке. Система владения Rust позволяет применять этот принцип и с объектами, выделяемыми в куче. Благодаря этому автоматическое высвобождение ресурсов – например, памяти, файлов, сокетов - становится хорошо предсказуемым и гарантированным во время компиляции.
В динамических языках вроде Python или Ruby есть подобные возможности, но они не сравнятся по мощности с Rust IMO.

Конкурентность без гонок данных

Rust обеспечивает безопасность данных при конкурентном программировании - то есть, гарантирует, что в любой конкретный момент времени обратиться к данным могут лишь множество считывателей или один «записыватель».

Алгебраический тип данных

Кроме обычных типов (кортежи и структуры) в Rust также предоставляются перечислимые типы (здесь они называются «типы-суммы» или «вариантные типы») и сравнение с шаблоном. Удивительно, что у языка для системного программирования такая развитая система типов.

Композиция приоритетнее наследования

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

Недостатки (условные)

Все должно быть предельно четко

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

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

Сборка мусора вторична

В Rust есть простейший сборщик мусора: Rc, подсчет ссылок и Arc, атомарный подсчет ссылок без циклического обнаружения. Однако эти возможности не действуют в языке по умолчанию, и вам придется чаще использовать стандартные механизмы управления памятью в Rust (стек, & и Box). Если проблемы с памятью в вашем приложении несущественны, то вам придется потерпеть Rust-овскую модель безопасности памяти, где не применяется сборщик мусора.

Выразительность - не самоцель

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

Сравнительно высокий входной барьер

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

Rust и другие языки

Динамические языки

Динамические (сценарные) языки находятся на противоположном от Rust конце спектра языков программирования. По сравнению с Rust, писать код на динамических языках обычно быстрее и проще. Думаю, динамические языки выигрывают у Rust в таких ситуациях:

  • Быстрое прототипирование или одноразовые задачи
  • Код не для продакшена, либо такой, где ошибка во время исполнения - небольшая проблема
  • Собственный (индивидуальный) проект
  • Полуавтоматическая работа (напр. парсинг/анализ логов, пакетная обработка текста)

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

  • Работы в средней или крупной команде
  • Кода, ориентированного на долгосрочное использование в продакшене
  • Кода, который будет использоваться долго, предполагает регулярную поддержку и/или рефакторинг
  • Код, для обеспечения безопасности которого вы бы написали много модульных тестов

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

Go

Сравнение двух этих языков – отличный повод поспорить, но, поскольку я некоторое время изучал , все-таки поделюсь здесь моими субъективными впечатлениями о нем. По сравнению с Rust, вот что мне нравится в Go:

  • легковесность – язык маленький (и простой, при этом очень мощный)
  • утилита gofmt – значительно снижает умственную нагрузку при программировании
  • goroutine/channel
  • Мгновенная компиляция

Почему я завязал с Go:

  • Он слишком минималистичен. Система типов и сам язык не слишком расширяемы
  • Программирование на Go кажется мне немного сухим. Напоминает дни, когда я программировал на Java: хорошо подходит для корпоративной разработки, механистично и… не так интересно (напоминаю: о вкусах не спорят)
  • Популярность Go сохраняется благодаря поддержке Google, но это вызывает у меня некоторый скепсис. Когда интересы сообщества и компании не совпадают, первыми могут пожертвовать. Разумеется, любая компания преследует прежде всего собственные интересы. В этом нет ничего плохого. Это просто… немного раздражает. (Многие языки и фреймворки, продвигаемые корпорациями, сталкиваются с подобной проблемой. Mozilla хотя бы не зависит от биржевых котировок).

Nim

Nim (раньше назывался Nimrod) - очень интересный язык. Он компилируется в C, поэтому производительность у него весьма хорошая. Внешне он напоминает Python - язык, программировать на котором мне всегда нравилось. Это язык со сборкой мусора, однако в нем предоставляется мягкая поддержка в реальном времени, и само поведение сборщика мусора более предсказуемое. В нем интересная система эффектов. В принципе, этот язык мне очень нравится.

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

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

Прочие

Есть и другие языки вроде Julia и . Julia - динамический язык с хорошей производительностью и гладкими вызовами в стиле C. (Если вы любите динамические языки и REPL - обратите внимание). Julia привлек всеобщее внимание, благодаря своим числовым и научным полям. Хотя он и может стать языком общего назначения, мне кажется, что на развитие этого языка значительно влияет сообщество его зачинателей.

D, как минимум, изначально, представлял собой попытку создать «C++, только лучше». Его версия 1.0 вышла в 2007 году, поэтому данный язык не такой уж и новый. Это хороший язык, но по объективным причинам он до сих пор не прижился: дело и в расколе на Phobos/Tango на раннем этапе, и в обеспечении безопасности памяти в первую очередь через сборку мусора, и в изначальном позиционировании в качестве замены для C++.

Почему я считаю, что шансы у Rust довольно высоки

В наше время появляется так много новых языков программирования. Чем же, на мой взгляд, среди них выделяется Rust? Приведу следующие доводы:

Настоящий язык для системного программирования

Встраиваемость - нелегкая задача. Пожалуй, она решается буквально на нескольких языках, а то и всего на двух: C и C++. (Возможно, именно поэтому Skylight выбрала Rust для разработки расширения для Ruby, хотя это и было крайне рискованно.) Примечательно, насколько хорошо Rust удалось избавиться от издержек времени исполнения. Это открывает перед Rust уникальные перспективы.

Без Null

Null-объект/указатель (так называемая "ошибка на миллиард долларов ") - распространенный источник ошибок времени исполнения. Существует лишь несколько языков программирования, в которых отсутствует null, в основном это функциональные языки. Дело в том, что для избавления от null требуется очень продвинутая система типов. Как правило, чтобы справиться с этим на синтаксическом уровне языка, требуется алгебраический тип данных и сравнение с шаблоном.

Низкоуровневый язык с продвинутыми высокоуровневыми конструкциями

Будучи вплоть до ядра «языком на голом металле» (как минимум, теоретически), Rust также предлагает множество относительно высокоуровневых возможностей, в том числе, алгебраический тип данных, сравнение с шаблоном, типаж, выведение типов и т.д.

Сильное сообщество и практическая востребованность

Сообщество Rust очень дружелюбное и активное. (Разумеется, это субъективное впечатление). Кроме того, Rust использовался в некоторых серьезных практических проектах - речь, в частности, о компиляторе Rust, Servo , Skylight и т.д. еще на этапе разработки языка.

До сих пор - без крупных ошибок

Временами разработка языка или фреймворка, ведущаяся в рамках компании, может случайно зайти в тупик. К счастью, основная команда Rust пока отлично справляется со своими задачами. Так держать, Rust!

Rust для веб-разработки

Если Rust – язык для системного программирования, подойдет ли он для веб-разработки? Я ищу ответ и на этот вопрос.

Библиотеки и фреймворки

В первую очередь, для этого должны быть готовы некоторые HTTP-библиотеки. (Об этом рассказано на сайте «Are we web yet»). Первая библиотека rust-http уже устарела; ее потенциальная наследница Teepee практически в анабиозе. К счастью, Hyper кажется хорошей кандидатурой. Она уже принята в Servo, симбиотическом проекте Rust, я расцениваю это как благословление на роль HTTP-библиотеки для Rust.

Стандартная библиотека Rust пока не поддерживает асинхронный ввод/вывод. Для этой цели можно использовать внешнюю библиотеку mio , обеспечивающую неблокирующий сокетный ввод/вывод. Поддержка зеленых потоков была упразднена в рамках упрощения I/O.

Активно разрабатывается несколько веб-фреймворков для Rust, в их числе - Iron и nickel.rs . Может потребоваться время, прежде чем ситуация с ними уляжется.

Rust – язык для веба?

Когда-нибудь библиотеки и фреймворки будут готовы. Вопрос в том, подходит ли Rust как таковой для веб-разработки? Не слишком ли сложны низкоуровневые возможности Rust, связанные с управлением памятью и обеспечением безопасности?

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

Годится ли Rust для веб-стартапов?

Что насчет стартапов? На них требуется быстрое прототипирование и циклы разработки. Это более неоднозначный вопрос, но я остаюсь при своем мнении: если вы рассчитываете на долгосрочный проект, то важно правильно подобрать язык программирования, и Rust заслуживает особого внимания. С точки зрения бизнеса язык, обеспечивающий быстрое прототипирование, дает существенные преимущества, а рефакторинг и устранение узких мест всегда можно оставить на потом. Инженерная реальность такова, что стоимость работ по рефакторингу обычно выше, чем кажется, и даже если вы перетряхнете массу элементов вашей системы, то код, написанный когда-то давно, все равно сохранится в каком-нибудь уголке. На долгие годы.

Попробуйте Rust!

Вы можете помочь и перевести немного средств на развитие сайта



Перевод статьи Федерико Мена-Кинтеро, который, наряду с Мигелем де Икаса, основал проект GNOME - широко используемую, свободную графическую среду, в основном для систем GNU/Linux. Перед этим он некоторое время поддерживал GIMP . Сейчас Федерико активно развивает библиотеку librsvg с использованием языка программирования Rust. По его мнению, разработка достигла момента, когда портирование некоторых крупных компонент с C на Rust выглядит более лёгкой задачей, чем просто добавление аксессоров к ним. Федерико часто приходится переключаться с C на Rust и обратно, и в статье он рассказывает, почему считает C очень и очень примитивным языком для современного ПО.

Своего рода элегия по C

Я влюбился в язык программирования C около 24-ёх лет назад. Я выучил основы, прочитав испанский перевод второго издания «Языка программирования C» Кернигана/Ритчи (K&R) . До этого я писал на Turbo Pascal в довольно низкоуровневой манере - с указателями и ручным выделением памяти. После него C казался освежающим и мощным.

К&R - это отличная книга благодаря стилю изложения и лаконичности программирования. Эта книга даже учит, как реализовать простые функции malloc/free , что крайне поучительно. Даже такие низкоуровневые конструкции, которые выглядят как часть языка, могут быть реализованы на самом языке!

В последующие годы я хорошо освоил C. Это простой язык с небольшой стандартной библиотекой. Наверное, это был идеальный язык для реализации ядер Unix в 20 000 строк кода или около того.

GIMP и GTK+ научили меня тому, как использовать модный объектно-ориентированный подход в C. GNOME показал, как поддерживать крупномасштабные проекты, написанные на C. Стало казаться, что 20 000 строк C кода - это проект, который можно практически полностью понять за пару недель.

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

Опыт использования C

Положительный

  • Чтение исходного кода проекта POV-Ray впервые и изучение того, как использовать объектно-ориентированный подход и наследование в чистом C;
  • Чтение исходного кода проекта GTK+ и изучение читаемого, поддерживаемого и чистого стиля написания кода на C;
  • Чтение исходного кода проекта SIOD, а также ранних исходников проекта Guile и понимание того, как интерпретатор Scheme может быть написан на C;
  • Написание первых версий Eye of Gnome и доработка системы микротайлового рендеринга.

Негативный

  • Работа в команде Evolution, когда программа постоянно падала. Мы были вынуждены приобрести машину с Solaris на борту, чтобы иметь возможность купить Purify; в те времена Valgrind-а еще не существовало;
  • Отладка взаимных блокировок потоков в gnome-vfs;
  • Безуспешная отладка Mesa;
  • Когда мне передали исходники первых версий Nautilus-share, я увидел, что free() вообще не используется;
  • Попытки рефакторинга кода, о стратегии управления памятью которого я не имел понятия;
  • Попытка сделать библиотеку из кода, кишащего глобальными переменными, и в котором ни одна функция не помечена как static .

Фичи Rust, которых не хватает в C

Автоматическое управление ресурсами

Один из первых блог-постов, которые я прочитал о Rust, назывался «В Rust вам никогда не придётся закрывать сокет» . Rust заимствует у C++ идеи об идиоме (Resource Acquisition Is Initialization, получение ресурса есть инициализация) и умных указателях, добавляет принцип единоличного владения для значений и предоставляет механизм автоматического, детерминированного управления ресурсами в очень изящной упаковке.

  • Автоматическое: не нужно вызывать free() вручную. Память освободится, файлы закроются, мьютексы разблокируются, когда переменные выйдут из зоны видимости. Если вам нужно написать обёртку для стороннего ресурса, то всё, что нужно сделать, это реализовать типаж Drop. Обёрнутый ресурс ощущается как часть языка, потому что вам не приходится нянчиться с его временем жизни вручную;
  • Детерминированное: ресурсы создаются (память выделяется и инициализируется, файлы открываются и т. д.) и уничтожаются, когда выходят из зоны видимости. Никакой сборки мусора: ресурсы действительно освобождаются, когда вы закрываете скобку. Вы начинаете видеть время жизни данных в своей программе как дерево вызовов функций.

После того, как постоянно забываешь освобождать/закрывать/уничтожать объекты в C, или, ещё хуже, пытаешься понять, где в чужом коде забыли сделать что-то из этого (или ошибочно сделали дважды )… я просто больше этого не хочу.

Дженерики

Vec - это действительно вектор, размер элементов которого равен размеру объекта типа T . Это не массив указателей на объекты, память для которых выделялась отдельно. Он специально компилируется в код, который может работать только с объектами типа T .

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

Типажи - это больше, чем просто интерфейсы

Rust - это не Java-подобный объектно-ориентированный язык, подробнее об этом можно прочитать в open-source книге «The Rust Programming Language» . Вместо этого в нём есть типажи, которые поначалу похожи на интерфейсы в Java, - простой способ осуществления динамического переключения (dynamic dispatch), так что если объект реализует Drawable , то можно предположить, что у него есть метод draw() .

Однако типажи - это более мощный инструмент. Одной из отличительных особенностей типажей можно считать ассоциированные типы (associated types). Например, Rust предоставляет типаж Iterator , который вы можете реализовать:

Pub trait Iterator { type Item; fn next(&mut self) -> Option; }

Это означает, что всякий раз, когда вы реализуете этот типаж для какого-либо объекта, поддерживающего итерирование, вы также указываете тип Item для значений, которые он выдаёт. Если вы вызываете next() и элементы ещё остались, вы получите Some(ТипВашегоЭлемента) . Когда у вашего итератора закончатся элементы, он вернет None .

Ассоциированные типы могут ссылаться на другие типажи.

Например, в Rust вы можете использовать циклы for со всем, что реализует типаж IntoIterator:

Pub trait IntoIterator { /// Тип элементов, по которым идёт итерация type Item; /// В какой тип итератора мы преобразуемся? type IntoIter: Iterator; fn into_iter(self) -> Self::IntoIter; }

Когда реализуете этот типаж, вы должны указать и тип элементов, которые будет выдавать ваш итератор, и сам тип IntoIter , который реализует типаж Iterator и хранит состояние вашего итератора.

Таким образом, вы можете построить настоящую сеть типов, которые ссылаются друг на друга. Вы можете написать типаж, который говорит: «Я могу сделать foo и bar, но только если вы дадите мне тип, который умеет делать вот это и это».

Срезы

Я уже писал о том, насколько в C не хватает срезов (slices) для работы со строками и какая это головная боль, когда привык, что они под рукой.

Современные инструменты для управления зависимостями

Вместо того, чтобы

  • Запускать pkg-config руками или через Autotools-макрос;
  • Сражаться с include-путями в заголовочных файлах…
  • … и библиотечных файлах;
  • И, по сути, полагаться на то, что пользователь гарантирует установку верных версий библиотек,

вы пишете файл Cargo.toml , в котором перечисляются названия и версии всех наших зависимостей. Они будут загружены из общеизвестного источника или из любого другого, указанного вами.

Не нужно сражаться с зависимостями. Оно просто работает, когда вы набираете cargo build .

Тесты

В C очень сложно покрывать код тестами по нескольким причинам:

  • Внутренние функции часто помечены как static . Это означает, что они не могут быть вызваны вне файла, в котором эта функция определена. Тестовая программа вынуждена либо #include -ить содержимое исходника, в котором функция объявлена, либо использовать #ifdef , чтобы убирать static только при тестировании;
  • Вам придётся плясать с бубном вокруг вашего Makefile, чтобы слинковать тестовую программу с определённой частью зависимостей основной или с какой-то частью оставшейся программы;
  • Вам придётся выбрать фреймворк для тестирования. Вам придётся зарегистрировать свои тесты в фреймворке для тестирования. Вам придётся изучить этот фреймворк.

В Rust вы пишете

# fn test_that_foo_works() { assert!(foo() == expected_result); }

в любом месте программы или библиотеки, и, когда вы набираете cargo test , ОНО ПРОСТО, *****, РАБОТАЕТ. Этот код линкуется только в тестовый исполняемый файл. Не нужно ничего компилировать дважды вручную, писать Makefile-магию или разбираться, как вытащить внутренние функции для тестирования.

Для меня это одна из главных киллер-фич языка.

Документация с тестами

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

/// Multiples the specified number by two /// /// ``` /// assert_eq!(multiply_by_two(5), 10); /// ``` fn multiply_by_two(x: i32) -> i32 { x * 2 }

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

Гигиеничные макросы

В Rust особые гигиеничные макросы, позволяющие избежать проблем, при которых во время разворачивания C макросов происходит непреднамеренное затенение идентификаторов в коде. Вам больше не нужно писать макросы, заключая все символы в скобки, чтобы max(5 + 3, 4) работал правильно.

Никакого неявного приведения типов

Все эти баги, которые появляются в C из-за непреднамеренного приведения int к short или к char и т. п. - в Rust их нет. Вы должны приводить типы явно.

Никакого целочисленного переполнения

Этим всё сказано.

Как правило, никакого неопределённого поведения в безопасном режиме

В Rust, если что-то вызывает неопределенное поведение в «безопасном режиме» (всё, что написано вне блоков unsafe {}), это расценивается как баг самого языка. Например, можно сделать побитовый сдвиг отрицательного целого числа вправо и произойдёт именно то, что вы ожидаете.

Сопоставление с образцом

Знаете, как gcc выдает предупреждение, если вы используете switch() с перечислением (enum), но обработаете не все варианты? Это детский сад по сравнению с Rust.

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

Impl f64 { pub fn sin_cos(self) -> (f64, f64); } let angle: f64 = 42.0; let (sin_angle, cos_angle) = angle.sin_cos();

match работает на строках. ВЫ МОЖЕТЕ МАТЧИТЬ ГРЁБАНЫЕ СТРОКИ.

Let color = "зеленый"; match color { "красный" => println!("Это красный"), "зеленый" => println!("Это зеленый"), _ => println!("Что-то другое"), }

Вы же знаете, насколько такое плохо читается?

my_func(true, false, false)

Как насчет того, чтобы вместо этого использовать сопоставление с образцом на аргументах функции:

Pub struct Fubarize(pub bool); pub struct Frobnify(pub bool); pub struct Bazificate(pub bool); fn my_func(Fubarize(fub): Fubarize, Frobnify(frob): Frobnify, Bazificate(baz): Bazificate) { if fub { ...; } if frob && baz { ...; } } ... my_func(Fubarize(true), Frobnify(false), Bazificate(true));

Стандартная полезная обработка ошибок

Я подробно останавливался на этом. Больше никаких булевых возвращаемых значений без нормального описания ошибки, никаких случайно проигнорированных ошибок, никакой обработки исключительных ситуаций longjmp-ами.

#

Если вы пишете новый тип (скажем, структуру с кучей полей), то можно написать # , и Rust будет знать, как автоматически напечатать содержимое этого типа для отладки. Больше не нужно руками писать специальную функцию, которую затем придётся вызывать из gdb, только для того, чтобы посмотреть содержимое полей пользовательского типа.

Замыкания

Вам больше не придётся передавать указатели на функцию и user_data вручную.

Заключение

Я пока не попробовал «fearless concurrency» , где компилятор может предотвращать гонки данных в многопоточном коде. Я полагаю, что это в корне меняет положение дел для людей, которые пишут параллельный код на регулярной основе.

C - это старый язык с примитивными конструкциями и примитивными инструментами. Он хорошо подходил для небольших однопроцессорных Unix-ядер, которые работали в доверенных, академических средах. Но для современного программного обеспечения он больше не подходит.

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

Итак, хотим представить вашему вниманию недавнего именинника (15 мая 2016 года ему исполнился год) — Rust. Это универсальный язык программирования, разрабатываемый компанией Mozilla, три основных принципа которого: скорость, безопасность и эргономика. Сами создатели нескромно считают его одним наиболее вероятных наследников C/C++. Согласно опросу портала StackOverflow , именно Rust сегодня наиболее любимый разработчиками язык. Итак, давайте подробнее разбираться в том, что же он из себя представляет.

Rust для новичка

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

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

Как изучать

Почти всё необходимое вы можете найти на официальном сайте . Кроме того, сообщество последователей Rust весьма обширно и доброжелательно, поэтому за советом всегда можно обратиться в IRC (есть русский раздел) и официальный форум. Кроме того, понемногу стали появляться книги, в том числе электронные . Пока трудно оценить их качество, но факт такой имеется.

Для тех, кто прошёл начальную стадию знакомства, на GiHub можно найти уйму полезного материала, в том числе RFC и коммиты . Кроме того, вы можете посетить лично или хотя бы посмотреть интернет-трансляцию одной из конференций по Rust, намеченных на вторую половину года. Вот календарь:

  • 9-10 сентября конференция RustConf в Портленде, США;
  • 17 сентября конференция европейского сообществ RustFest в Берлине, Германия;
  • 27 октября конференция Rust Belt Rust в Питтсбурге, США;

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

Особенности

Немного дублируя то, о чём было сказано ранее, выделим основные плюсы и минусы языка Rust.

Плюсы:

  • Безопасная работа с памятью;
  • Высокое быстродействие;
  • Алгебраический тип данных;
  • Предсказуемость компиляции;

Минусы:

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

На самом деле к отличиям, вроде замены наследования на способности, быстро привыкаешь. Как только глаза привыкли, руки набились, Rust превращается во вполне рабочий язык, проще и функциональнее C++, но уступающий по «красивости» многим другим языкам программирования. Фактически же главное отличие Rust от конкурентов и предшественников — именно скорость и безопасность.

Востребованность

На сегодняшний день Rust популярен сред разработчиков игр, графики и операционных систем. Однако по понятным причинам количество стационарных мест, где бы требовались узкопрофильные знатоки Rust, в мире и уже тем более в России крайне невелико. Тем не менее, пока не видно никаких предпосылок, что язык канет в лету, больше похоже на планомерный захват мира. А значит хорошие навыки использования Rust в будущем помогут найти высокооплачиваемую интересную работу как в нашей стране, так и за рубежом.

Еще востребованного: профессия « ».