Rust — это язык программирования общего назначения , делающий акцент на производительности , безопасности типов и параллелизме . Он обеспечивает безопасность памяти , то есть все ссылки указывают на допустимую память. Он делает это без традиционного сборщика мусора ; вместо этого как ошибки безопасности памяти, так и гонки данных предотвращаются «проверкой заимствований», которая отслеживает время жизни объектов ссылок во время компиляции .
Rust не навязывает парадигму программирования , но на него повлияли идеи функционального программирования , включая неизменяемость , функции высшего порядка , алгебраические типы данных и сопоставление с образцом . Он также поддерживает объектно-ориентированное программирование с помощью структур, перечислений , черт и методов. Он популярен для системного программирования . [13] [14] [15]
Разработчик программного обеспечения Грейдон Хоар создал Rust как личный проект во время работы в Mozilla Research в 2006 году. Mozilla официально спонсировала проект в 2009 году. В последующие годы после первого стабильного релиза в мае 2015 года Rust был принят такими компаниями, как Amazon , Discord , Dropbox , Google ( Alphabet ), Meta и Microsoft . В декабре 2022 года он стал первым языком, кроме C и ассемблера , который поддерживался при разработке ядра Linux .
Rust был отмечен за свое быстрое внедрение и изучался в исследованиях теории языков программирования .
Rust начинался как личный проект в 2006 году сотрудника Mozilla Грейдона Хоара. [16] Хоар заявил, что Rust был назван в честь группы грибов , которые «сверхусовершенствованы для выживания». [16] В период между 2006 и 2009 годами Rust не был представлен другим сотрудникам Mozilla и был написан в свободное время Хоара; [17] Хоар начал говорить о языке около 2009 года после того, как небольшая группа в Mozilla заинтересовалась проектом. [18] Хоар подчеркнул приоритетность хороших идей из старых языков над новыми разработками, ссылаясь на такие языки, как CLU (1974), BETA (1975), Mesa (1977), NIL (1981), Erlang (1987), Newsqueak (1988), Napier (1988), Hermes (1990), Sather (1990), Alef (1992) и Limbo (1996) как на оказавшие влияние, заявив, что «многие старые языки [лучше] новых», и описав язык как «технологию из прошлого, пришедшую, чтобы спасти будущее от самого себя». [17] [18] Ранний разработчик Rust Маниш Горегаокар аналогичным образом описал Rust как основанный на «в основном на многолетних исследованиях». [16]
В ранние годы компилятор Rust был написан примерно на 38 000 строках OCaml . [17] [19] Ранний Rust содержал такие функции, как явное объектно-ориентированное программирование с помощью obj
ключевого слова (позже удаленного) и систему для так называемых typestates , которая позволяла отслеживать переменные типа вместе с изменениями состояния (например, переход от неинициализированного к инициализированному). Функции были чистыми по умолчанию, что означало, что побочные эффекты (например, чтение или запись в файл) не допускались без явной аннотации. [17]
Mozilla официально спонсировала проект Rust в 2009 году. [16] Брендан Эйх и другие руководители, заинтригованные возможностью использования Rust для безопасного движка веб-браузера , назначили инженеров на проект, включая Патрика Уолтона, Нико Мацакиса, Феликса Клока и Маниша Горегаокара. Конференц-зал, занятый разработчиками проекта, был окрещен «пещерой ботаников», с табличкой, размещенной снаружи двери. [16]
В этот период времени работа перешла от первоначального компилятора OCaml к самостоятельным компиляторам , т. е . написанным на Rust, на основе LLVM . [20] [примечание 4] Система собственности Rust также была введена в действие к 2010 году. [16] Логотип Rust был разработан в 2011 году на основе велосипедной цепной передачи . [22]
Первый публичный релиз Rust 0.1 был выпущен в январе 2012 года. [20] [ требуется лучший источник ] В начале 2010-х годов наблюдалось увеличение участия добровольцев с открытым исходным кодом за пределами Mozilla и за пределами Соединенных Штатов. В Mozilla руководители в конечном итоге наняли более дюжины инженеров для работы над Rust на постоянной основе в течение следующего десятилетия. [16]
Годы с 2012 по 2015 были отмечены существенными изменениями в системе типов Rust , в частности, удалением системы typestate, консолидацией других языковых функций и удалением сборщика мусора . [17] [16] Управление памятью через систему владения постепенно консолидировалось и расширялось для предотвращения ошибок, связанных с памятью. К 2013 году функция сборщика мусора использовалась редко и была удалена командой в пользу системы владения. [16] Другие изменения за это время включали удаление чистых функций , которые были объявлены явной pure
аннотацией, в марте 2013 года. [23] Специализированная поддержка синтаксиса для каналов и различных типов указателей была удалена для упрощения языка. [17]
Расширение и консолидация Rust происходили под влиянием разработчиков, пришедших из C++ (например, низкоуровневая производительность функций), скриптовых языков (например, Cargo и управление пакетами) и функционального программирования (например, разработка систем типов). [17]
Грейдон Хоар ушел из Rust в 2013 году. [16] Это позволило ему развиваться органично в рамках более федеративной структуры управления с «основной командой» из 6 человек изначально, 30-40 разработчиков в различных других командах в общей сложности и процессом запроса комментариев (RFC) для новых языковых функций, добавленным в марте 2014 года. [17] Основная команда выросла до 9 человек к 2016 году с более чем 1600 предложенными RFC. [17]
В январе 2014 года главный редактор журнала Dr. Dobb's Journal Эндрю Бинсток прокомментировал шансы Rust стать конкурентом C++ , наряду с D , Go и Nim (тогда Nimrod). По словам Бинстока, хотя Rust «широко рассматривался как удивительно элегантный язык», его принятие замедлилось, поскольку он радикально менялся от версии к версии. [24] Разработка Rust в то время была сосредоточена на завершении функций языка и переходе к версии 1.0, чтобы достичь обратной совместимости и превратить язык в продукт для потенциального принятия в отрасли. [17]
Спустя шесть лет после того, как Mozilla спонсировала его разработку, 15 мая 2015 года был опубликован первый стабильный релиз Rust 1.0. [16] Через год после выпуска компилятор Rust собрал более 1400 участников, а на сайте управления пакетами Rust Crates.io было опубликовано более 5000 сторонних библиотек. [17]
Разработка браузерного движка Servo продолжалась параллельно с Rust. Команды, стоящие за двумя проектами, работали в тесном сотрудничестве; новые функции в Rust тестировались командой Servo, а новые функции в Servo использовались для обратной связи с командой Rust. [17] Первая версия Servo была выпущена в 2016 году . [16] Веб- браузер Firefox поставлялся с кодом Rust по состоянию на 2016 год (версия 45); [17] [25] , но компоненты Servo не появлялись в Firefox до сентября 2017 года (версия 57) как часть проектов Gecko и Quantum . [26]
В течение нескольких лет после версии 1.0 были внесены улучшения в экосистему инструментов Rust, включая Rustfmt, интегрированную интеграцию среды разработки , регулярный цикл тестирования и выпуска компилятора, кодекс поведения сообщества и обсуждения сообщества, организованные через чат IRC . [17]
Самым ранним принятием за пределами Mozilla стали отдельные проекты в Samsung , Facebook (теперь Meta Platforms ), Dropbox и других, включая Tilde, Inc. (компания, стоящая за ember.js ). [17] [16] В 2020 году последовала за ней Amazon Web Services . [16] Инженеры назвали производительность, отсутствие сборщика мусора, безопасность и приятность работы на языке причинами принятия, признав при этом, что это была рискованная ставка, поскольку Rust был новой технологией. Разработчики Amazon сослались на тот факт, что Rust потребляет вдвое меньше электроэнергии , чем аналогичный код, написанный на Java , уступая только C , [16] как было обнаружено в исследовании в Университете Минью , Университете NOVA в Лиссабоне и Университете Коимбры . [27] [примечание 5]
В августе 2020 года Mozilla уволила 250 из 1000 своих сотрудников по всему миру в рамках корпоративной реструктуризации, вызванной пандемией COVID-19 . [28] [29] Команда Servo была расформирована. Это событие вызвало опасения по поводу будущего Rust из-за совпадения двух проектов. [30] На следующей неделе основная команда Rust признала серьезное влияние увольнений и объявила о планах создания фонда Rust. Первой целью фонда будет принятие на себя всех товарных знаков и доменных имен , а также финансовая ответственность за их расходы. [31]
8 февраля 2021 года пять компаний-основателей: Amazon Web Services , Google , Huawei , Microsoft и Mozilla объявили о создании Rust Foundation . [32] [33] Фонд, возглавляемый Шейном Миллером в течение первых двух лет, предлагал гранты в размере 20 000 долларов и другую поддержку программистам, работающим над основными функциями Rust. [16] В сообщении в блоге , опубликованном 6 апреля 2021 года, Google объявила о поддержке Rust в рамках Android Open Source Project в качестве альтернативы C/C++. [34]
22 ноября 2021 года команда модераторов, которая отвечала за соблюдение кодекса поведения сообщества, объявила о своей отставке «в знак протеста против того, что основная команда возложила на себя ответственность только перед собой». [35] В мае 2022 года основная команда Rust, другие ведущие программисты и некоторые члены совета Rust Foundation провели реформы управления в ответ на инцидент. [36]
6 апреля 2023 года Rust Foundation опубликовал проект новой политики в отношении товарных знаков, включая правила использования логотипа и названия Rust, что вызвало негативную реакцию пользователей и участников Rust. [37]
Синтаксис Rust похож на синтаксис C и C++, [38] [39] хотя многие из его функций были созданы под влиянием функциональных языков программирования, таких как OCaml . [40] Хоар описал Rust как ориентированный на разочарованных разработчиков C++ и подчеркнул такие функции, как безопасность, управление распределением памяти и параллелизм . [18] Безопасность в Rust включает гарантии безопасности памяти, безопасности типов и отсутствия гонок данных.
Ниже представлена программа "Hello, World!" на Rust. fn
Ключевое слово обозначает функцию , а println!
макрос (см. § Макросы) выводит сообщение на стандартный вывод . [41] Операторы в Rust разделяются точкой с запятой .
fn main () { println! ( "Привет, мир!" ); }
Переменные в Rust определяются с помощью let
ключевого слова. [42] В приведенном ниже примере значение присваивается переменной с именем foo
.
fn main () { let foo = 10 ; println! ( "Значение foo равно {foo}" ); }
Переменные по умолчанию неизменяемы , а добавление mut
ключевого слова позволяет изменять переменную. [43] В следующем примере используется //
, что обозначает начало комментария . [ 44]
fn main () { let mut foo = 10 ; // Этот код не скомпилируется без добавления "mut". println! ( "Значение foo равно {foo}" ); foo = 20 ; println! ( "Значение foo равно {foo}" ); }
Несколько let
выражений могут определять несколько переменных с одинаковым именем, что известно как затенение переменных . Затенение переменных позволяет преобразовывать переменные без необходимости называть их по-разному. [45] В примере ниже объявляется новая переменная с тем же именем, которое в два раза больше исходного значения:
fn main () { let foo = 10 ; println! ( "Значение foo равно {foo}" ); let foo = foo * 2 ; println! ( "Значение foo равно {foo}" ); }
Переменное затенение также возможно для значений разных типов, начиная от строки и заканчивая ее длиной:
fn main () { пусть пробелы = " " ; пусть пробелы = пробелы . len (); }
В Rust блоки кода разделяются фигурными скобками . [46]
if
блокиУсловное if
выражение выполняет код на основе того, является ли заданное значение true
. else
может использоваться, когда значение вычисляется как false
, и может использоваться для объединения нескольких выражений. [47]else if
fn main () { let x = 10 ; if x > 5 { println! ( "значение больше пяти" ); } if x % 7 == 0 { println! ( "значение делится на 7" ); } else if x % 5 == 0 { println! ( "значение делится на 5" ); } else { println! ( "значение не делится на 7 или 5" ); } }
while
петлиwhile
может использоваться для повторения блока кода, пока выполняется условие. [48]
fn main () { // Перебираем все целые числа от 4 до 10 let mut value = 4 ; while value <= 10 { println! ( "value = {value}" ); value += 1 } }
for
циклы и итераторыЦиклы For в Rust обходят элементы коллекции. [49] Выражения «For» работают с любым типом итератора .
fn main (){ // Использование `for` с синтаксисом диапазона для той же функциональности, что и выше for value in 4 ..= 10 { println! ( "value = {value}" ); } }
В приведенном выше коде 4..=10
есть значение типа Range
, реализующее Iterator
черту. Код в фигурных скобках применяется к каждому элементу, возвращаемому итератором.
Итераторы можно комбинировать с функциями над итераторами, такими как map
, filter
, и sum
. Например, следующее складывает все числа от 1 до 100, кратные 3:
( 1 ..= 100 ). фильтр ( |& x | x % 3 == 0 ). сумма ()
loop
и break
заявленияВ более общем смысле, loop
ключевое слово позволяет повторять часть кода до тех пор, пока break
не произойдет a. break
может опционально выйти из цикла со значением. Метки, обозначенные как , могут использоваться для прерывания внешнего цикла, когда циклы вложены. [50]'label_name
fn main () { let value = 456 ; let mut x = 1 ; let y = loop { x *= 10 ; if x > value { break x / 10 ; } }; println! ( "наибольшая степень десяти, которая меньше или равна значению: {y}" ); пусть mut up = 1 ; ' внешний : цикл { пусть mut down = 120 ; цикл { если вверх > 100 { break 'внешний ; } если вниз < 4 { перерыв ; } вниз /= 2 ; вверх += 1 ; println! ( "вверх: {вверх}, вниз: {вниз}" ); } вверх *= 2 ; } }
Rust ориентирован на выражения , причем почти каждая часть тела функции является выражением , включая операторы потока управления. [51] Выражение if
используется для предоставления тернарного условного оператора . Поскольку возвраты неявные, функция не обязательно должна заканчиваться выражением return
; если точка с запятой опущена, значение последнего выражения в функции используется в качестве возвращаемого значения , [52] как показано в следующей рекурсивной реализации функции факториала :
fn факториал ( i : u64 ) -> u64 { если i == 0 { 1 } иначе { i * факториал ( i - 1 ) } }
Следующая итеративная реализация использует ..=
оператор для создания инклюзивного диапазона:
fn факториал ( i : u64 ) -> u64 { ( 2 ..= i ). продукт () }
Выражения match
и могут использоваться для сопоставления с образцом . Например, можно использовать для удвоения необязательного целочисленного значения, если оно присутствует, и возврата нуля в противном случае: [53]if let
match
fn double ( x : Option < u64 > ) -> u64 { match x { Some ( x ) => x * 2 , None => 0 , } }
Эквивалентно это можно записать с помощью и :if let
else
fn double ( x : Option < u64 > ) -> u64 { if let Some ( x ) = x { x * 2 } else { 0 } }
Rust является строго типизированным и статически типизированным . Типы всех переменных должны быть известны во время компиляции; присвоение значения определенного типа переменной с другим типом приводит к ошибке компиляции . Вывод типа используется для определения типа переменных, если он не указан. [54]
Целочисленный тип по умолчанию — i32
, а тип с плавающей точкой по умолчанию — f64
. Если тип литерального числа явно не указан, он либо выводится из контекста, либо используется тип по умолчанию. [55]
Целочисленные типы в Rust именуются на основе знака и количества бит, которые занимает тип. Например, i32
— это знаковое целое число, которое занимает 32 бита памяти, тогда как u8
— беззнаковое и занимает только 8 бит памяти. isize
и usize
занимают память в зависимости от архитектуры компьютера, на котором выполняется код, например, на компьютерах с 32-битной архитектурой оба типа будут занимать 32 бита пространства.
По умолчанию целочисленные литералы имеют основание 10, но поддерживаются различные основания с префиксами, например, 0b11
для двоичных чисел , 0o567
для восьмеричных чисел и 0xDB
для шестнадцатеричных чисел . По умолчанию целочисленные литералы по умолчанию имеют i32
тип . Такие суффиксы, как , 4u32
могут использоваться для явного задания типа литерала. [56] Байтовые литералы, как , b'X'
доступны для представления значения ASCII (в u8
) определенного символа. [57]
Булев тип называется , bool
который может принимать значение либо , true
либо false
. A char
занимает 32 бита и представляет скалярное значение Unicode: кодовую точку Unicode, которая не является суррогатом . [58] Числа с плавающей точкой IEEE 754 поддерживаются с помощью f32
для чисел с плавающей точкой одинарной точности и f64
для чисел с плавающей точкой двойной точности . [59]
Пользовательские типы создаются с помощью ключевых слов struct
или enum
. struct
Ключевое слово используется для обозначения типа записи , который группирует несколько связанных значений. [60] enum
s может принимать различные варианты во время выполнения, с его возможностями, аналогичными алгебраическим типам данных, найденным в функциональных языках программирования. [61] Как структуры, так и перечисления могут содержать поля с различными типами. [62] Альтернативные имена для одного и того же типа могут быть определены с помощью type
ключевого слова . [63]
Ключевое impl
слово может определять методы для типа, определяемого пользователем. Данные и функции определяются отдельно. Реализации выполняют роль, аналогичную роли классов в других языках. [64]
Option
значения обрабатываются с помощью синтаксического сахара , такого как if let
конструкция, для доступа к внутреннему значению (в данном случае к строке): [80]
fn main () { let name1 : Option <& str > = None ; // В этом случае ничего не будет выведено, если let Some ( name ) = name1 { println! ( "{name}" ); } let name2 : Option <& str > = Some ( "Matthew" ); // В этом случае слово "Matthew" будет выведено , если let Some ( name ) = name2 { println! ( "{name}" ); } }
Rust не использует нулевые указатели для указания на отсутствие данных, так как это может привести к разыменованию null . Соответственно, базовые &
и &mut
ссылки гарантированно не будут нулевыми. Вместо этого Rust использует Option
для этой цели: Some(T)
указывает на наличие значения и None
является аналогом нулевого указателя. [81] реализует «оптимизацию нулевого указателя», избегая любых пространственных издержек для типов, которые не могут иметь нулевого значения ( например, Option
ссылки или типы). [82]NonZero
В отличие от ссылок, типы сырых указателей *const
и *mut
могут быть нулевыми; однако, их невозможно разыменовать, если код явно не объявлен небезопасным с помощью блока unsafe
. В отличие от разыменования, создание сырых указателей разрешено внутри безопасного кода Rust. [83]
Rust не обеспечивает неявного преобразования типов (приведения) между примитивными типами. Но явное преобразование типов (приведение) может быть выполнено с использованием as
ключевого слова. [84]
пусть x = 1000 ; println! ( "1000 как u16 это: {}" , x как u16 );
Система владения Rust состоит из правил, которые обеспечивают безопасность памяти без использования сборщика мусора. Во время компиляции каждое значение должно быть прикреплено к переменной, называемой владельцем этого значения, и каждое значение должно иметь ровно одного владельца. [85] Значения перемещаются между разными владельцами посредством назначения или передачи значения в качестве параметра функции. Значения также могут быть заимствованы, то есть они временно передаются другой функции перед возвратом владельцу. [86] С помощью этих правил Rust может предотвратить создание и использование висячих указателей : [86] [87]
fn print_string ( s : String ) { println! ( "{}" , s ); } fn main () { let s = String :: from ( "Hello, World" ); print_string ( s ); // s используется print_string // s был перемещен, поэтому больше не может использоваться // еще одна print_string(s); приведет к ошибке компиляции }
Из-за этих правил владения типы Rust известны как линейные или аффинные типы, что означает, что каждое значение может быть использовано ровно один раз. Это обеспечивает форму изоляции сбоев программного обеспечения , поскольку владелец значения несет исключительную ответственность за его корректность и освобождение. [88]
Когда значение выходит из области видимости, оно удаляется путем запуска его деструктора . Деструктор может быть программно определен путем реализации Drop
черты. Это помогает управлять ресурсами, такими как дескрипторы файлов, сетевые сокеты и блокировки , поскольку при удалении объектов связанные с ними ресурсы автоматически закрываются или освобождаются. [89]
Время жизни объекта относится к периоду времени, в течение которого ссылка действительна; то есть, времени между созданием и уничтожением объекта. [90] Эти времена жизни неявно связаны со всеми типами ссылок Rust. Хотя они часто подразумеваются, их также можно указать явно с помощью именованных параметров времени жизни (часто обозначаемых 'a
, 'b
и т. д.). [91]
Время жизни в Rust можно рассматривать как лексически ограниченное , что означает, что продолжительность жизни объекта выводится из набора местоположений в исходном коде (т. е. функций, строк и номеров столбцов), для которых переменная является допустимой. [92] Например, ссылка на локальную переменную имеет время жизни, соответствующее блоку, в котором она определена: [92]
fn main () { let x = 5 ; // ------------------+- Время жизни 'a // | let r = & x ; // -+-- Время жизни 'b | // | | println! ( "r: {}" , r ); // | | // | | // -+ | } // ------------------+
Затем проверка заимствований в компиляторе Rust обеспечивает, чтобы ссылки использовались только в тех местах исходного кода, где соответствующее время жизни является допустимым. [93] [94] В приведенном выше примере сохранение ссылки на переменную x
в r
допустимо, поскольку переменная x
имеет более длительное время жизни ( 'a
), чем переменная r
( 'b
). Однако, если x
имеет более короткое время жизни, проверка заимствований отклонит программу:
fn main () { let r ; // ------------------+- Время жизни 'a // | { // | let x = 5 ; // -+-- Время жизни 'b | r = & x ; // | | // Здесь ОШИБКА: x не живет достаточно долго } // -| | // | println! ( "r: {}" , r ); // | } // ------------------+
Поскольку время жизни переменной, на которую делается ссылка ( 'b
), короче времени жизни переменной, содержащей ссылку ( 'a
), проверка заимствований выдает ошибку, не позволяя x
использовать ее за пределами ее области действия. [95]
Время жизни может быть указано с помощью явных параметров времени жизни в аргументах функции. Например, следующий код указывает, что ссылка, возвращаемая функцией, имеет то же время жизни, что и original
(и не обязательно то же время жизни, что и prefix
): [96]
fn remove_prefix <' a > ( mut original : & ' a str , prefix : & str ) -> & ' a str { if original . starts_with ( prefix ) { original = original [ prefix . len () .. ]; } original }
Когда определяемые пользователем типы содержат ссылки на данные, им также необходимо использовать параметры времени жизни. В примере ниже анализируются некоторые параметры конфигурации из строки и создается структура, содержащая параметры. Функция parse_config
также демонстрирует исключение времени жизни, что снижает необходимость явного определения параметров времени жизни. [97]
использовать std :: collections :: HashMap ; // Эта структура имеет один параметр времени жизни, 'src. Имя используется только в определении структуры. #[derive(Debug)] struct Config <' src > { hostname : & ' src str , username : & ' src str , } // Параметр '_lifetime в этом случае относится к анонимному времени жизни, прикрепленному к типу // аргумента `config`. fn parse_config ( config : & str ) -> Config <' _ > { let key_values : HashMap < _ , _ > = config .lines ( ) .filter ( | line | ! line .starts_with ( '#' )) .filter_map ( | line | line .split_once ( ' =' )) .map ( | ( key , value ) | ( key.trim ( ), value.trim ( ) )) .collect ( ); Config { hostname : key_values [ " hostname " ], username : key_values [ " username " ] , } } fn main () { let config = parse_config ( r#"hostname = foobar username=barfoo"# , ); println! ( "Проанализированная конфигурация: {:#?}" , config ); }
В компиляторе владение и время жизни работают вместе, чтобы предотвратить проблемы безопасности памяти, такие как зависшие указатели. [98]
Более продвинутые возможности Rust включают использование универсальных функций . Универсальной функции задаются универсальные параметры , которые позволяют применять одну и ту же функцию к разным типам переменных. Эта возможность уменьшает дублирование кода [99] и известна как параметрический полиморфизм .
Следующая программа вычисляет сумму двух величин, для которой сложение реализовано с помощью универсальной функции:
использовать 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 ); println! ( "Сумма: {}" , result1 ); // Сумма: 30 let result2 = sum ( 10.23 , 20.45 ); println! ( "Сумма равна: {}" , result2 ); // Сумма равна: 30.68 }
Во время компиляции такие полиморфные функции, как , sum
создаются с использованием определенных типов, требуемых кодом; в данном случае это сумма целых чисел и сумма чисел с плавающей точкой.
Обобщенные функции могут использоваться в функциях, чтобы реализовать поведение для разных типов без повторения одного и того же кода. Обобщенные функции могут быть написаны относительно других обобщенных функций, без знания фактического типа. [100]
Система типов Rust поддерживает механизм, называемый чертами, вдохновленный классами типов в языке Haskell [6] , для определения общего поведения между различными типами. Например, Add
черта может быть реализована для чисел с плавающей точкой и целых чисел, которые могут быть добавлены; а черты Display
или Debug
могут быть реализованы для любого типа, который может быть преобразован в строку. Черты могут использоваться для предоставления набора общего поведения для различных типов без знания фактического типа. Эта возможность известна как полиморфизм ad hoc .
Универсальные функции могут ограничивать универсальный тип для реализации определенного признака или признаков; например, функция add_one
может требовать, чтобы тип реализовывал Add
. Это означает, что универсальная функция может быть проверена на тип, как только она определена. Реализация универсальных функций похожа на типичную реализацию шаблонов C++: для каждого экземпляра генерируется отдельная копия кода. Это называется мономорфизацией и контрастирует со схемой стирания типа, обычно используемой в Java и Haskell. Стирание типа также доступно через ключевое слово dyn
(сокращение от dynamic). [101] Поскольку мономорфизация дублирует код для каждого используемого типа, она может привести к более оптимизированному коду для конкретных случаев использования, но время компиляции и размер выходного двоичного файла также увеличиваются. [102]
В дополнение к определению методов для определяемого пользователем типа, impl
ключевое слово может использоваться для реализации признака для типа. [64] Признаки могут предоставлять дополнительные производные методы при реализации. [103] Например, признак Iterator
требует, чтобы next
метод был определен для типа. После next
определения метода признак может предоставлять общие функциональные вспомогательные методы для итератора, такие как map
или filter
. [104]
Черты Rust реализованы с использованием статической диспетчеризации , что означает, что тип всех значений известен во время компиляции; однако Rust также использует функцию, известную как объекты черт, для выполнения динамической диспетчеризации (также известную как утиная типизация ). [105] Динамически диспетчеризируемые объекты черт объявляются с использованием синтаксиса dyn Tr
, где Tr
является чертой. Объекты черт имеют динамический размер, поэтому они должны быть помещены за указателем, например Box
. [106] Следующий пример создает список объектов, где каждый объект может быть распечатан с использованием Display
черты:
использовать std :: fmt :: Display ; пусть v : Vec < Box < dyn Display >> = vec! [ Box :: new ( 3 ), Box :: new ( 5.0 ), Box :: new ( "hi" ), ]; для x в v { println! ( "{x}" ); }
Если элемент в списке не реализует Display
черту, это вызовет ошибку времени компиляции. [107]
Rust разработан так, чтобы быть безопасным для памяти . Он не допускает нулевых указателей, висячих указателей или гонок данных . [108] [109] [110] Значения данных могут быть инициализированы только через фиксированный набор форм, все из которых требуют, чтобы их входные данные были уже инициализированы. [111]
Небезопасный код может обойти некоторые из этих ограничений, используя unsafe
ключевое слово. [83] Небезопасный код также может использоваться для низкоуровневой функциональности, такой как доступ к энергозависимой памяти , встроенные функции, зависящие от архитектуры, каламбуры и встроенная ассемблерная обработка. [112]
Rust не использует сборку мусора . Память и другие ресурсы вместо этого управляются с помощью соглашения «приобретение ресурсов — это инициализация» [113] с необязательным подсчетом ссылок . Rust обеспечивает детерминированное управление ресурсами с очень низкими накладными расходами [114] . Значения выделяются в стеке по умолчанию, и все динамические выделения должны быть явными. [115]
Встроенные ссылочные типы, использующие &
символ , не включают подсчет ссылок во время выполнения. Безопасность и действительность базовых указателей проверяется во время компиляции, предотвращая появление висячих указателей и других форм неопределенного поведения . [116] Система типов Rust отделяет общие неизменяемые ссылки формы &T
от уникальных изменяемых ссылок формы &mut T
. Изменяемая ссылка может быть приведена к неизменяемой ссылке, но не наоборот. [117]
Язык Rust можно расширить с помощью макросов.
Декларативный макрос (также называемый «макросом по примеру») — это макрос, определенный с помощью macro_rules!
ключевого слова, который использует сопоставление с образцом для определения своего расширения. [118] [119] Примером является макрос println!()
. [120]
Процедурные макросы — это функции Rust, которые запускают и изменяют поток входных токенов компилятора до компиляции любых других компонентов. Они, как правило, более гибкие, чем декларативные макросы, но их сложнее поддерживать из-за их сложности. [121] [122]
Процедурные макросы бывают трех видов:
custom!(...)
#[derive(CustomDerive)]
#[custom_attribute]
Макрос rsx!
в фреймворке фронтенда Dioxus является примером макроса, похожего на функцию. [123] [124] Макрос [125] предоставляет широко используемую библиотеку для генерации кода для чтения и записи данных во многих форматах, таких как JSON .serde_derive
Макросы атрибутов обычно используются для языковых привязок, таких как библиотека для привязок Rust к R. [126]extendr
В следующем коде показано использование производных от Serialize
, Deserialize
и Debug
процедурных макросов для реализации чтения и записи JSON, а также возможность форматирования структуры для отладки.
использовать serde :: { Сериализация , Десериализация }; #[derive(Сериализация, Десериализация, Отладка)] структура Point { x : i32 , y : i32 , } fn main () { пусть точка = Точка { x : 1 , y : 2 }; пусть сериализовано = serde_json :: to_string ( & point ). unwrap (); println! ( "сериализовано = {}" , сериализовано ); пусть десериализовано : Point = serde_json :: from_str ( & сериализовано ). unwrap (); println! ( "десериализовано = {:?}" , десериализовано ); }
Rust не поддерживает вариативные аргументы в функциях. Вместо этого он использует макросы . [127]
macro_rules! calculate { // Шаблон для одного `eval` ( eval $e : expr ) => {{ { let val : usize = $e ; // Принудительно сделать типы целыми числами println! ( "{} = {}" , stringify! { $e }, val ); } }}; // Рекурсивно разложить несколько `eval`ов ( eval $e : expr , $( eval $es : expr ), + ) => {{ calculate ! { eval $e } calculate ! { $( eval $es ), + } }}; } fn main () { calculate ! { // Смотри, ма! Variadic `calculate!`! eval 1 + 2 , eval 3 + 4 , eval ( 2 * 3 ) + 1 } }
c_variadic
переключатель функций. Как и в случае с другими интерфейсами C, система считается unsafe
Rust. [128]Rust имеет интерфейс внешних функций (FFI), который может использоваться как для вызова кода, написанного на таких языках, как C , из Rust, так и для вызова кода Rust из этих языков. По состоянию на 2024 год [обновлять]существует внешняя библиотека CXX для вызова в C++ или из него. [129] Rust и C различаются тем, как они размещают структуры в памяти, поэтому структурам Rust может быть присвоен атрибут #[repr(C)]
, заставляющий использовать ту же компоновку, что и эквивалентная структура C. [130]
Экосистема Rust включает в себя компилятор, стандартную библиотеку и дополнительные компоненты для разработки программного обеспечения. Установка компонентов обычно управляется rustup
, установщиком Rust toolchain, разработанным проектом Rust. [131]
Компилятор Rust, rustc
, транслирует код Rust в низкоуровневый LLVM IR . Затем LLVM вызывается как подкомпонент для применения оптимизаций и трансляции полученного IR в объектный код . Затем компоновщик используется для объединения объектов в один исполняемый образ или двоичный файл. [132]
Помимо LLVM, компилятор также поддерживает использование альтернативных бэкэндов, таких как GCC и Cranelift, для генерации кода. [133] Цель этих альтернативных бэкэндов — увеличить покрытие платформы Rust или улучшить время компиляции. [134] [135]
Стандартная библиотека Rust определяет и реализует множество широко используемых пользовательских типов данных, включая основные структуры данных, такие как Vec
, Option
, и HashMap
, а также типы интеллектуальных указателей . Rust также предоставляет способ исключить большую часть стандартной библиотеки с помощью атрибута #![no_std]
; это позволяет приложениям, таким как встроенные устройства, которые хотят удалить код зависимости или предоставить свои собственные основные структуры данных. Внутренне стандартная библиотека разделена на три части, core
, alloc
, и std
, где std
и alloc
исключаются с помощью #![no_std]
. [136]
Cargo — это система сборки и менеджер пакетов Rust . Он загружает, компилирует, распространяет и выгружает пакеты, называемые ящиками , которые поддерживаются в официальном реестре. Он также действует как интерфейс для Clippy и других компонентов Rust. [137]
По умолчанию Cargo получает свои зависимости из пользовательского реестра crates.io , но репозитории Git и контейнеры в локальной файловой системе, а также другие внешние источники также могут быть указаны в качестве зависимостей. [138]
Rustfmt — это форматировщик кода для Rust. Он форматирует пробелы и отступы для создания кода в соответствии с общим стилем , если не указано иное. Его можно вызывать как отдельную программу или из проекта Rust через Cargo. [139]
Clippy — встроенный инструмент линтинга Rust для улучшения корректности, производительности и читаемости кода Rust. По состоянию на 2024 год [обновлять]он содержит более 700 правил. [140] [141]
После Rust 1.0 новые функции разрабатываются в ночных версиях, которые выпускаются ежедневно. В течение каждого шестинедельного цикла выпуска изменения в ночных версиях выпускаются в бета-версии, в то время как изменения из предыдущей бета-версии выпускаются в новой стабильной версии. [142]
Каждые два или три года выпускается новая «редакция». Редакции выпускаются для того, чтобы разрешить внесение ограниченных критических изменений , таких как повышение await
до ключевого слова для поддержки функций async/await . Ящики, ориентированные на разные редакции, могут взаимодействовать друг с другом, поэтому ячейка может обновиться до новой редакции, даже если ее вызывающие или ее зависимости все еще нацелены на старые редакции. Миграция на новую редакцию может быть выполнена с помощью автоматизированных инструментов. [143]
rust-analyzer — это набор утилит, которые предоставляют интегрированным средам разработки (IDE) и текстовым редакторам информацию о проекте Rust через протокол Language Server Protocol . Это позволяет использовать такие функции, как автодополнение и отображение ошибок компиляции во время редактирования. [144]
В целом, гарантии безопасности памяти Rust не накладывают дополнительных расходов во время выполнения. [145] Заметным исключением является индексация массива , которая проверяется во время выполнения, хотя это часто не влияет на производительность. [146] Поскольку Rust не выполняет сборку мусора, он часто быстрее других языков, безопасных для памяти. [147] [88] [148]
Rust предоставляет два «режима»: безопасный и небезопасный. Безопасный режим — это «нормальный», в котором написана большая часть Rust. В небезопасном режиме разработчик отвечает за безопасность памяти кода, которая используется разработчиками в случаях, когда компилятор слишком ограничителен. [149]
Многие из функций Rust являются так называемыми абстракциями с нулевой стоимостью , то есть они оптимизируются во время компиляции и не влекут за собой никаких штрафов во время выполнения. [150] Система владения и заимствования допускает реализации с нулевым копированием для некоторых задач, чувствительных к производительности, таких как синтаксический анализ . [151] Статическая диспетчеризация используется по умолчанию для устранения вызовов методов , за исключением методов, вызываемых для динамических объектов-характеристик. [152] Компилятор также использует встроенное расширение для устранения вызовов функций и статически диспетчеризованных вызовов методов. [153]
Поскольку Rust использует LLVM , любые улучшения производительности в LLVM также переносятся в Rust. [154] В отличие от C и C++, Rust позволяет переупорядочивать элементы структур и перечислений [155] для уменьшения размеров структур в памяти, лучшего выравнивания памяти и повышения эффективности доступа к кэшу . [156]
Rust использовался в программном обеспечении в различных областях. Rust изначально финансировался Mozilla как часть разработки Servo , экспериментального параллельного браузерного движка, в сотрудничестве с Samsung . [157] Компоненты из движка Servo были позже включены в браузерный движок Gecko , лежащий в основе Firefox. [158] В январе 2023 года Google ( Alphabet ) объявила о поддержке использования сторонних библиотек Rust в Chromium . [159] [160]
Rust используется в нескольких внутренних программных проектах крупных веб-сервисов . OpenDNS , служба разрешения DNS , принадлежащая Cisco , использует Rust внутри компании. [161] [162] Amazon Web Services использует Rust в «чувствительных к производительности компонентах» своих нескольких сервисов. В 2019 году AWS открыл исходный код Firecracker , решения для виртуализации, в основном написанного на Rust. [163] Microsoft Azure IoT Edge, платформа, используемая для запуска сервисов Azure на устройствах IoT , имеет компоненты, реализованные на Rust. [164] Microsoft также использует Rust для запуска контейнерных модулей с WebAssembly и Kubernetes . [165] Cloudflare , компания, предоставляющая сетевые сервисы доставки контента , использовала Rust для создания нового веб-прокси под названием Pingora для повышения производительности и эффективности. [166] Менеджер пакетов npm начал использовать Rust для своей службы аутентификации в производственной среде в 2019 году. [167] [168] [169]
В операционных системах разработчики Android использовали Rust в 2021 году для переписывания существующих компонентов. [170] [171] Проект Rust для Linux , запущенный в 2020 году, добавил начальную поддержку Rust в Linux в конце 2022 года, а первые драйверы Linux, написанные на Rust, были выпущены в конце 2023 года. [172] [173] Microsoft переписывает части Windows на Rust. [174] Проект r9 направлен на повторную реализацию Plan 9 от Bell Labs на Rust. [175] Rust использовался при разработке новых операционных систем, таких как Redox , «Unix-подобная» операционная система и микроядро , [176] Theseus, экспериментальная операционная система с модульным управлением состоянием, [177] [178] и большая часть Fuchsia . [179] Rust также используется для инструментов командной строки и компонентов операционной системы, включая stratisd , менеджер файловой системы [180] [181] и COSMIC, среду рабочего стола от System76 . [182]
В веб-разработке Deno , безопасная среда выполнения для JavaScript и TypeScript , построена на основе V8 с использованием Rust и Tokio. [183] Другие заметные внедрения в этой области включают Ruffle , эмулятор SWF с открытым исходным кодом , [184] и Polkadot , блокчейн и криптовалютную платформу с открытым исходным кодом . [185]
Discord , компания, занимающаяся программным обеспечением для обмена мгновенными сообщениями , переписала части своей системы на Rust для повышения производительности в 2020 году. В том же году Dropbox объявила, что ее синхронизация файлов была переписана на Rust. Facebook ( Meta ) также использовала Rust для перепроектирования своей системы, которая управляет исходным кодом для внутренних проектов. [16]
В опросе разработчиков Stack Overflow 2023 года 13% респондентов недавно вели обширную разработку на Rust. [186] Опрос также назвал Rust «самым любимым языком программирования» каждый год с 2016 по 2023 год (включительно) и «самым почитаемым языком программирования» в 2024 году на основе количества разработчиков, заинтересованных в продолжении работы на том же языке. [187] [примечание 7] В 2023 году Rust был 6-й «самой востребованной технологией», при этом 31% разработчиков, в настоящее время не работающих на Rust, выразили заинтересованность в этом. [186]
Rust изучался в академических исследованиях, как с точки зрения свойств самого языка, так и полезности, которую язык предоставляет для написания программного обеспечения, используемого для исследований. Были изучены его особенности, касающиеся безопасности [188] [149] и производительности [189] .
В журнальной статье, опубликованной в Proceedings of the International Astronomical Union , астрофизики Бланко-Куаресма и Больмонт повторно реализовали программы, отвечающие за моделирование многопланетных систем на Rust, и обнаружили, что он является конкурентоспособным языком программирования благодаря своей «скорости и точности». [14] Аналогичным образом, статья, опубликованная в Nature, поделилась несколькими историями о биоинформатиках, использующих Rust из-за его производительности и безопасности. [137] Однако обе статьи ссылались на уникальные концепции Rust, включая его систему собственности, которую трудно изучить, как на один из главных недостатков принятия Rust.
Rust был отмечен как инклюзивное сообщество, и особенно приветствовал людей из квир-сообщества , отчасти из-за его кодекса поведения , который обрисовал набор ожиданий для членов сообщества Rust, которым нужно следовать. Одна статья MIT Technology Review описала сообщество Rust как «необычно дружелюбное» к новичкам. [16] [137]
Rust Foundation — некоммерческая организация, зарегистрированная в США , основными целями которой являются поддержка технического проекта в качестве юридического лица и помощь в управлении товарными знаками и инфраструктурными активами. [192] [39]
Фонд был создан 8 февраля 2021 года пятью корпоративными членами-основателями (Amazon Web Services, Huawei, Google, Microsoft и Mozilla). [193] Совет директоров фонда возглавляет Шейн Миллер. [194] С конца 2021 года его исполнительным директором и генеральным директором является Ребекка Рамбул. [195] До этого Эшли Уильямс была временным исполнительным директором. [39]
Проект Rust состоит из команд , которые отвечают за различные подобласти разработки. Команда компилятора разрабатывает, управляет и оптимизирует внутренние компоненты компилятора; а команда языка проектирует новые языковые функции и помогает их внедрять. На веб-сайте проекта Rust перечислены 6 команд высшего уровня по состоянию на июль 2024 года [обновлять]. [196] Представители команд формируют Совет лидеров, который курирует проект Rust в целом. [197]
str
и String
всегда являются допустимыми UTF-8 и могут содержать внутренние нули.Те из вас, кто знаком со стилем Elm, могут заметить, что обновленные сообщения
--explain
во многом вдохновлены подходом Elm.
Они вдохновлены ... типами владения и заимствованными указателями в языке программирования Rust.
Оба языка являются языками с фигурными скобками и синтаксисом, похожим на C, что делает их неустрашимыми для программистов на C.
Мы наблюдаем большую дисперсию в накладных расходах проверенной индексации: 23,6% тестов сообщают о значительном падении производительности из-за проверенной индексации, но 64,5% сообщают о незначительном влиянии или его отсутствии, и, что удивительно, 11,8% сообщают об улучшении производительности ... В конечном счете, хотя непроверенная индексация может улучшить производительность, в большинстве случаев этого не происходит.
... Хотя некоторые компиляторы (например, Rust) поддерживают переупорядочивание структур [82], компиляторам C и C++ запрещено переупорядочивать структуры данных (например, struct или class) [74] ...