Rust — это мультипарадигмальный язык программирования общего назначения , в котором упор делается на производительность , безопасность типов и параллелизм . Он обеспечивает безопасность памяти (то есть все ссылки указывают на действительную память) без сборщика мусора . Чтобы одновременно обеспечить безопасность памяти и предотвратить гонки данных , его «проверка заимствования» отслеживает время жизни объектов всех ссылок в программе во время компиляции . На Rust повлияли идеи функционального программирования , включая неизменяемость , функции высшего порядка и алгебраические типы данных . Он популярен в системном программировании . [13] [14] [15]
Разработчик программного обеспечения Грейдон Хоар создал Rust как личный проект во время работы в Mozilla Research в 2006 году. Mozilla официально спонсировала проект в 2009 году. В годы, прошедшие после выхода первого стабильного выпуска в мае 2015 года, Rust был принят такими компаниями, как Amazon , Discord , Dropbox , Google ( Alphabet ), Meta и Microsoft . В декабре 2022 года он стал первым языком, кроме C и ассемблера , который поддерживался при разработке ядра Linux .
Rust известен своим быстрым распространением [16] и изучается в исследованиях теории языков программирования . [17] [18] [19]
Rust вырос из личного проекта, начатого в 2006 году сотрудником Mozilla Research Грейдоном Хоаром. [20] Mozilla начала спонсировать проект в 2009 году в рамках продолжающейся разработки экспериментального браузерного движка под названием Servo , [21] о котором Mozilla официально объявила в 2010 году . [22] [23] В это время логотип Rust был разработан на основе велосипедной экипировки . [24] Примерно в том же году работа перешла от первоначального компилятора , написанного на OCaml , к автономному компилятору на основе LLVM , написанному на Rust. Новый компилятор Rust успешно скомпилировался в 2011 году. [21]
Система типов Rust претерпела значительные изменения между версиями 0.2, 0.3 и 0.4. В версии 0.2, вышедшей в марте 2012 года, впервые были представлены классы . [25] Четыре месяца спустя в версии 0.3 были добавлены деструкторы и полиморфизм за счет использования интерфейсов. [26] В октябре 2012 года была выпущена версия 0.4, в которой были добавлены черты как средство наследования . Интерфейсы были объединены с особенностями и удалены как отдельная функция; а классы были заменены комбинацией реализаций и структурированных типов . [27]
В начале 2010-х годов управление памятью через систему владения постепенно консолидировалось, чтобы предотвратить ошибки памяти. К 2013 году сборщик мусора Rust был удален, а правила владения остались в силе. [20]
В январе 2014 года главный редактор Dr. Dobb's Journal Эндрю Бинсток прокомментировал шансы Rust стать конкурентом C++ наряду с D , Go и Nim (тогда Nimrod). По словам Бинстока, хотя Rust «широко считался удивительно элегантным языком», его внедрение замедлилось, поскольку он радикально менялся от версии к версии. [28] Первая стабильная версия Rust 1.0 была анонсирована 15 мая 2015 года. [29] [30]
Разработка браузерного движка Servo продолжалась параллельно с ростом Rust. В сентябре 2017 года был выпущен Firefox 57 как первая версия, включающая компоненты Servo, в проекте под названием « Firefox Quantum ». [31]
В августе 2020 года Mozilla уволила 250 из 1000 своих сотрудников по всему миру в рамках корпоративной реструктуризации, вызванной пандемией COVID-19 . [32] [33] Команда Servo была расформирована. Мероприятие вызвало обеспокоенность по поводу будущего Rust, поскольку некоторые члены команды активно участвовали в разработке Rust. [34] На следующей неделе команда Rust Core признала серьезные последствия увольнений и объявила, что планы по созданию фонда Rust находятся в стадии реализации. Первой целью фонда будет стать владельцем всех товарных знаков и доменных имен и взять на себя финансовую ответственность за их расходы. [35]
8 февраля 2021 года пять компаний-основателей ( AWS , Huawei , Google , Microsoft и Mozilla ) объявили о создании Rust Foundation . [36] [37] В сообщении в блоге , опубликованном 6 апреля 2021 года, Google объявил о поддержке Rust в рамках проекта Android Open Source Project в качестве альтернативы C/C++. [38]
22 ноября 2021 года группа модераторов, которая отвечала за соблюдение стандартов сообщества и Кодекса поведения, объявила о своей отставке «в знак протеста против того, что основная команда не несет ответственности ни перед кем, кроме себя». [39] В мае 2022 года команда Rust Core, другие ведущие программисты и некоторые члены правления Rust Foundation провели реформы управления в ответ на инцидент. [40]
6 апреля 2023 года Фонд Rust опубликовал проект новой политики в отношении товарных знаков , в котором были пересмотрены правила использования логотипа и названия Rust, что вызвало негативную реакцию со стороны пользователей и участников Rust. [41]
Синтаксис Rust аналогичен синтаксису C и C++, [42] [43] , хотя на многие его функции повлияли языки функционального программирования . [44] Хоар описал Rust как ориентированный на «разочарованных разработчиков C++» и подчеркнул такие функции, как безопасность, контроль структуры памяти и параллелизм . [21] Безопасность в Rust включает в себя гарантии безопасности памяти, безопасности типов и отсутствия гонок за данными.
Ниже находится надпись «Hello, World!» программа на Rust. Ключевое fn
слово обозначает функцию , и println!
макрос выводит сообщение на стандартный вывод . [45] Операторы в Rust разделяются точкой с запятой.
fn main () { println! ( "Привет, мир!" ); }
В Rust блоки кода разделяются фигурными скобками , а поток управления реализуется с помощью таких ключевых слов, как if
, else
, while
и for
. [46] Сопоставление с образцом можно выполнить с помощью match
ключевого слова. [47] В приведенных ниже примерах пояснения даются в комментариях , которые начинаются с //
. [48]
fn main () { // Определение изменяемой переменной с помощью 'let mut' // Использование макроса vec! чтобы создать вектор, пусть значения mut = vec! [ 1 , 2 , 3 , 4 ]; для значения в & значений { println! ( "значение = {}" , значение ); } если значения . len () > 5 { println! ( «Список длиннее пяти элементов» ); } // Значения соответствия шаблону . len () { 0 => println! ( «Пусто» ), 1 => println! ( «Одно значение» ), // при сопоставлении с образцом можно использовать диапазоны целых чисел 2 ..= 10 => println! ( «От двух до десяти значений» ), 11 => println! ( "Одиннадцать значений" ), // Шаблон `_` называется "подстановочным знаком", он соответствует любому значению _ => println! ( «Много значений» ), }; // цикл while с предикатом и сопоставлением с образцом с использованием let while let Some ( value ) = values . поп () { println! ( "значение = {значение}" ); // использование фигурных скобок для форматирования локальной переменной } }
Rust ориентирован на выражения , причем почти каждая часть тела функции является выражением , включая операторы потока управления. [49] Вместо троичного условного выражения Cif
используется обычное выражение . Поскольку возвраты являются неявными, функция не обязательно должна заканчиваться выражением ; если точка с запятой опущена, в качестве возвращаемого значения используется значение последнего выражения в функции , [50] , как показано в следующей рекурсивной реализации функции факториал :return
fn факториал ( i : u64 ) -> u64 { if i == 0 { 1 } else { i * факториал ( i - 1 ) } }
Следующая итеративная реализация использует ..=
оператор для создания включающего диапазона:
fn факториал ( я : u64 ) -> u64 { ( 2 ..= i ). продукт () }
В Rust анонимные функции называются замыканиями. [51] Они определяются с использованием следующего синтаксиса:
|< параметр - имя > : < тип >| -> < возврат - тип > { < тело > };
Например:
пусть ж = | х : i32 | -> i32 { х * 2 };
Однако с помощью вывода типа компилятор может определить тип каждого параметра и тип возвращаемого значения, поэтому приведенную выше форму можно записать как:
пусть ж = | х | { х * 2 };
При замыканиях с одним выражением (т. е. телом с одной строкой) и неявным возвращаемым типом фигурные скобки можно опустить:
пусть ж = | х | х * 2 ;
Замыкания без входного параметра записываются так:
пусть f = || распечататьлн! ( "Привет, мир!" );
Замыкания могут передаваться в качестве входных параметров функций, которые ожидают указатель на функцию:
// Функция, которая принимает указатель функции в качестве аргумента и вызывает ее // со значением `5`. fn apply ( f : fn ( i32 ) -> i32 ) -> i32 { // Нет точки с запятой, чтобы указать неявный возврат f ( 5 ) } fn main () { // Определение замыкания let f = | х | х * 2 ; распечататьлн! ( "{}" , применить ( f )); // 10 println! ( "{}" , f ( 5 )); // 10 }
Однако для описания того, как фиксируются значения в теле замыкания, могут потребоваться сложные правила. Они реализованы с использованием черт Fn
, FnMut
и FnOnce
: [52]
Fn
: замыкание фиксируется по ссылке ( &T
). Они используются для функций, которые все еще можно вызывать, если у них есть только ссылочный доступ (с &
) к своей среде.FnMut
: замыкание фиксируется по изменяемой ссылке ( &mut T
). Они используются для функций, которые могут быть вызваны, если у них есть доступ к изменяемой ссылке (с &mut
) к их среде.FnOnce
: замыкание фиксируется по значению ( T
). Они используются для функций, которые вызываются только один раз.Благодаря этим особенностям компилятор будет захватывать переменные наименее ограничительным образом. [52] Они помогают управлять тем, как значения перемещаются между областями действия, что очень важно, поскольку Rust следует конструкции жизненного цикла, чтобы гарантировать, что значения «заимствованы» и перемещены предсказуемым и явным образом. [53]
Ниже показано, как можно передать замыкание в качестве входного параметра, используя этот Fn
признак:
// Функция, которая принимает значение типа F (который определяется как // универсальный тип, реализующий признак `Fn`, например замыкание) // и вызывает его со значением `5`. fn apply_by_ref < F > ( f : F ) -> i32 где F : Fn ( i32 ) -> i32 { f ( 5 ) } fn main () { let f = | х | { печать! ( «Я получил значение: {}» , x ); х * 2 }; // Применяет функцию перед печатью возвращаемого значения println! ( "5 * 2 = {}" , apply_by_ref ( f )); } // ~~ Вывод программы ~~ // Я получил значение: 5 // 5 * 2 = 10
Предыдущее определение функции также можно для удобства сократить следующим образом:
fn apply_by_ref ( f : impl Fn ( i32 ) -> i32 ) -> i32 { f ( 5 ) }
Rust строго типизирован и статически типизирован . Типы всех переменных должны быть известны во время компиляции; присвоение значения определенного типа переменной другого типа приводит к ошибке компиляции . Переменные объявляются с ключевым словом let
, а для определения их типа используется определение типа. [54] Переменные, назначенные несколько раз, должны быть помечены ключевым словом mut
(сокращение от mutable). [55]
Целочисленный тип по умолчанию — i32
, а тип с плавающей запятой по умолчанию — f64
. Если тип буквального числа не указан явно, он либо выводится из контекста, либо используется тип по умолчанию. [56]
Option
значения обрабатываются с использованием синтаксического сахара , такого как if let
конструкция, для доступа к внутреннему значению (в данном случае, строке): [76]
fn main () { let name1 : Option <& str > = None ; // В этом случае ничего не будет выведено, если let Some ( name ) = name1 { println! ( "{имя}" ); } let name2 : Option <& str > = Some ( "Мэтью" ); // В этом случае слово «Мэтью» будет напечатано, если let Some ( name ) = name2 { println! ( "{имя}" ); } }
Rust не использует нулевые указатели для обозначения отсутствия данных, так как это может привести к нулевому разыменованию . Соответственно, базовые &
и &mut
ссылки гарантированно не будут нулевыми. Вместо этого Rust использует Option
для этой цели: Some(T)
указывает на наличие значения и None
аналогичен нулевому указателю. [77] Option
реализует «оптимизацию нулевого указателя», избегая любых пространственных издержек для типов, которые не могут иметь нулевое значение ( NonZero
например, ссылки или типы). [78]
В отличие от ссылок, типы необработанных указателей *const
могут *mut
быть нулевыми; однако их невозможно разыменовать, если код явно не объявлен небезопасным посредством использования блока unsafe
. В отличие от разыменования, создание необработанных указателей разрешено внутри безопасного кода Rust. [79]
Пользовательские типы создаются с помощью ключевых слов struct
или enum
. Ключевое struct
слово используется для обозначения типа записи , который группирует несколько связанных значений. [80] enum
s может принимать различные варианты во время выполнения, его возможности аналогичны алгебраическим типам данных, встречающимся в языках функционального программирования. [81] И структуры, и перечисления могут содержать поля разных типов. [82] Альтернативные имена для одного и того же типа можно определить с помощью type
ключевого слова. [83]
Ключевое impl
слово может определять методы для определяемого пользователем типа (данные и функции определяются отдельно). Реализации выполняют роль, аналогичную роли классов в других языках. [84]
Rust не обеспечивает неявного преобразования типов (принуждения) между примитивными типами. Но явное преобразование типов (приведение типов) можно выполнить с помощью as
ключевого слова. [85]
пусть х = 1000 ; распечататьлн! ( "1000 как u16: {}" , x как u16 );
Система владения Rust состоит из правил, обеспечивающих безопасность памяти без использования сборщика мусора. Во время компиляции каждое значение должно быть прикреплено к переменной, называемой владельцем этого значения, и каждое значение должно иметь ровно одного владельца. [86] Значения перемещаются между разными владельцами путем присвоения или передачи значения в качестве параметра функции. Значения также могут быть заимствованы, то есть они временно передаются в другую функцию перед возвратом владельцу. [87] С помощью этих правил Rust может предотвратить создание и использование висячих указателей : [87] [88]
fn print_string ( s : String ) { println! ( "{}" , с ); } fn main () { let s = String :: from ( «Hello, World» ); print_string ( ы ); // s, использованные print_string // s были перемещены, поэтому их больше нельзя использовать // другой print_string(s); приведет к ошибке компиляции }
Из-за этих правил владения типы Rust известны как линейные или аффинные типы, что означает, что каждое значение можно использовать ровно один раз. Это обеспечивает своего рода изоляцию ошибок программного обеспечения , поскольку владелец значения несет полную ответственность за его правильность и освобождение. [89]
Время жизни обычно является неявной частью всех ссылочных типов в Rust. Каждое время жизни включает в себя набор мест в коде, для которых действительна переменная. Например, ссылка на локальную переменную имеет время жизни, соответствующее блоку, в котором она определена: [90]
fn main () { let r = 9 ; // ------------------+- Время жизни 'a // | { // | пусть х = 5 ; // --+-- Время жизни 'b | распечататьлн! ( "r: {}, x: {}" , r , x ); // | | } // | | // | распечататьлн! ( "р: {}" , р ); // | } // ------------------+
Средство проверки заимствований в компиляторе Rust использует время жизни, чтобы гарантировать, что значения контрольных точек остаются действительными. Это также гарантирует, что изменяемая ссылка существует только в том случае, если одновременно не существует неизменяемых ссылок. [91] [92] В приведенном выше примере время существования субъекта ссылки (переменная x
) короче, чем время жизни самой ссылки (переменная r
), поэтому при проверке заимствований возникают ошибки, препятствующие x
использованию из-за пределов ее области действия. [93] На систему памяти и владения Rust повлияло региональное управление памятью в таких языках, как Cyclone и ML Kit. [5]
Rust определяет взаимосвязь между временем жизни объектов, созданных и используемых функциями, с использованием параметров времени жизни в качестве сигнатурной функции. [94]
Когда стек или временная переменная выходит за пределы области видимости, они удаляются путем запуска деструктора . Деструктор может быть определен программно через drop
функцию. Этот метод реализует так называемый шаблон проектирования «приобретение ресурсов — инициализация» (RAII), в котором ресурсы, такие как файловые дескрипторы или сетевые сокеты, привязаны к времени жизни объекта: при удалении объекта ресурс закрывается. [95] [96]
В приведенном ниже примере анализируются некоторые параметры конфигурации из строки и создается структура, содержащая эти параметры. Структура содержит только ссылки на данные; поэтому, чтобы структура оставалась допустимой, данные, на которые ссылается структура, также должны быть действительными. Сигнатура функции parse_config
явно определяет эту связь. В этом примере явное время жизни не требуется в новых версиях Rust из-за исключения времени жизни — алгоритма, который автоматически назначает время жизни функциям, если они тривиальны. [97]
используйте std :: collections :: HashMap ; // Эта структура имеет один параметр времени жизни, 'src. Имя используется только в определении структуры. #[derive(Debug)] struct Config <' src > { имя хоста : & ' src str , имя пользователя : & ' src str , } // Эта функция также имеет параметр времени жизни 'cfg. 'cfg прикрепляется к параметру "config", который // устанавливает, что данные в "config" живут, по крайней мере, в течение времени жизни 'cfg. // Возвращенная структура также использует 'cfg на протяжении всего своего существования, поэтому она может существовать не более, чем 'cfg. fn parse_config <' cfg > ( config : & ' cfg str ) -> Config <' cfg > { let key_values : HashMap < _ , _ > = config . линии () . фильтр ( | линия | ! строка . начинается_с ( '#' )) . filter_map ( | линия | линия . Split_once ( '=' )) . карта ( | ( ключ , значение ) | ( ключ . обрезка (), значение . обрезка ())) . собирать (); Config { имя_хоста : значения_ключа [ "имя_хоста] ], имя пользователя : значения_ключей [ "имя_пользователя" ] } } fn main () { let config = parse_config ( r#"hostname = foobar username=barfoo"# , ); распечататьлн! ( "Разобранная конфигурация: {:#?}" , config ); }
Rust спроектирован так, чтобы быть безопасным для памяти . Он не допускает нулевых указателей, висячих указателей или гонок за данными . [98] [99] [100] Значения данных могут быть инициализированы только с помощью фиксированного набора форм, каждая из которых требует, чтобы их входные данные были уже инициализированы. [101]
Небезопасный код может обойти некоторые из этих ограничений, используя unsafe
ключевое слово. [79] Небезопасный код также может использоваться для низкоуровневых функций, таких как доступ к энергозависимой памяти , встроенные функции, специфичные для архитектуры, каламбур типов и встроенная ассемблерная система. [102]
Rust не использует сборку мусора . Вместо этого память и другие ресурсы управляются по соглашению «получение ресурсов — это инициализация» [103] с дополнительным подсчетом ссылок . Rust обеспечивает детерминированное управление ресурсами с очень низкими накладными расходами . [104] По умолчанию значения распределяются в стеке , и все динамические выделения должны быть явными. [105]
Встроенные типы ссылок, использующие этот &
символ, не требуют подсчета ссылок во время выполнения. Безопасность и достоверность базовых указателей проверяются во время компиляции, что предотвращает зависание указателей и другие формы неопределенного поведения . [106] Система типов Rust отделяет общие неизменяемые ссылки формы &T
от уникальных изменяемых ссылок формы &mut T
. Изменяемую ссылку можно привести к неизменяемой ссылке, но не наоборот. [107]
Более продвинутые возможности Rust включают использование универсальных функций . Универсальной функции присваиваются универсальные параметры , которые позволяют применять одну и ту же функцию к различным типам переменных. Эта возможность уменьшает дублирование кода [108] и известна как параметрический полиморфизм .
Следующая программа вычисляет сумму двух вещей, для чего сложение реализуется с помощью универсальной функции:
используйте std :: ops :: Add ; // sum — это универсальная функция с одним параметром типа T fn sum < T > ( num1 : T , num2 : T ) -> T где T : Add < Output = T > , // T должен реализовать признак Add, где сложение возвращает еще один T { num1 + num2 // num1 + num2 — это синтаксический сахар для num1.add(num2), предоставляемый признаком Add } fn main () { let result1 = sum ( 10 , 20 ); распечататьлн! ( "Сумма: {}" , результат1 ); // Сумма: 30 пусть результат2 = сумма ( 10,23 , 20,45 ); распечататьлн! ( "Сумма: {}" , результат2 ); // Сумма: 30,68 }
Во время компиляции создаются экземпляры таких полиморфных функций, как sum
конкретные типы , требуемые кодом; в данном случае сумма целых чисел и сумма чисел с плавающей запятой.
Обобщенные шаблоны можно использовать в функциях, чтобы обеспечить реализацию поведения для разных типов без повторения одного и того же кода. Универсальные функции могут быть написаны относительно других дженериков, не зная фактического типа. [109]
Система типов Rust поддерживает механизм, называемый типажами, вдохновленный классами типов в языке Haskell [5] для определения общего поведения между различными типами. Например, этот Add
признак может быть реализован для чисел с плавающей запятой и целых чисел, которые можно добавлять; а признаки Display
или Debug
могут быть реализованы для любого типа, который можно преобразовать в строку. Черты могут использоваться для обеспечения набора общего поведения для разных типов без знания фактического типа. Эта возможность известна как специальный полиморфизм .
Универсальные функции могут ограничивать универсальный тип для реализации определенного признака или признаков; например, add_one
для реализации функции может потребоваться тип Add
. Это означает, что универсальную функцию можно проверить по типу сразу после ее определения. Реализация дженериков аналогична типичной реализации шаблонов C++: для каждого экземпляра создается отдельная копия кода. Это называется мономорфизацией и контрастирует со схемой стирания типов , обычно используемой в Java и Haskell. Стирание типов также доступно по ключевому слову dyn
(сокращение от «динамический»). [110] Поскольку мономорфизация дублирует код для каждого используемого типа, это может привести к более оптимизированному коду для конкретных случаев использования, но время компиляции и размер выходного двоичного файла также увеличиваются. [111]
Помимо определения методов для определяемого пользователем типа, impl
ключевое слово можно использовать для реализации признака типа. [84] При реализации трейты могут предоставлять дополнительные производные методы. [112] Например, признак Iterator
требует, чтобы next
метод был определен для типа. Как только next
метод определен, признак может предоставлять общие функциональные вспомогательные методы для итератора, такие как map
или filter
. [113]
Черты Rust реализованы с использованием статической диспетчеризации , что означает, что тип всех значений известен во время компиляции; однако Rust также использует функцию, известную как объекты-характеристики, для выполнения динамической отправки (также известной как утиная типизация ). [114] Динамически отправляемые объекты свойств объявляются с использованием синтаксиса, dyn Tr
где Tr
— признак. Объекты признаков имеют динамический размер, поэтому их необходимо помещать за указателем, например Box
. [115] В следующем примере создается список объектов, каждый из которых может быть распечатан с использованием Display
признака:
используйте std :: fmt :: Display ; let v : Vec < Box < dyn Display >> = vec! [ Коробка :: новый ( 3 ), Коробка :: новый ( 5.0 ), Коробка :: новый ( «привет» ), ]; для x в v { println! ( "{Икс}" ); }
Если элемент в списке не реализует этот Display
признак, это вызовет ошибку времени компиляции. [116]
Циклы for в Rust работают в функциональном стиле как операции над типом итератора . Например, в цикле
для х в 0 .. 100 { f ( x ); }
0..100
— значение типа Range
, реализующего Iterator
признак; код применяет функцию f
к каждому элементу, возвращаемому итератором. Итераторы можно комбинировать с функциями над итераторами, такими как map
, filter
и sum
. Например, следующая команда суммирует все числа от 1 до 100, кратные 3:
( 1 ..= 100 ). фильтр ( |& x | x % 3 == 0 ). сумма ()
Язык Rust можно расширить с помощью макросов.
Декларативный макрос (также называемый «макросом по примеру») — это макрос, который использует сопоставление с образцом для определения своего расширения. [117] [118]
Процедурные макросы — это функции Rust, которые запускают и изменяют входной поток токенов компилятора до компиляции любых других компонентов. Они, как правило, более гибкие, чем декларативные макросы, но их сложнее поддерживать из-за их сложности. [119] [120]
Процедурные макросы бывают трех видов:
custom!(...)
#[derive(CustomDerive)]
#[custom_attribute]
Макрос println!
является примером функционального макроса. Макрос [121] предоставляет широко используемую библиотеку для генерации кода для чтения и записи данных во многих форматах, таких serde_derive
как JSON . Макросы атрибутов обычно используются для привязок к языку, например библиотека привязок Rust к R. [122]extendr
В следующем коде показано использование процедурных макросов Serialize
, Deserialize
и Debug
-derived для реализации чтения и записи JSON, а также возможности форматирования структуры для отладки.
используйте serde_json :: { Сериализация , Десериализация }; #[derive(Serialize, Deserialize, Debug)] struct Point { x : i32 , y : i32 , } fn main () { let point = Point { x : 1 , y : 2 }; let сериализованный = serde_json :: to_string ( & point ). развернуть (); распечататьлн! ( "сериализованный = {}" , сериализованный ); пусть десериализовано : Point = serde_json :: from_str ( & сериализовано ). развернуть (); распечататьлн! ( "десериализован = {:?}" , десериализовано ); }
Rust не поддерживает переменные аргументы в функциях. Вместо этого он использует макросы . [123]
макро_правила! вычисление { // Шаблон для одного `eval` ( eval $e : expr ) => {{ { let val : usize = $e ; // Принудительно сделать типы целыми числами println! ( "{} = {}" , stringify! { $e }, val ); } }}; // Рекурсивно разложить несколько `eval` ( eval $e : expr , $( eval $es : expr ), + ) => {{ Calculation ! { eval $e } вычислить ! { $( оценка $es ), + } }}; } fn main () { вычислить ! { // Смотри, ма! Вариатическое `вычислить!`! оценка 1 + 2 , оценка 3 + 4 , оценка ( 2 * 3 ) + 1 } }
c_variadic
переключатель функций. Как и другие интерфейсы C, система считается unsafe
Rust. [124]В Rust есть интерфейс внешних функций (FFI), который можно использовать как для вызова кода, написанного на таких языках, как C , из Rust, так и для вызова кода Rust с этих языков. По состоянию на 2023 год существует внешняя библиотека CXX для вызова C++ или из него. [125] Rust и C различаются тем, как они размещают структуры в памяти, поэтому структурам Rust может быть присвоен атрибут #[repr(C)]
, обеспечивающий ту же структуру, что и эквивалентная структура C. [126]
Экосистема Rust включает в себя компилятор, стандартную библиотеку и дополнительные компоненты для разработки программного обеспечения. Установка компонента обычно управляется установщиком набора инструментовrustup
Rust , разработанным в рамках проекта Rust. [127]
Компилятор Rust называется rustc
и переводит код Rust в язык низкого уровня, называемый промежуточным представлением LLVM (LLVM IR). Затем LLVM вызывается как подкомпонент для перевода IR-кода в машинный код . Затем компоновщик используется для объединения нескольких контейнеров в один исполняемый или двоичный файл . [128] [129]
Стандартная библиотека Rust определяет и реализует множество широко используемых пользовательских типов данных, включая основные структуры данных, такие как Vec
, Option
и HashMap
, а также типы интеллектуальных указателей . Rust также предоставляет возможность исключить большую часть стандартной библиотеки с помощью атрибута #![no_std]
; это позволяет приложениям, таким как встроенные устройства, удалять код зависимостей или предоставлять свои собственные основные структуры данных. Внутри стандартная библиотека разделена на три части: core
, alloc
, и std
, где std
и alloc
исключаются #![no_std]
. [130]
Cargo — это система сборки и менеджер пакетов Rust . Он загружает, компилирует, распространяет и загружает пакеты, называемые ящиками , которые сохраняются в официальном реестре. Он также действует как интерфейс для Clippy и других компонентов Rust. [16]
По умолчанию Cargo получает свои зависимости из пользовательского реестра crates.io , но в качестве зависимостей также можно указать репозитории и ящики Git в локальной файловой системе, а также другие внешние источники. [131]
Rustfmt — форматировщик кода для Rust. Он форматирует пробелы и отступы для создания кода в соответствии с общим стилем , если не указано иное. Его можно вызвать как отдельную программу или из проекта Rust через Cargo. [132]
Clippy — это встроенный в Rust инструмент проверки , позволяющий улучшить корректность, производительность и читаемость кода Rust. По состоянию на 2024 год [обновлять]в нем имеется более 700 правил. [133] [134]
После Rust 1.0 новые функции разрабатываются в ночных версиях, которые выпускаются ежедневно. В течение каждого шестинедельного цикла выпуска изменения ночных версий переводятся в бета-версию, а изменения предыдущей бета-версии переводятся в новую стабильную версию. [135]
Каждые два-три года выпускается новое «издание». Выпускаются версии, позволяющие вносить ограниченные критические изменения , например повышение await
ключевого слова для поддержки функций async/await . Крейты, предназначенные для разных выпусков, могут взаимодействовать друг с другом, поэтому крейт может обновиться до новой редакции, даже если его вызывающие программы или его зависимости по-прежнему ориентированы на более старые выпуски. Переход на новую редакцию можно облегчить с помощью автоматизированных инструментов. [136]
Самым популярным языковым сервером для Rust является Rust Analyzer , который официально заменил исходный языковой сервер RLS в июле 2022 года. [137] Rust Analyzer предоставляет IDE и текстовым редакторам информацию о проекте Rust; базовые функции, включая автодополнение и отображение ошибок компиляции при редактировании. [138]
В общем, гарантии безопасности памяти Rust не накладывают накладных расходов во время выполнения. [139] Заметным исключением является индексация массива , которая проверяется во время выполнения, хотя это часто не влияет на производительность. [140] Поскольку Rust не выполняет сборку мусора, он часто работает быстрее, чем другие языки, безопасные для памяти. [141] [142] [143]
Rust предоставляет два «режима»: безопасный и небезопасный. Безопасный режим — это «обычный» режим, в котором написана большая часть Rust. В небезопасном режиме разработчик несет ответственность за безопасность памяти кода, что полезно в случаях, когда компилятор слишком строгий. [144]
Многие из функций Rust являются так называемыми абстракциями с нулевой стоимостью , что означает, что они оптимизируются во время компиляции и не несут никаких потерь во время выполнения. [145] Система владения и заимствования допускает реализацию с нулевым копированием для некоторых задач, чувствительных к производительности, таких как синтаксический анализ . [146] Статическая диспетчеризация используется по умолчанию для устранения вызовов методов , за исключением методов, вызываемых для динамических объектов типажей. [147] Компилятор также использует встроенное расширение для устранения вызовов функций и вызовов статически отправляемых методов. [148]
Поскольку Rust использует LLVM , любые улучшения производительности в LLVM также переносятся и на Rust. [149] В отличие от C и C++, Rust позволяет переупорядочивать элементы struct и enum [150] для уменьшения размеров структур в памяти, лучшего выравнивания памяти и повышения эффективности доступа к кэшу . [151]
Rust использовался в программном обеспечении, охватывающем разные области. Первоначально Rust финансировался Mozilla в рамках разработки Servo, экспериментального параллельного браузерного движка , в сотрудничестве с Samsung . [152] Компоненты движка Servo позже были включены в движок браузера Gecko , лежащий в основе Firefox. [153] В январе 2023 года Google ( Alphabet ) объявила о поддержке сторонних библиотек Rust в Chromium и, следовательно, в базе кода ChromeOS . [154]
Rust используется в нескольких проектах серверного программного обеспечения крупных веб-сервисов . OpenDNS , служба разрешения DNS , принадлежащая Cisco , использует Rust внутри себя. [155] [156] Amazon Web Services начала разработку проектов на Rust еще в 2017 году, [157] включая Firecracker , решение для виртуализации; [158] Bottlerocket, решение для распространения и контейнеризации Linux ; [159] и Tokio — асинхронный сетевой стек. [160] Microsoft Azure IoT Edge, платформа, используемая для запуска сервисов Azure на устройствах Интернета вещей , имеет компоненты, реализованные на Rust. [161] Microsoft также использует Rust для запуска контейнерных модулей с помощью WebAssembly и Kubernetes . [162] Cloudflare , компания, предоставляющая сетевые услуги доставки контента, использует Rust в качестве механизма сопоставления шаблонов брандмауэра . [163] [164]
В операционных системах проект Rust for Linux был начат в 2021 году с целью добавления поддержки Rust в ядро Linux . [165] Поддержка Rust (наряду с поддержкой языка C и ассемблера ) была официально добавлена в версии 6.1. [166] В 2020 году Microsoft объявила, что некоторые части Microsoft Windows переписываются на Rust. По состоянию на 2023 год DWriteCore[обновлять] , системная библиотека для макетирования текста и рендеринга глифов, содержит около 152 000 строк кода Rust и около 96 000 строк кода C++, а в некоторых случаях наблюдается прирост производительности от 5 до 15 процентов. [167] Google объявила о поддержке Rust в операционной системе Android также в 2021 году. [168] [169] Другие операционные системы, написанные на Rust, включают Redox , «Unix-подобную» операционную систему и микроядро , [170] и Тесей, экспериментальная операционная система с модульным управлением состояниями. [171] [172] Rust также используется для инструментов командной строки и компонентов операционной системы, включая stratisd , менеджер файловой системы [173] [174] и COSMIC, среду рабочего стола от System76 . [175] [176]
В веб-разработке менеджер пакетов npm начал использовать Rust в 2019 году . [177] [178] [179] Deno , безопасная среда выполнения для JavaScript и TypeScript , построена на V8 , Rust и Tokio. [180] Другие заметные разработки в этой области включают Ruffle , эмулятор SWF с открытым исходным кодом , [181] и Polkadot , блокчейн- платформу с открытым исходным кодом и криптовалюту . [182]
Discord , социальная платформа для обмена мгновенными сообщениями, использует Rust для некоторых частей своей серверной части, а также для кодирования видео на стороне клиента . [183] В 2021 году Dropbox объявила об использовании Rust для сервиса захвата экрана, видео и изображений. [184] Facebook ( Meta ) использовал Rust для Mononoke, сервера для системы контроля версий Mercurial . [185]
В опросе разработчиков Stack Overflow 2023 года 13% респондентов недавно провели обширную разработку на Rust. [186] В ходе опроса Rust также назывался «самым любимым языком программирования» каждый год с 2016 по 2023 год (включительно), исходя из количества разработчиков, заинтересованных в продолжении работы на том же языке. [187] [примечание 8] В 2023 году Rust заняла 6-е место среди «самых востребованных технологий», причем 31% разработчиков, которые в настоящее время не работают в Rust, выразили заинтересованность в этом. [186]
Rust Foundation — это некоммерческая членская организация, зарегистрированная в США , основной целью которой является поддержка технического проекта в качестве юридического лица и помощь в управлении товарными знаками и инфраструктурными активами. [190] [43]
Он был создан 8 февраля 2021 года пятью корпоративными членами-учредителями (Amazon Web Services, Huawei, Google, Microsoft и Mozilla). [191] Совет фонда возглавляет Шейн Миллер. [192] С конца 2021 года ее исполнительным директором и генеральным директором станет Ребекка Рамбул. [193] До этого временным исполнительным директором была Эшли Уильямс. [194]
Проект Rust состоит из команд , отвечающих за различные области разработки. Команда компилятора разрабатывает, управляет и оптимизирует внутренние компоненты компилятора; а языковая группа разрабатывает новые функции языка и помогает их реализовать. На сайте проекта Rust перечислены 9 команд топ-уровня по состоянию на январь 2024 года [обновлять]. [195] Представители команд образуют Лидерский совет, который курирует проект Rust в целом. [196]
i32
по умолчанию или выводится из контекста.str
В отличие от них , String
UTF-8 всегда допустимы и могут содержать внутренние нули.Те из вас, кто знаком со стилем Elm, возможно, поймут, что обновленные сообщения
--explain
во многом вдохновлены подходом Elm.
Они вдохновлены… типами владения и заимствованными указателями из языка программирования Rust.
Оба являются языками с фигурными скобками и синтаксисом, подобным C, что делает их непугающими для программистов C.
Мы наблюдаем большую разницу в издержках проверенного индексирования: 23,6% тестов сообщают о значительном снижении производительности от проверенного индексирования, но 64,5% сообщают о незначительном влиянии или его отсутствии, и, что удивительно, 11,8% сообщают об улучшении производительности... В конечном итоге , хотя неконтролируемое индексирование может повысить производительность, в большинстве случаев это не так.
... В то время как некоторые компиляторы (например, Rust) поддерживают переупорядочение структур [82], компиляторам C и C++ запрещено переупорядочивать структуры данных (например, структуру или класс) [74] ...