Это ознакомительная статья о языке программирования Rust и его инструментах, с помощью которой я надеюсь привлечь ваше внимание к этому интересному и уникальному языку, созданному чтобы дать ответ на следующие вопросы разработчиков:
Как убедиться, что в моем приложении нет проблем и уязвимостей, связанных с неправильной работой с памятью?
Как быть уверенным в том, что любой доступ к общим объектам правильно защищен?
Как свести к минимуму любую работу, не связанную напрямую с написанием кода?
Цель данной статьи не рассказать о доселе невиданных возможностях Rust (сразу говорю, что тут ничего нового "Растоманы" не найдут), на Хабре вы итак найдете множество интересных статей о внутренностях языка и интересных случаев использования. Моя цель рассказать о том, что он предлагает в качестве решения обозначенных выше проблем, как это будет выглядеть со стороны программиста, и почему это важно.
История одного языка
Все началось в 2006 году как персональный проект сотрудника Mozilla - Грейдона Хоара (Graydon Hoare). В 2009 году Грейдон представил свой новый язык программирования коллегам из Mozilla Research, предложив его как перспективную замену C++, поскольку он разрабатывался с приоритетом на безопасность и эффективность создания параллельных приложений (современные браузеры, такие как Mozilla Firefox, являются крупными и очень сложными проектами с не тривиальными алгоритмами параллельной обработки данных). Вскоре, на ежегодном саммите Mozilla 2010, Грейдон представил язык Rust и анонсировал проект Servo (браузерный движок нового поколения). Вначале сообщество разработчиков отнеслось к этой инициативе довольно скептически и некоторые обвиняли Mozilla в синдроме NIH (Not Invented Here). Они назвали его ещё одним бесполезным языком программирования, который умрёт ещё до того, как он станет достаточно популярным, чтобы иметь хоть какое-то значение (вспоминая язык D и его судьбу). Однако дальнейшее развитие событий показало, что это не очередной академический проект для тестирования идей, а полноценный конкурент для таких мастодонтов, как C/C++.
В 2011 г. Rust, используя LLVM, успешно скомпилировал сам себя, а в 2015 году была выпущена первая стабильная версия 1.0. И, наконец, в 2017 году Mozilla выпустила свой первый полноценный проект, написанный на Rust, который был успешно интегрирован в Firefox - Quantum CSS - полностью мультипоточный CSS-движок, в основу которого взят результат разработки прототипа Servo. На этом этапе Mozilla доказала, что язык Rust уже зрелый и обладает достаточной функциональностью для создания больших и сложных production-ready приложений.
Прошло уже больше пяти лет с момента первого стабильного релиза. От изначальной версии Rust, показанной на презентации в 2010 году, уже мало что осталось. Полистав ту презентацию, вы можете легко заметить, что тогда язык был совершенно иным: другой синтаксис, зеленые потоки вместо системных и т.д. Rust сильно изменился во всех аспектах и стал языком программирования общего назначения, который подходит для любых задач. Этот результат был достигнут в основном благодаря открытому и дружелюбному сообществу, создавшему инфраструктуру, статьи, книги, учебные пособия и полезные инструменты. В настоящее время Rust можно применять в любой сфере, независимо от того, является ли проект desktop-приложением, ядром ОС или веб-приложением для облачного сервиса.
В последние годы мнение разработчиков о языке изменилось в положительную сторону. Невероятно, но Rust является самым любимым языком пользователей Stack Overflow уже пять лет подряд! Более того, стали появляться проекты, целиком написанные на Rust, а его присутствие в популярных open-source и коммерческих проектах растет с каждым годом. Например, ripgrep - это проект с открытым исходным кодом для рекурсивного поиска в каталогах с использованием регулярных выражений. Он в 10 раз быстрее, чем GNU grep, и в 4 раза быстрее, чем Silver Searcher. В результате ripgrep был интегрирован в MS Visual Studio Code в качестве средства поиска регулярок по умолчанию. Также, в копилку доказательства хорошей производительности получаемых программ можно добавить результаты The Computer Language Benchmarks Game проекта Debian, где Rust в одной лиге с C/C++.
Сегодня множество технологических компаний выбирают Rust за его производительность, безопасность и эффективность разработки. Позвольте мне привести несколько примеров:
Apple – технологический гигант, который ставит на первое место производительность и безопасность, решил перевести низкоуровневую часть Apple Cloud Traffic на Rust, а новый функционал, по возможности, в первую очередь писать на нем.
Amazon – выбрал Rust для реализации Firecracker – проекта безопасных и быстрых microVM для бессерверных вычислений.
Microsoft – также активно исследует возможность перехода на Rust для низкоуровневых и чувствительных к производительности компонентов. Они уже попробовали в качестве эксперимента переписать некоторые низкоуровневые системные компоненты Windows.
Google – разрешает использовать Rust (кроме kernel части) для реализации своей новой операционной системы Fuchsia.
Mozilla – собирается переписать внутренние компоненты на Rust, чтобы улучшить многопоточную обработку, как часть инициативы сделать Firefox более безопасным и быстрым - Firefox Quantum.
Huawei – также исследует возможности использовать язык Rust в своих будущих проектах.
Список можно продолжить и далее: Dropbox, Facebook, Bitbucket, Discord и т.д. – все эти известные компании начали использовать Rust в частях своих серверных платформ. Обратите внимание, что не только крупные международные компании используют Rust – в нем заинтересованы и стартапы. Например, новая компания, основанная Брайаном Кантриллом (изначальный разработчик dtrace), планирует использовать Rust в качестве основного языка для реализации своего проекта. Немаловажно для всех этих компаний то, что разработчики Rust принимают решения, связанные с дизайном языка, руководствуясь обратной совместимостью и стабильностью кодовой базы.
Но, несмотря на все успехи Rust, колыбель языка Mozilla чувствовала себя не очень хорошо и, в связи с финансовыми трудностями, в 2020 году приступила к отделению проекта в независимую организацию Rust Foundation, серьезно уменьшив свое влияние на развитие языка и отпустив его в свободное плавание. Изначальный список членов организации внушает оптимизм за будущее языка:
Как вы можете видеть, в данный момент Rust является одним из самых любимых и перспективных языков программирования. Было бы ошибкой для программистов просто игнорировать все эти факты и ни разу не попробовать данный язык в деле.
Но что делает Rust таким желанным для многих разработчиков? Давайте немного углубимся в детали и изучим некоторые важные и полезные аспекты языка.
Приоритет безопасности
Если мы хотим создать приложения с минимумом уязвимостей, мы должны обратить пристальное внимание на операции управления памятью. Нарушение безопасности памяти приводит к неожиданному сбою программ и может быть использовано для несанкционированного доступа к данным пользователя. В результате пользователь сталкивается с утечкой данных и удаленным выполнением кода. К таким нарушениям относятся:
Использование памяти после освобождения (Use After Free)
Разыменование нулевого указателя (Null Dereference)
Использование неинициализированной памяти (Using uninitialized variables)
Двойное освобождение (Double Free)
Переполнение буфера (Buffer Overflow)
MSRC Security Research 2019 года (Microsoft) показал, что 70% Common Vulnerabilities and Exposures (CVEs) в их продуктах были связаны с проблемами безопасности памяти в С и С++ (думаю весьма известный график):
Кто-то может сказать, что данный отчет лишь показывает проблемы Microsoft, так как их продукты являются проприетарным программным обеспечением, тогда как продукт с открытым исходным кодом благодаря контролю «тысячи глаз» будет иметь значительно меньше проблем. На самом деле не все так радужно. Взгляните на CVEs за 2019 год (последний из доступных на данном ресурсе), найденные в ядре Linux, разрабатываемом и проверяемом ведущими отраслевыми экспертами:
Как мы видим, open-source сообщество допустили достаточно большое количество уязвимостей, связанных с безопасностью работы с памятью (примерно 65%). Я уверен, вы знаете, что такого рода проблемы тяжело найти и зачастую они проходят мимо глаз. Однако в Rust эти проблемы не могут возникнуть, как говорится, by design (если конечно не использовать unsafe, но это тема для отдельного разговора). И все это достигается на этапе компиляции!
Для этих целей Rust имеет такие элементы языка, как Ownership и Borrowing. Система Ownership является основой для всего языка. Rust гарантирует, что у любого заданного объекта на все время его жизни существует ровно один владелец:
fn main() {
let a = String::from("Hello"); // Аллоцируем объект на куче
let b = a; // меняем владение объектом с a на b
// let c = a; // Ошибка компиляции
println!("{}", b); // b теперь владелец строки
}
Кроме того, передача объекта в качестве аргумента функции также приведет к передаче права собственности на область действия аргумента функции:
fn do_smth(a: String) {
// а владеет объектом
println!("{}", a);
}
fn main() {
let a = String::from("Hello");
// Переносим объект в область видимости функции
do_smth(a);
// let b = a; // Ошибка компиляции
}
Одним из дополнительных преимуществ Rust является диагностика ошибок в базовом компиляторе. Я уверен, что вам понравятся такие дружелюбные и полезные сообщения об ошибках. В Rust каждое сообщение:
Объясняет, почему проблема произошла;
Выделяет все элементы, которые вызвали проблему;
Дает инструкции о том, как получить больше информации об ошибке, и даже предлагает возможный вариант исправления.
Для примера, посмотрите на это подробное сообщение об ошибке:
error[E0382]: use of moved value: `a`
--> src/main.rs:4:13
|
2 | let a = String::from("Hello");
| - move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait
3 | let b = a;
| - value moved here
4 | let c = a;
| ^ value used here after move
error: aborting due to previous error
For more information about this error, try `rustc --explain E0382`.
Хорошо, а когда освободится выделенная память? В Rust нету GC, поэтому волшебный runtime не придет на помощь. В таких языках как C/C++ разработчик обязан самостоятельно определять, когда пора возвратить память ОС. Данная задача не из тривиальных и требует достаточно высокую квалификацию в сложных проектах. Rust решает эту проблему по-своему: он позволяет хранить данные как в стеке, так и в куче, а память автоматически возвращается, как только переменная, которой она принадлежит, уходит за пределы области видимости:
{
let s = String::from("hello"); // создаем строку s
// различные операции над s
} // С этого момент s вышла за предел области видимости и может быть удалена
В Rust можно также заимствовать (borrow) объект, используя ссылку, чтобы избежать излишние изменения владельца и копирования. Однако для этого существуют некоторые строгие ограничения, процитирую:
В любой момент времени вы можете иметь либо одну изменяемую ссылку, либо любое количество неизменяемых ссылок.
Ссылки всегда должны быть актуальными.
Для этой цели Rust отслеживает время жизни (lifetime) для каждой ссылки. Чаще всего времена жизни объектов генерируются автоматически, но в некоторых случаях мы должны аннотировать их вручную, используя специальный синтаксис языка. Это позволяет эффективно управлять памятью без оверхеда от использования GC. Пример заимствования:
fn do_smth(a: &String) {
println!("{}", a); // Немутабельная операция
}
fn main() {
let a = String::from("Hello");
do_smth(&a); // одалживаем объект у a
println!("{}", a); // a все еще владелец объекта
}
А вот так мы можем нарушить озвученные выше ограничения. Но тут приходит borrow checker, и выписывает нам ошибку:
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // Здесь ошибка компиляции
println!("{}, {}", r1, r2);
}
В данном примере мы нарушаем одно из правил языка – в один момент времени разрешена только одна изменяемая ссылка. В этом случае мы получаем следующее сообщение:
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> wiki/src/main.rs:5:14
|
4 | let r1 = &mut s;
| ------ first mutable borrow occurs here
5 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here
6 |
7 | println!("{}, {}", r1, r2);
| -- first borrow later used here
Эти ограничения позволяют производить мутации, но в строго контролируемом стиле. Это то, с чем новые разработчики имеют наибольшее количество проблем, потому что такие языки как C/C++ позволяют мутировать объекты без каких-либо ограничений. Все эти правила были введены, чтобы предотвратить одну очень неприятную проблему при разработке многопоточных программ - data race. Но об этом поговорим чуть позже.
Rust - это язык со статической типизацией, который дополнительно имеет следующие особенности:
Разделяет переменные на изменяемые и неизменяемые.
Каждая переменная является неизменяемой по умолчанию и обязательно должна быть проинициализирована актуальным для данного типа значением (например, определенным значением enum).
Не позволяет выполнять неявное приведение типов даже для примитивов.
Проверка границ массивов (небольшая цена производительности для исключения более серьезных проблем).
В debug-режиме компилируется с проверкой integer overflow.
Как некоторые говорят, Rust - это весьма педантичный компилятор и эти неудобства могут немного надоедать, увеличивая время вхождения для новичков. Но эти неудобства языка обязательны для обеспечения безопасности работы с памятью. В ответ мы получаем нечто большее - в Rust (опять же, если не использовать unsafe) невозможны упомянутые выше ошибки: использование памяти после освобождения, разыменование нулевого указателя, использование неинициализированной памяти, двойное освобождение и переполнение буфера.
И под конец главы добавлю, что сами разработчики Rust не только о себе думают, но и активно участвуют в улучшении безопасности проекта LLVM. Например в этом году они в тесной коллаборации с LLVM добавили поддержку защиты от Stack Clash в LLVM/Clang.
Многопоточность без гонок
Последние поколения процессоров склонны наращивать количество ядер. Еще несколько лет назад максимальное количество ядер в корпоративных процессорах составляло 28, но не так давно получили распространение процессоры AMD Epyc и Kunpeng, которые подняли планку до 64 ядер! Это огромная мощность, которую очень трудно использовать эффективно. Максимальное использование ресурсов таких CPUs приводит к тому, что алгоритмы и приложения становятся все более сложными. Исторически, программирование в этом контексте всегда было трудоемким и подвержено ошибкам, поэтому разработчикам нужны инструменты, которые помогут им реализовать свои идеи без головной боли.
Fearless Concurrency - этим термином разработчики Rust называют способность своего языка помогать создавать многопоточные алгоритмы безопасно и эффективно. Давайте начнем с простого примера, как создать поток:
use std::thread;
fn main() {
// Создаем поток и запускаем на нем анонимную функцию
let handler = thread::spawn(|| {
// Тело функции
println!("I'm thread");
});
// Родительский процесс выполняется здесь
println!("I'm main thread");
// Ожидаем пока завершится поток
handler.join().unwrap();
}
В Rust потоки изолированы друг от друга с помощью системы владения объектом. Записи могут происходить только в том случае, если поток является владельцем данных или имеет изменяемую ссылку. Таким образом, в любой момент времени существует только один поток, который имеет мутабельный доступ к данным. Чтобы ограничить передачу данных между потоками, Rust поддерживает два типажа (traits): Sync и Send. Оба этих типажа реализуются автоматически, когда компилятор определяет, что тип данных соответствуют требованиям. И тут мы уже увидели одну очень приятную особенность Rust: язык различает безопасные и небезопасные типы для передачи данных между потоками .
Send – это типы, которые могут быть безопасно перемещены из одного потока в другой. Например, Arc (Atomic Reference Counter) и String - это типы Send. Один из способов передачи данных между потоками - каналы в стиле Go. Это потокобезопасная операция, и компилятор проверяет, реализует ли объект Send. Вот пример переноса объекта:
fn main() {
// Создаем канал на передачи данных
let (sender, receiver) = channel();
// Создаем новый поток и передаем туда sender
thread::spawn(move || {
// Создаем объект
let hello = String::from("Hello");
// Отправляем объект через канал
sender.send(hello).unwrap();
// Начиная с этого места, владение объектом передано через канал
// let tmp = hello; // Ошбика компиляции
});
// Получаем данные из канала
let data = receiver.recv().unwrap();
// Печатаем строку "Hello"
println!("{}", data);
}
Если разработчик попытается переместить объект, не являющийся объектом Send, в другой поток, используя захват переменной замыканием, компилятор Rust выведет нечто подобное:
`std::rc::Rc<i32>` cannot be sent between threads safely
Sync - типы, на которые можно безопасно ссылаться между потоками. Примитивные типы, такие как целое и число с плавающей запятой, являются Sync. Sync предоставляет иммутабельный доступ к объекту для нескольких потоков одновременно:
fn main() {
// Создаем объект
let tmp = 42;
// Отправляем владение объектом в Arc
let data = Arc::new(tmp);
// Создаем новую ссылку на Arc
let data_ref = data.clone();
// Создаем поток и запускаем функцию в нем
let thread = thread::spawn(move || {
// Разыменовываем объект из Arc и печатаем его
println!("{}", *data_ref);
});
// Печатаем объект
println!("{}", *data);
// Синхронизируем потоки
thread.join().unwrap();
}
В случаях, когда требуется потокобезопасная мутабельность, Rust предоставляет атомарные типы данных и исключающую блокировку через Mutex. Основное преимущество подхода Rust - это то, что нет простого способа получить доступ к объекту без блокировки мьютекса или без использования атомарного типа данных. Например, когда вы создаете мьютекс, вы также передаете в него права на собственность объекта. Метод Lock создает некий smart-pointer MutexGuard на объект внутри мьютекса, через который вы можете получить доступ к объекту в этом потоке, пока у MutexGuard не закончится время жизни:
// Аллоцируем на куче
let s = String::from("Hello, ");
// Переносим владение объектом в Mutex
let mutex = Mutex::new(s);
// Ошибка: s больше не владелец
// s.push_str("World!");
// Блокируем мьютекс и создаем MutexGuard
let mut d = mutex.lock().unwrap();
d.push_str("World!");
// Вывод: Hello, World
println!("{}", d);
// MutexGuard освобождается при выходе из области видимости
В Rust наличие изменяемого доступа к объекту гарантирует атомарность его обновления, так как ни один другой поток не может иметь параллельный доступ для чтения. На этом этапе компилятор Rust предотвращает все потенциальные состояния гонки данных во время компиляции, однако результатом такого подхода является увеличение количества неудобств при разработки приложений с многопоточной обработкой, особенно если пытаться обойти unsafe операции. Например, исследователи столкнулись со сложностями при безопасном распараллеливании алгоритмов своими руками, и в итоге решили воспользоваться помощью готовой библиотеки.
Подводя итог, в Rust вам не нужно беспокоиться, что вы где-то забыли защитить данные или обратились неправильно к общим объектам, так как компилятор скажет и укажет вам на все небезопасные операции. Это стало возможным благодаря системе Ownership и грамотной архитектуре ограничения передачи объектов между потоками.
One Tool to Rule Them All
Что мы узнали: Rust - это уникальный язык, который может сделать жизнь разработчиков программного обеспечения проще (или сложнее), а их программы безопаснее (хотя и требует более долгого времени вхождения до начала эффективного применения). Но есть еще один чрезвычайно важный аспект всех языков программирования, который требует отдельного внимания - инструментарий. Давайте вспомним, какой инструментарий используется в C/C++? На самом деле, на это трудно ответить, потому что каждый проект выбирает свой пакетный менеджер, билд-систему, формат документации и систему тестирования. Это вызывает трудности в обслуживании продуктов у мейнтейнеров.
Разработчики Rust знают об этой ситуации и понимают, что они должны обязательно предложить нечто лучшее, чтобы обеспечить все необходимые функции и уменьшить трату времени на задачи, которые не связаны непосредственно с разработкой продукта. Результат их работы они назвали Cargo. Cargo поставляется вместе с Rust и используется почти в каждом проекте (хотя без него вы все еще можете использовать компилятор rustc, как любой другой компилятор C/C++). Этот менеджер берет все процедуры обслуживания на себя и помогает вам создавать проекты, управлять зависимостями, собирать приложение, создавать документацию и тестировать проект.
Итак, давайте посмотрим, как это выглядит. Во-первых, нам нужно создать наш новый замечательный проект, просто введя следующую команду:
cargo new test-proc
Cargo создаст каталог со следующими файлами:
.
+-- .git
+-- .gitignore
+-- Cargo.lock
+-- Cargo.toml
+-- src
L-- main.rs
Как мы видим, Cargo создал git репозиторий, каталог с исходниками, TOML-файл, содержащий конфигурацию проекта, и некоторые служебные файлы. Самый интересный файл TOML. Здесь хранятся все настройки проекта, включая имя проекта, версию, список зависимостей, флаги для компилятора и другие классные вещи. Его легко читать и редактировать, а так же централизовано управлять релизами. Пример содержания файла после инициализации:
[package]
name = "test-proc" # Имя проекта
version = "0.1.0" # Версия в нотации Semver
authors = ["hackerman"] # Список авторов
edition = "2018" # Версия Rust
Далее, давайте напишем простенькую программу:
fn add(a: u32, b: u32) -> u32 {
a + b
}
fn main() {
println!("Data = {}", add(1, 2));
}
И соберём ее:
cargo build
Compiling test-proc v0.1.0 (/home/hackerman/projects/test-proc)
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Это все, что требуется от разработчика, чтобы собрать проект со всеми зависимостями. И, самое главное, при росте проекта сложность управления не сильно изменится благодаря продуманной структуре проекта и инструментов. Cargo проверяет все изменения, сканирует зависимости и обновляет их при необходимости, используя для конфигурации TOML файл.
Тестирование
Rust и Cargo уже имеют все, что нужно для написания тестов без каких-либо внешних инструментов. Вкратце, тест в Rust — это функция, аннотированная атрибутом #[test]
. Давайте для примера создадим тестовую функцию, добавив несколько строк кода в main.rs:
// Атрибут для тестовой функции
#[test]
// Имя функции это имя теста
fn it_works() {
// Тело теста
assert_eq!(add(2, 2), 4);
}
Готово! Теперь запускаем:
cargo test
Compiling test-proc v0.1.0 (/home/hackerman/projects/test-proc)
Finished test [unoptimized + debuginfo] target(s) in 0.53s
Running target/debug/deps/test_proc-7c55eae116dd19b3
running 1 test
test it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
С помощью атрибута #[cfg(test)]
можно создать отдельный подмодуль со всеми нужными вспомогательными функциями для тестов который будут использоваться только во время тестирования.
Документация
Cargo предоставляет продвинутые инструменты для создания интерактивной документации с богатыми возможностями форматирования. Rust имеет особые комментарии (///
- тройная косая черта) для документации, которые известны как комментарии к документации с markdown нотацией, которые будут использоваться для генерации HTML-документов. В этом примере мы добавили некоторую информацию о функции add()
и собрали соответствующую документацию:
/// Sums two values.
///
/// # Examples
///
/// ```
/// let arg1 = 6;
/// let arg2 = 1;
/// let answer = testproc::add(arg1, arg2);
///
/// assert_eq!(add(6, 1), answer);
/// ```
fn add(a: u32, b: u32) -> u32 {
a + b
}
И собираем документацию одной простой командой:
cargo doc
Documenting test-proc v0.1.0 (/home/hackerman/projects/test-proc)
Finished dev [unoptimized + debuginfo] target(s) in 0.82s
В результате мы получаем HTML-страницы с гиперссылками, подсветкой синтаксиса и поисковой системой:
Внешние зависимости
В Rust единица компиляции называется "crate" и может быть либо библиотекой, либо исполняемой программой.
Например, нам нужно проанализировать некоторые JSON-файлы, но мы не хотим делать это с нуля. В этом случае мы идем на постоянно растущий crates.io репозиторий и ищем подходящий crate для нас. Отлично! Мы нашли Serde - библиотека для сериализации и десериализации структур данных. Давайте попробуем импортировать ее с поддержкой json в наш проект с помощью TOML-файла:
[dependencies]
serde_json = "1.0.53"
И вызываем команду сборки проекта:
> cargo build
Compiling serde v1.0.110
Compiling ryu v1.0.4
Compiling itoa v0.4.5
Compiling serde_json v1.0.53
Compiling test-proc v0.1.0 (/home/hackerman/projects/test-proc)
Finished dev [unoptimized + debuginfo] target(s) in 7.41s
Cargo проверил все зависимости, загрузил их и собрал. Теперь вы можете пользоваться библиотекой напрямую! Раздел [dependencies]
- это место, где вы сообщаете Cargo требуемые крейты и их версии. В этом примере мы задаем семантический спецификатор версии 1.0.53. Cargo понимает Semver нотацию, и это фактически означает "любую версию, совместимый с версией 1.0.53". Для обновления крейтов до новой версии достаточно воспользоваться командой cargo update
. Кроме того, любой может поделиться своими собственным крейтом на crates.io, и вы можете это сделать лишь парой команд! С помощью крейтов можно установить дополнительные инструменты в рабочее пространство (например, ripgrep) для различных целей вашего проекта. Вдобавок, вы можете установить свой собственный сервер crate.io в сети вашей организации и быть независимыми от внешних поставщиков. Короче говоря, замечательный инструмент, который делает разработку на Rust такой гладкой и беспроблемной.
Среды разработки
Последнее, но не менее важное, о чем я хочу вам рассказать - это rust-analyzer (будущий преемник RLS), предоставляющий поддержку Rust в различных средах разработки. В данный момент, с использованием rust-analyzer или своей собственной реализации анализатора, уже поддерживаются следующие IDE и редакторы: vim, emacs, MS Visual Studio Code, IntelliJ Idea и т.д. Они имеют все необходимые функции для эффективного написания кода, такие как: дополнение кода, вывод документации и подсветка ошибок. Количество поддерживаемых интегрированных сред достаточно велико, чтобы вы могли себя чувствовать, как дома.
Эпилог
Если вы уже заинтересованы в этом языке, существует очень подробное учебное пособие. И, если вы хотите углубиться в особенности языка, статьи о темных и сложных сторонах Rust - Rustonomicon. Как вы видите Rust весьма интересный язык с дружелюбными инструментами и с приоритетом безопасности, но со своими нюансами, поэтому, чтобы оценить его достоинства и недостатки я бы предложил вам просто попробовать Rust в своем личном проекте, и, возможно, вы станете еще одним из поклонников этого языка.
alexxisr
Интересная штука. Только сходу я не понял 2 вещи:
snuk182
Со списком все просто — элемент передается списку во владение. По поводу не-гита — вопрос стоит несколько шире, но еще не решен.
agalakhov
Связный список технически можно написать на 100% safe Rust на умных указателях.
Option<Rc<T>>
например. Проверку наNone
придется везде делать явно, а все, кто ссылается на список, будут получать слабую ссылку (Weak
). Для разыменования слабой ссылки ее надо временно проапгрейдить до сильной. Если разрушить такой список, пока кто-то работает с его элементами (классический пример с remove под for each), объекты останутся во временном владении у тех, кто на них ссылается.Но на практике так не делают. Для списков и вообще для хитрых алгоритмах на указателях, правильность которых доказана на бумаге, в Rust есть указатели в стиле Си (без проверки области видимости). Разыменование таких указателей возможно только с ключевым словом
unsafe
. Словоunsafe
не отключает проверки, а просто добавляет пару слабо контролируемых компилятором возможностей. Но основное назначение этой штуки — не оптимизация алгоритмов, а FFI и обращения к железу, которые компилятор проверить не может. Они тоже считаются unsafe. Поскольку словоunsafe
в коде встречается редко (под ним будет всего несколько строк), проверить алгоритм вручную становится намного легче. При необходимости словоunsafe
можно вообще запретить в модуле или во всем проекте, благо алгоритмы вроде связных списков и все важные системные вызовы уже реализованы в стандартной библиотеке. В большинстве реальных программ словоunsafe
не встречается нигде или встречается только в интерфейсе к чужим библиотекам на C/C++.Можно не пользоваться git, при этом cargo будет работать примерно как обычный cmake и все. В таком режиме можно использовать svn и что угодно еще. Если использовать git, появляются некоторые дополнительные возможности по работе с зависимостями.
lrrr11
раст — это язык, в котором куча рассчитанных на новичков статей повторяет одну и ту же пропаганду. Кто, блин, проверяет код вручную в проектах больше хелловорлда? Правильно, никто. Не говоря уже про то, что причина возникновения ub вполне может прятаться в "safe"-коде. Это "магическое" unsafe абсолютно бесполезно, и по этой причине отсутствует в других языках
Reformat
unsafe с сырыми указателями есть и в C# например
andy128k
Так в том и дело, что весь код проверять и не надо. Только
unsafe
блоки. В них обычно пишут больше всевозможных проверок и покрывают их тестами.Я уже года 4 пишу на расте и за всё время мне unsafe понадобился всего пару раз. Это были переписывания с C/C++ на раст и такой код был временным клеем. Как только последняя функция была переписана на расте — он удалялся. И в чистом коде на расте никакого unsafe уже не было.
Ну и важно понимать, что даже unsafe раст намного безопаснее C/C++. У компилятора есть проверки которые он выполняет всегда (safe и unsafe) и проверки которые доступны только в safe. Это всего лишь означает, что компилятор не может сделать это сам и требуется помощь человека (обычно это означает, что нужны тесты для этого куска кода).
moncruist
Даже в чистом расте unsafe бывает часто необходим, например, для доступа к глобальным переменным.
mayorovp
Глобальные переменные в прикладном программировании не нужны.
moncruist
В embedded, куда собственно раст тоже метит, очень часто бывают нужны, потому что там нет динамической памяти и стек очень ограничен.
agalakhov
Тоже не нужны. https://crates.io/crates/cmim
moncruist
И что это доказывает? Весь крейт — это обертка над UnsafeCell и unsafe кодом. Там даже тестов нет, так что никто не знает работает ли это вообще или нет. Плюс это лишь для работы с данными внутри прерываниях. Я же говорю про глобальные объекты в целом. Всякие буферы для сетевых операций, большие жирные объекты для логики приложения где хранить как не глобально?
AnthonyMikh
А зачем хранить их глобально?
red75prim
Эти unsafe'ы можно завернуть в безопасный интерфейс. См. например: https://docs.rust-embedded.org/book/concurrency/index.html#global-mutable-data
moncruist
То, что unsafe можно завернуть в safe обертки ещё не гарантирует memory safety для клиентского кода. Кто знает, может в этих safe обертках творится настоящий ад из утечек и повреждений памяти. Клиентский же код будет всё время находится с чувством ложной безопасности до того момента, когда всё упадёт.
Это больше всего напоминает запихивание мусора под ковёр во время уборки.
mayorovp
В смысле — "кто знает"? Откройте их исходный код да посмотрите!
Те самые мифические программисты, которые всегда на Си пишут без ошибок, уж точно должны быть способны один раз написать безопасные обёртки для стандартной библиотеки Rust.
moncruist
Часто ли вы испектируете все зависимости, которые используете? А зависимости зависимостей? У меня вот на мелких проектах с 2-3 зависимостями тянется около 100 крейтов с репозитория. И что, предлагаете все их просматривать и инспектировать на предмет корректности в unsafe коде? Проще застрелиться.
mayorovp
Лично я — часто. Но в случае с распространёнными библиотеками можно с некоторой вероятностью положиться на сообщество.
А вот часто ли сишники инспектируют свои зависимости?
wigneddoom
Лично я — всегда. В тех областях где применима "сишка", это просто перетекает в обязанность. Я просто не могу применить в Embedded код о котором я ничего не знаю. Например, сколько он жрёт памяти, есть ли там динамические аллокации и т. д.
На плюсах в моей области та же ситуация. Нужно ли мне тянуть Boost ради Event-loop или я напишу его сам для трёх дескрипторов на kqueue/epoll.
На Python кстати вообще почти не тяну левых библиотек, т.к. у него на мой взгляд одна из лучших стандартных библитек, которая покрывает мои запросы на 90%.
Я искренне желаю, чтобы инфраструктура Rust не повторила ошибок Node.js и NPM.
PsyHaSTe
Кстати в расте хорошо то что код который требует аллокаций явно об этом говорит видом
extern crate alloc;
agalakhov
Это гарантирует, что если API библиотеки выполняет контракт, то memory safety будет независимо от способа использования этого API.
Хотя это не обеспечивает абсолютную безопасность, это дает намного бОльшую безопасность, чем написание в стиле Си, где вообще весь код unsafe. Проверить вручную один раз пять простых строк в safe-обертке намного легче, чем дебажить каждый клиентский проект.
moncruist
И да, и нет. Да, если ваш случай использования совпадает с тем, о чем думал автор при написании. Но часто бывает так, что всякие скрытые контракты авторы библиотек не документируют (потому что не учли), а документации красуется 100% safe.
В хороших библиотеках на С в свою очередь документируется стратегия владения объектами (потому что иначе никто не сможет это использовать) и это в обязанностях программиста следовать им.
mayorovp
Что такое, в вашем понимании, "скрытый контракт"?
moncruist
Это контракт, который нигде не задокументирован, но неявно вытекает из реализации метода.
PsyHaSTe
В худшем случае он запаникует на каком-то инварианте или потечет памятью. Не напортачит молча не ту память и не совершит уб
AnthonyMikh
А в Rust эта задача возложена на компилятор. Не совпали ожидания — код просто не скомпилируется.
Morgan_iv
Не используйте библиотеки и обертки, которые недостаточно протестированы и в которых может твориться настоящий ад из утечек и повреждений памяти. Emdedded — это всего лишь малая часть от всего кода на Rust, и в обычных проектах чувство безопасности у клиентского кода вовсе не будет ложным. При этом, если писать на C++ такие же обычные проекты, избежать ошибок памяти без опыта использования современных фич C++ очень сложно, а избежать UB вообще считай что невозможно.
Antervis
без опыта на любом языке тяжело писать.
а вот тут можно вдаваться в очень глубокую демагогию. Дело в том, что стандарт с++ очень явно определяет какое поведение определено стандартом, какое реализацией, а какое не определено совсем. Соответственно, кусок кода, не содержащий UB, обязан быть корректным. Однако спецификация rust куда менее строгая, большая часть описанных в ней гарантий покрывает именно safe код, и формально практически любой код с unsafe является UB.
WASD1
Вот смотрите «safe rust» подмножество языка накладывает на код множество инвариантов, благодаря которым часть ошибок невозможна.
Надо заметить, что это «очень умное множество инвариантов» (ОЧЕНЬ УМНОЕ МНОЖЕСТВО ИНВАРИАНТОВ). Вынь любой из них — и вся Rust Safity посыпется.
Так вот проблема таких unsafe-блоков (как я её вижу) в том, что:
1.) unsafe-блок — это «вот в этом коде мы нарушили инварианты. Реальная ошибка может проявиться где угодно далее.
2) Пользователю предлагается „на коленке“ проверить и поверить, что его программа, хоть и нарушает Rust-safe инварианты, всё ещё сохраняет safity (просто из-за того, что его программа частный случай) — это весьма хлопотно.
2.б) Пользователю предлагается не просто однократно поверить, а как правило поверить на всё дальнейшее время существования программы, что unsafe-блок останется безопасным, как бы не менялась safe-часть его программы.
Вообще недоверие к пунктам „2“ и „2.б“ по-моему вполне оправдано.
DaylightIsBurning
1.б) автор unsafe блока должен проверить, что unsafe block будут сохранять safity в контексте любого safe кода, а не только в частном случае конкретной программы.
Особенно если этот unsafe блок находится в библиотеке. Если сильный вариант проверки выполнен, пользователь может не волноваться и 2) и 2б) не требуются.
andy128k
Ну можно впасть в крайность и разметить весь код как unsafe. И даже в этом случае получится язык более безопасный чем C/C++.
Процитирую мануал. В unsafe коде можно дополнительно делать:
Всё! Все остальные инварианты и проверки сохраняются, в том числе и пресловутый borrow checker.
PsyHaSTe
Борровчекер не поможет запретить трансмут &t -> &mut t, а если ансейф глобальный то ничто не мешает. Да, это УБ, но в итоге по безопасности +- те же плюсы
Akon32
Зачем тогда нужен Rust, если правильность алгоритмов доказана на бумаге, а не автоматически? Здесь подход Rust мог бы быть огромным плюсом, предоставляя автоматическую проверку корректности алгоритмов после изменений, но указатели в стиле С как раз такую проверку отключают.
agalakhov
Затем, что в большинстве случаев правильность доказывается автоматически, и только для ничтожного процента кода ее приходится доказывать вручную.
Полностью автоматическое доказательство правильности в любом случае принципиально невозможно из-за проблемы остановки. Rust решает проблему остановки тем, что проверяет автоматически большинство алгоритмов, а не все. Для оставшегося меньшинства программисту на выбор предлагается два варианта: перенести проверку на runtime или провести некоторые действия с неполной проверкой.
Я пишу на Rust уже 7 лет, у написанной мной библиотеки 2.2 млн. скачиваний, и за все это время мне пришлось поставить под unsafe в общей сложности меньше сотни строк. Причем в подавляющем большинстве — не алгоритмы, а просто вызовы сишных библиотек.
locutus
Проблемы остановки не существует для компьютеров с конечным числом состояний.
ilammy
Эй, почто человека минусуете? Если у компьютера конечное число состояний, то компьютер с достаточно бо?льшей памятью может построить полный граф переходов между состояниями и найти в нём все циклы.
mayorovp
В теории — да, а вот на практике понадобится слишком много памяти. Это во-первых.
А во-вторых, сама идея доказывать правильность работы программы исходя из той посылки, что на компьютере где программа будет работать будет не более X памяти отдаёт чем-то странным. Пройдёт время, появятся компьютеры с большим объёмом памяти — и что тогда? Правильный алгоритм должен иметь ограничение на используемую память снизу, а не сверху.
red75prim
У компьютера с 64кбайт памяти 2^524288 состояний, машина Тьюринга с 6-ю состояниями может проработать 10^36536 шагов перед тем как остановится.
Не в нашей вселенной такое перебирать.
locutus
Изначальный посыл был «Полностью автоматическое доказательство правильности в любом случае принципиально невозможно»
Для конечной машины всегда возможно.
red75prim
"Принципиально невозможно в любой вселенной, имеющей ограниченные вычислительные ресурсы". Так пойдёт?
locutus
В предложенном вами примере достаточно всего 10^36536 шагов для решения задачи в общем виде для указанного компьютера.
red75prim
Так в какой вселенной будем анализировать? В нашей? Не выйдет. В той, в которой выйдет? Там можно взять машину Тьюринга с 7-ю состояниями. И тоже не выйдет.
locutus
Вы можете уточнить исходное определение (например, уточняя понятие «вселенная» и дополняя понятие «ограниченности» временным ограничением), назовем это «расширенным утверждением», и будете скорее всего правы в рамках ваших расширенных допущений. Однако это не означает неправоты моего ответа на изначальное более общее утверждение другого участника Хабра.
JPEG
Верно, но немного лукаво. Только часть доступной памяти участвует в реализации алгоритма и конечность многих конструкций доказывается формально. Это позволяет значительно снизить вариативность, на чем мы люди и держимся :)
red75prim
Мы, люди, держимся на том, что не пытаемся решать проблему остановки (точнее, обойти теорему Райса). Мы не берём произвольный алгоритм, и потом пытаемся доказать, что он решает нужную задачу. Мы конструируем алгоритм под задачу. Правда баги и CVE показывают, что алгоритмы сконструированные таким способом обычно решают не совсем ту задачу.
Исходного посыла это не меняет. Невозможно (ни практически, ни теоретически) решить проблему остановки. Тут помогут только супер-Тьюринговые компьютеры, которых не видно даже на горизонте.
Ghostwriter
> Полностью автоматическое доказательство правильности в любом случае принципиально невозможно из-за проблемы остановки.
А в каком контексте проблема остановки связана с операциями на списках, которые вы упоминаете выше? Можно подробнее в деталях?
По существу, проблема остановки решаема для linear-bounded automaton. Ну и формальная верификация алгоритмов для списков (и вообще для арифметики на указателях) при помощи компилятора уже существует, просто Rust так не умеет:
* ats-lang.sourceforge.net/DOCUMENT/INT2PROGINATS/HTML/HTMLTOC/c3321.html
* ats-lang.github.io/DOCUMENT/INT2PROGINATS/HTML/x3828.html
agalakhov
Проблема остановки лишь приводит к необходимости иметь в языке "unsafe" для самого общего случая. Частный случай со списками возник по другой причине, а "unsafe" для него использовали потому, что он все равно уже был в языке.
Проблема верификации коллекций в Rust вытекает из искусственного ограничения, введенного из соображений эргономики: при верификации обращения к модулю запрещено заглядывать внутрь модуля, модуль следует рассматривать как черный ящик и смотреть только на написанные человеком сигнатуры функций. Это сделано для того, чтобы изменения внутренностей модуля не приводили к непреднамеренным изменениям его свойств с точки зрения клиентского кода.
Код указателей и прочих unsafe-действий тоже верифицируется, но менее строго. Например, в stable rust до последнего времени не было способа сказать, что приходящий из чужого модуля указатель никогда не бывает нулевым (теперь есть,
NonNull
называется). Пока нет в общем случае способа сказать, что указатель на объект из коллекции не затрагивает другие объекты (единственное, что можно сказать — что это указатель на объект, принадлежащий другому объекту). В подобных случаях мы пока что просто говорим компилятору "поверь нам на слово, что так делать можно". Либо добавляем проверку на рантайме в надежде на то, что оптимизатор обнаружит инвариант и выкинет эту проверку.Пример со списками: если написать модуль для списков, то есть способ сказать компилятору, что функция
push()
как-то изменяет содержимое списка, но нельзя сказать, что она не инвалидирует указатели на уже имеющиеся элементы. Хотя компилятор в принципе способен это вывести сам, он не имеет права, так как в сигнатуре функции мы этого не сказали, и, возможно, в будущем планируем поведение функцииpush()
изменить.Возможно, с точки зрения теории это не очень красивый способ, но это лучше, чем получить теоретически правильный язык, которым будет слишком трудно пользоваться для массового применения.
Ghostwriter
> Частный случай со списками возник по другой причине, а «unsafe» для него использовали потому, что он все равно уже был в языке.
ок, значит имплементации разных списков и unsafe в них — не про HP, и теоретически нет непреодолимых препятствий проверять их безопасность компилятором — есть проблема эргономики, о которой вы говорите ниже.
Но тогда у меня есть философский вопрос: если Rust позиционирует себя относительно С/С++ как язык, который позволяет предотвращать проблемы безопасности по памяти и продвигает себя в аудитории, для которой эти проблемы должны быть важны и насущны, то почему в этом позиционировании присутствует вопрос эргономики, который изначально субъективен и не направлен на обобщенное решение проблем с памятью?
Некоторые вещи в мире действительно сложны и требуют изучения, неэргономичность математики, например, не является аргументом в пользу введения в нее необобщенных построений.
То есть, если я тот инженер, перед которым стоит задача решить проблемы безопасности софта по памяти, мне не очень понятен аспект эргономики, ради которой приходится жертвовать безопасностью в некоторых случаях, особенно если эти случаи распространены в низкоуровневом коде ядер ОС и драйверов. Для меня как для инженера более логично выбрать инструмент, который решает эту проблему обобщенно, без оглядки на эргономику. А уже когда эта основная задача покрыта, можно подумать об эргономике.
freecoder_xx
Потому что выйдя за определенные рамки эргономики мы покидаем аудиторию, для которой изначально и предназначался язык и безопасность программ которой он и был призван улучшать. У Раста нет цели быть принципиально 100% безопасным в ущерб остальному, у него цель быть как можно безопаснее в заданных ограничениях, коих еще вагон.
PsyHaSTe
Мне кажется тут разумнее упомянуть теорему Райса, а не проблему остановки.
Хотя вывод примерно тот же
locutus
К тому же вполне возможна формальная верификация программ, написанных на C, например микроядро seL4 тыц
Ну и фреймворки типа Frama-C помогают писать спецификации прямо в коде.
JPEG
Немного оффтоп. Вы 7 лет пишете коммерческий софт? Если да, то расскажите немного подробнее, пожалуйста.
andy128k
Потому что Rust это практичный компромисс между полным отсутствием проверок (С/С++) и слишком сложными и медленными системами доказательств.
И, как показывает опыт, компромисс вполне удачный. Этого самого unsafe кода исчезающе мало по сравнению с остальным кодом.
Antervis
на этот счет есть какая-то статистика? А то утверждение кажется чересчур амбициозным
wigneddoom
У меня нет статистики. Но мне кажется позиционирование Rust как убийцы C/C++ не совсем верно. Он скорее приходит в область где правили Go и Java. Там где рулили языки с GC. А теперь появилась альтернатива с высокой производительностью и богатыми возможностями.
А вот в каком-то Embedded usafe кода просто не избежать.
AnthonyMikh
Есть. Оценка там довольно грубая, но по ней выходит, что около 70% крейтов не содержат unsafe-код вообще и что в целом 95% кода, размещенного на crates.io, является safe.
domix32
Есть отличная статья по теме списков
rezchikov
Двусвязный список уже в стандартной библиотеке Rust: std::collections::LinkedList.
Альтернативная реализация c unsafe-кодом в репозитарии RustAlgorithms: ссылка на linked_list.rs
И реализация без unsafe-кода: leviathan88/safe_lists_rust
AndreiVorobev
Как мне кажется, в расте, для реализации различных структур, ноды которых имеют ссылки на другие элементы. Наиболее идиоматичным, будет вариант с использованием списка с индексным доступом вроде Slab или Vec и хранение его ключей как ссылок на связанные элементы.
beho1der
А как обстоят дела с GUI?
andy128k
https://www.areweguiyet.com/ — вот тут собирают информацию о состоянии GUI в Rust.
Из личных наблюдений — есть очень приличный gtk-rs (привязка к gtk3), куча обвязок вокруг WebView, и написанные с нуля библиотеки со скупыми наборами виджетов.
beho1der
Да это видел, просто интересен опыт именно тех кто пробывал сам
snuk182
Я пробовал байндинги к gtk3, qt, cocoa, winapi. Самым беспроблемным с точки зрения соответствия оригинальной документации являлся, внезапно, winapi — можно код на си напрямую переписывать в Rust (естесственно, завернув в unsafe). Следом идет cocoa, который мог бы быть первым, но для замыканий и другого асинхронного кода там намутили, разумеется, забыв задокументировать. Qt слегка наркоманский в плане API, но если понять принцип, становится несложно, но многословно. Gtk прекрасно используется, пока вы используете стандартные контролы, но стоит отойти в кастомизацию или расширение стандартного функционала, начинаются приключения — документации нет, иногда вырезана поддержка в коде (кастомные сигналы).
proninyaroslav
Ещё Flutter как вариант. Хотя думаю смысл раста в нём только если нужно писать куски кода для доступа к нативному API системы, вместо C/C++.
ExceptionallyHandsome
Думаю вот эта ссылка будет куда полезней.
vladkorotnev
Пока пользуюсь Druid, на мой вкус выглядит бомбезно, но уж больно непривычно, если до этого не писать на каком-то другом фреймворке с парадигмой триады и не прописывать вручную весь лайфцикл приложения.
Всякие примочки типа вывода динамических слоёв для однооконного приложения (показ Toast'ов, модальных алертов внутри окна) пришлось писать самому.
Для immediate mode есть биндинги к imgui, но т.к. стояла задача кроссплатформы без внешних либ вообще, то не стал их использовать.
shamilsan
Про состояние GUI мне понравилась вот эта статья: dev.to/davidedelpapa/rust-gui-introduction-a-k-a-the-state-of-rust-gui-libraries-as-of-january-2021-40gl
Там автор потрудился попробовать несколько гуишных крейтов и поделился своими впечатлениями.
EvilMonk
Насколько будет быстрым вызов из внешних dll, написанных на C/C++? Быстрее, чем [DllImport] в C#?
tzlom
Быстрее на анбоксинг, соответственно если в шарпе анбоксинг уже решён то эквивалентен. Вопрос только что делать с исключениями, но это и для шарпа проблема.
Cheater
Вопрос не совсем корректен, тк не относится конкретно к расту. Rust генерирует бинарники в формате целевой платформы, например ELF на линуксе, и этот бинарник обычным образом вызывает код из динамической библиотеки, через Posix С ABI. То же произойдёт если бинарник написан на си или на плюсах. ЕМНИП первый вызов будет более медленный (оверхед на поиск функции в таблице PLT), остальные быстрее.
Так что вопрос здесь "скорость вызова DLL в C# и в компилируемых языках". Не знаю правильный ответ на этот вопрос, интуитивно кажется что компилируемые языки делают вызов из DLL быстрее. Но вот обратный пример с luajit. Там есть ссылка на проект на гитхабе с бенчмарками различных языков при вызове .so кода, шарп там медленнее компилируемых языков. Короче всё зависит от механизма вызова.
agalakhov
Rust не добавляет дополнительного оверхеда к таким вызовам. Скорость будет неотличима от C/C++ на компиляторе с тем же бэкэндом и аналогичном линковщике.
svr_91
Интересна история вопроса, почему отказались от green threads в пользу системных потоков?
tzlom
Потому что системные потоки фундаментальные. На расте можно сделать гринтреды средствами языка, в обратную сторону это не сработало бы.
Kels Автор
Вы можете посмотреть RFC где подробно описаны причины.
Если вкратце, разработчики Rust решили что это язык для максимально эффективного системного программирования и должен иметь минимальный runtime.
Так же думаю, что зеленые треды мешают эффективному FFI с C. Например в Go любой вызов C функции это относительно дорогая операция.
agalakhov
Правильнее будет спросить, что сейчас сделалось с green threads. В Rust появилась библиотечная реализация фьючерсов, причем не требующая переключать стэки, с нулевым оверхедом, а потом на ее основе сделали async/await. Все это делает зеленые потоки в большинстве задач ненужными. Также появилась библиотечная реализация классических зеленых потоков. Все это позволило обойтись без встроенной в язык поддержки.
Поддержку системных потоков язык обязан иметь потому, что компилятор должен уметь проверять корректность передачи данных между потоками. Хотя сама реализация системных потоков тоже в библиотеке, язык знает, что такое "исполнение в другом потоке" и "thread-local".
neurocore
Кложура. Этим словом можно детишек пугать: вот придёт кложура, как вручит словарь русского языка!
Kels Автор
Поправил :)
orthoxerox
Не ешьте функцию вместе с кложурой!
Cheater
Как-то приелись мотивашки по расту и статьи со введением в Rust, их уже наштамповали мильён, а вот материалов более высокого уровня наоборот не хватает, из стандартных только rust reference и nomicon.
Lex98
Мне понравились вот эти лекции: compscicenter.ru/courses/rustprogramming/2019-spring/classes/4445, все очевидное, вроде «в расте есть циклы и условный оператор» заканчивается примерно на первой лекции. Есть еще книга www.manning.com/books/rust-in-action, сам пока не читал, но планирую, оглавление выглядит многообещающе.
McAaron
В C и в C++ нет никаких проблем с безопасностью памяти, поскольку malloc() и free() — это всего лишь обертки системных вызовов в ядро. Каких-то более «безопасных» системных вызовов управления кучей нет. При этом никто не мешает на их основе создавать субаллокаторы с проверками и прочим, тем более, что С++ предоставвляет такую возможность для всех своих контейнерных классов.
Проблема «безопасности памяти» не в языках, а в программистах. Из всех перечисленных выше «проблем» за всю свою долгую жизнь в C и С++ и даже в ассемблере не помню ни одной, с которой пришлось бы столкнуться. Налететь на них можно только если сильно спешить и писать код напрямую с головы, не предваряя кодирование проработкой алгоритмов, протоколов обмена объектами и прочим, что должно прорабатываться до тогшо, как начнет создаваться код, котороый увидит свет.
vladkorotnev
Но как и в любой другой работе, зачем её делать вручную, если можно делать не вручную?
Всякие сишарпы, джавы и джавоскрипты популярны стали отчасти потому что там с этим всем заморачиваться не надо, а оверхед от технологии, позволяющей этим не заморачиваться, для большинства задач прикладного программирования минимален. С растом же это приходит и в системное программирование.
wigneddoom
Вот и я так думаю. Зачем вручную воевать с borrow checker, лайфтаймами, элементарные вещи делать через стандартную библиотеку.
У меня конечно субъективный взгляд, т.к. пока не занимался детальным изучением Rust. Пока просматривая код интересных мне проектов на нём, нахожу синтаксис Rust крайне не интуитивным. Надеюсь это пройдёт.
red75prim
Это рассуждение из разряда "зачем заморачиваться с техникой безопасности и так всё нормально будет, мешает только". Правда программисты стреляют в ногу только метафорически, и не в свою ногу, а в пользовательскую.
Ghostwriter
А что если мне нужна безопасная семантика владения, которая не поддерживается borrow checker'ом? В других языках я могу определить свою семантику владения, и даже формально доказать ее безопасность, а в Rust мне придется воевать с компилятором и расставлять unsafe по коду.
mayorovp
Что такое "своя семантика владения"? По вашей ссылке что-то странное написано.
Ghostwriter
Своя семантика владения это например когда я могу иметь безопасный ring buffer (у него же есть семантика владения объектами внутри структуры) без unsafe в имплементации. У Rust нет механизма описания кастомной внутренней семантики владения, которая бы точно описывала почему та или иная структура и операции над ней безопасны по своей природе, и поэтому всё что не укладывается в правила выбранные авторами компилятора считается небезопасным.
> По вашей ссылке что-то странное написано.
а вот так лучше?
red75prim
Значит для ваших задач удобнее другая техника безопасности. Пользуйтесь ей, никто не запрещает.
Ghostwriter
Те кто минусует, я вас приглашаю к предметному диалогу. Не нужно быть фанатичным последователем культа Cargo, если вы сами в статьях и комментариях инициируете обсуждение вопросов безопасности по памяти в софте.
inferrna
«Своя семантика владения», «20 лет пишу, ни разу в ногу не стрелял» — это всё про одно и то же: про честное слово конкретного программиста и то, насколько мы ему доверяем — и это для всего кода. Раст же гарантирует отсутствие UB «из коробки» во всём safe коде, а unsafe блоки, где программист обещает, что «всё чисто», проще проанализировать, чем весь код, как в первом случае.
vladkorotnev
Да тут с обоих сторон, похоже, фанатиков хватает, мне тоже кто-то плюнуть в карму успел, при том что ни единого ответа по существу ни от кого, кроме вас, не было ?\_(?)_/?
DaylightIsBurning
Конечно, есть языки с более продвинутой системой типов чем у Раста, но у них свои проблемы. Идея Раста в том что бы найти удачный компромисс между универсальностью асма и C, безопасностью и эргономикой, при этом не отходить от принципа «What you don’t use, you don’t pay for». В эргономику я также включаю расходы на поддержку и тестирование кода.
Возможен ли язык, который лучше раста по всем направлениям? Конечно, возможен, но я о нем пока не слышал. Более того, по моему мнению, для многих задач Rust является лучшим языком в совокупности на данный момент, лучшим выбором в целом, так сказать. И число таких задач продолжает увеличиваться. Haskell и ко тоже круты, но Rust выходит сильно практичнее.
vladkorotnev
Ну так и пишите на ассемблере сразу, зачем заморачиваться с чем-то вообще, если можно всё сделать интуитивно и просто?
На практике воевать приходилось от силы первые часа два, пока не пришло осознание принципов не на уровне "прочитал доку", а на интуитивно-прикладном. После этого, в свою очередь, перестал творить всякое и терять аллокации/деаллокации/прочие приколы на обычном си — но начала пухнуть башка с него от того, что всё, что в расте делается неявно и автоматом, тут нужно держать в голове и прописывать.
С синтаксисом да, беды есть, т.к. авторы и сами заявляли, что затачивали его под более простой парсинг, чем под лёгкую читабельность. Но после пары проектов вызывает дискомфорта куда меньше, чем те же плюсы (при условии, что проекты не уровня хелловролд, а именно полноценных с дженериками и прочими темплейтами) — опять же, сугубо субъективно.
wigneddoom
Нет, ассамблер не интуитивен. Ну и мне кажется интуитивность это очень важное свойство языка программирования. И да, я понимаю, что не всё и не всегда можно сделать простым и интуитивным. Но я считаю, что замена С/С++ должна быть сходу доступна программистам на оных, без изучения теории типов и других концепций.
Я никогда в Embedded и в разработке модулей ядра ОС не теряю аллокаций. Потому что в первом случае их вообще нет, либо как у Rust unsafe — 0,0001%. Во втором их тоже две-три штуки. А дальше, дальше не нужно на Си писать микросервисы.
Я не против Rust, просто пока больно, надеюсь пойму его концепции.
snuk182
У каждой сущности есть владелец — вся концепция Rust.
Хочешь ссылку на сущность — имей в виду ее владельца. Чтение, запись — согласно принципам общего доступа, знакомым каждому по работе с многопоточностью, только здесь доступ проверяется статически во время компиляции. Да, нужно решать все проблемы сразу, иначе код просто не соберется, но во первых, с опытом правильный код пишется с первого раза, а во вторых, это снимает кучу головняка с техподдержкой.
vladkorotnev
Опять же, вкусовщина, по мне Z80 асм проще в изучении, чем средний бухгалтерский калькулятор. А вот в х86 таки да, недоперемудрили, если ещё и лезть в MMX/SSE/2/3/4 :-) Даже на Altivec как-то попроще казалось. Но да ладно, это уже всё флуд.
Ну вот, это просто ваш частный случай, где оно и не нужно.
А почему бы и не писать? Но вот на чистом Си оно больно и трешово, а на Расте элементарно, при том что производительность примерно та же.
У меня лично Rust занял весьма широкую нишу, если можно так выразиться, в геймдеве, а именно написании альтернативных серверов к играм. 120 активных юзеров, поделка на ноде жрала под пару гиг оперативки, на расте хватает пары сотен метров. Либо эмулятор лицензионного ключа, чтобы не пилить оригинал лишним использованием — тут уже всё чисто даже на стандартной библиотеке делается, т.к. удобные сокеты есть, строки есть. Всякие лоадеры и патчеры тоже — можно, конечно, писать на том же сишарпе, но это будут вагоны врапперов поверх WinAPI и прочего треша, можно писать на Си — но банально неудобно.
Плюс элементарнейшая кроссплатформенность. На си приходилось думать, как и куда что обернуть для этого, на расте просто делаешь
cargo build
, разве что докинув тулчейн для кросс-компиляции. В итоге пишешь сервер на винде на машине с игрой, а деплоишь на линуксовую в один тык. Или пишешь на современной винде, а потом деплоишь на эмбеддед винду XP, правда уже с нюансами — там стандартная библиотека неполная, нельзя мьютексы и ещё что-то использовать. Но сам факт, что по простоте сравнимо с той же джавой, для меня был решающим.Изучением системы типов тоже не занимался, просто начал писать код сразу, и как-то оно само дальше в голову залетало. Оно уже потом заинтересовало, когда стало ясно, насколько это удобно, когда код либо работает, либо даже не собирается :-)
Gorthauer87
Немного ещё позанудствую, malloc и free это библиотечные функции, которые перераспределяют память которую ядро одним куском выдало.
mayorovp
Да-да, а эти самые CVE враги выдумали?
Вот только такие проверки придётся делать в рантайме, т.е. на них будет тратиться время.
Ну а недоступность идеальных, никогда не допускающих ошибок программистов — уже проблема языка.
dimkrayan
Интересно, для чего huawei планирует использовать rust. Просто, абсолютное большинство вакансий — это какой-нибудь блокчейн, что не для всех интересно.
DirectoriX
Использовать Rust для блокчейна тоже можно.
dimkrayan
разве я говорил, что нельзя? Просто, это не всем интересно и регулярно встречаю обсуждения «а что есть, кроме блокчейна».
Huawei делает много интересного, возможно, появятся вакансии, совмещающие интересный язык с интересной сферой деятельности.
DirectoriX
dimkrayan
я не понимаю ваш комментарий.
Я никого не заставляю подстраиваться. Просто спрашиваю.
Если Huawei планирует тоже использовать rust только для блокчейна — ок, все понятно.
Но спросить то можно?
DirectoriX
Хорошо, вернёмся к началу:
Для чего угодно.Ну и ладно, не интересно так не интересно.
Почему вы вдруг решили, что Rust у них должен пойти только в новые проекты и от новых людей? У них и своя мобильная ОС есть, и оборудование они делают, и мало ли что ещё.
dimkrayan
Вы это говорите от имени huawei, или это ваше личное мнение.
Если ваше мнение — то и так понятно, что на расте можно писать (почти?) все и что он может применяться и на старых и на новых проектах.
Просто, если такой крупный вендор будет использовать раст для разработки мобильной ОС, для оборудования — что угодно за пределами блокчейна — это большая новость для раст сообщества.
Понятно, что даже если блокчейн — все равно большая новость, да и блокчейн многим нравится, но потребность в не-блокчейн проектах на расте довольно большая.
Поэтому, хотелось бы получить ответ от автора.
Kels Автор
Так как это все внутренние планы и проекты, более детальный ответ предоставить не могу.
Вероятно это не тот ответ, который вы ожидали от меня, но в этом вопросе я не могу вам помочь.
dimkrayan
Все равно спасибо.
PsyHaSTe
Есть HFT :) Бувально на следующей неделе планирую пособеситься в очередную контору
kovserg
Где описание как скачать и установить и какие системные требования?
DirectoriX
Вообще-то рекомендуется использовать rustup для установки…
mayorovp
А зачем это описание нужно? Неужели найти эту информацию самостоятельно так сложно?
kovserg
И какие системные требования у rust-а?
DirectoriX
Простите, но ваш вопрос такой же бессмысленный, как «Какие системные требования у Python». Не бывает у языка системных требований, они бывают у инструментов сборки, отладчиков, рантайма, но не языка. Потому что сам по себе язык — не более чем способ записи алгоритма по определённым правилам.
Да, я зануда.
kovserg
Раст в том виде что он есть, имеет системные требования или уже много альтернатив есть?
Тот же python: «Python 3.8.8 cannot be used on Windows XP or earlier» чем не требование.
Сколько надо выделить виртуальной машине ресурсов что бы оно завелось и компилировало.
Тот-же си очень скромные требования имеет и есть в огромном количестве исполнений.
DirectoriX
Зато есть MicroPython, который аж в микроконтроллерах работает.
AN3333
И это всё?
Знаете, с такой рекламой я лично языком не заинтересуюсь. Другого жду от языка.
Alex_ME
Лично я заинтересовался Rust'ом после того, как он начал на хабре регулярно всплывать в комментариях к ФП-шным постам. Про раст услышал году так в 2015 как "про тот сложный язык язык с владением, про который на хабре пишут статьи о том, как строку в функцию передать". А заинтересовался после всех этих трейтов, недо-монад Option и прочих типов-сумм.
DarkEld3r
Например?
Но вообще да, все такие статьи страдают тем, что не очень убедительно повторяют одни и те же тезисы. С другой стороны, даже такие "мелочи" как тестирование или документация, когда их распробуешь, начинают цениться намного больше. Есть достаточно инструментов для документирования кода не привязанных к языку, но когда есть один (и главное удобный!) способ причём документация автоматически проверяется при тестировании (опять же — без дополнительных приседаний!), то это качественно другой уровень. Мне кажется, что именно поэтому даже не у самых популярных растовых библиотек есть неплохая документация. С фрейворками для тестирования похожая история.
Лично для меня "килер-фичи" раста это" бороу чекер, явный ансейф и как раз "инфраструктура". Ну и то, что это "современный язык" во всеми прилагающимися штуками вроде паттерн матчинга.
AlexTheLost
В подавляющем большинстве случаев сегодня нужно максимизировать трудозатраты, Rust достаточно сложный язык и уже по существующим репозиториями вижу что код который на нем пишут будет вызывать не меньше трудностей с пониманием чем с++ и т.п.
Все описанные в статье фичи это хорошо. Но не самое важное в большинстве случаев как я писал выше. Поэтому у меня сомнения что есть перспективы кроме системного программирования.
nikbond
Раст минимизирует трудозатраты на отладку, строго запрещая вам делать то, что в других языках делать просто нежелательно. За счет этой строгости вы убираете огромный класс багов в принципе.
Согласен, что трудозатраты на обучение больше чем на другие известные мне языки, но не трудозатраты на написание кода! Обучение действительно довольно сложное, но если понять основные принципы (а они не сложные и их довольно немного!) и научиться их применять в коде (тут уже будут проблемы), то именно процесс решения конкретной задачи идет намного более гладко по сравнению с.
Я, например, чувствую себя с Rust намного более эффективным чем с С++, и возвращаться не желаю.
PsyHaSTe
Но если сравнивать с ГЦ языками то раст все ещё заставляет думать там где не хотелось бы. Все эти пины и кросс-реф ссылки между объектами, ...
dmitryb-dev
Вот чем лично мне зашел раст, так это:
1. Комьюнити. Оно тут классное. Всегда помогут, подскажут, библиотеки высокого качества. Простота. Тот же cargo, serde, actix-web — все это учится за полдня и запас по возможностям отличный.
2. Типизация, дженерики, подобие ООП. Дизайн языка классный, никакого наследования, но есть подобие интерфейсов, можно реализовывать трейты для уже существующих структур.
3. Функциональное программирование, асинхронность. Причем стандартная библиотека сделана как будто для людей. Каждый раз думаю по привычке после Java, Scala, JS, что вот придется щас утилитный метод писать, ан нет, он уже готовый есть в расте. А мутабельные ссылки? Это же просто красотища. Любую структуру можно сделать имутабельной.
4. А еще раст следит за многопточностью. И если я вдруг где то что-то забыл синхронизировать, компилятор подскажет.
5. Обработка ошибок. Никаких больше этих непойманых рантам эксепшенов. Никаких споров, мол что лучше вернуть результат или исключение бросить. Все очень явно.
6. И при этом всем он чертовски быстрый! В нем нет ограничений в виде виртуальной машины или рантайма, так что производительность ограничена только вашими навыками и временем.
И вот тут я хотел написать «назовите хоть одну причину, почему это не лучший язык в мире?». Но вот только не пользуюсь я им ровно по одной причине. Ты вроде такой на нем пишешь, и даже не замечаешь что это какой то низкоуровневый язык. А потом выскакивает ошибка типа «already borrowed» и все. Там можно неделю убить. Если какая то оптимизация, и хочется вот тут clone убрать, привет миллион lifetime параметров. И при этом в продакшн не потянешь. Потому что язык таки не простой, где программистов то искать на него? Но это все решается. Ошибки — путем набора опыта, программисты — путем роста популярности.
Вот такой вот парадокс. То что везде пишут как супер крутую фишку раста стало причиной почему я не могу его использовать.
Но это я его использовал как высокоуровневый язык, вместо той же Java. Если бы передо мной стоял выбор С++ или Rust, то тут все очевидно (хотя честно говоря, если бы передо мной стоял выбор с++ или <вообще любой другой язык, что угодно, только не с++, пожалуйста>, то я бы тоже вряд ли с++ выбрал бы)
mayorovp
Уточнение: убить кучу времени можно не на поиск самой ошибки, а на поиск способа её обхода. И да, неделя — всё равно много как-то.
Pancir
Не на поиск способа обхода, а причину почему она случилась, то самое место которое надо изменить чтобы ошибка пропала ) За год (до этого 10 лет С++) программирования на расте я буквально вчера первый раз поймал то, о чем говорит автор.
вставляются потребители, источники потом сами дергают методы у IConsumer которые в свою очередь тоже имеют (или их vector) что делает дерево.Мне надо смоделировать участок цепи и распространение энергии от источника к потребителям, все сделано так, что есть источники в которые как
Среди потребителей есть контакторы (свичи) которые могут выбирать активный источник, и вот этом дереве я получил «already borrowed» я не смог у себя в голове развернуть все это дело и понять где ошибка, кроме как спустя некоторое время догадался, что какие-то контакторы в дефолте стоят в не верных положениях. Итого: замечательно то, что благодаря этой ошибки можно понять, что цепь имеет не верные соединения, но очень тяжело понять где именно, я себя реально первый раз в расте ощутит беспомощным.
ПС пока писал этот пост я кажется понял как конкретно в моем частном случае искать где проблема, не буду грузить лишним текстом, лишь скажу, что в общем ситуация не сильно улучшилась, но сузилось количество мест для проверки и поиска.
Но даже не смотря на все это, я на с++ больше писать не хочу, и не буду, только в legacy.
А неделя я думаю просто утрирование.
mayorovp
Это самое место не надо искать, его сообщает компилятор.
А, ну да, RefCell. RefCell — это такая штука, которая переносит проверки в рантайм. По возможности лучше её не использовать.
Pancir
Именно, что RefCell, к сожалению не везде я пока могу придумать как его не использовать, особенно после большого опыта в C++, в данном случае если я проектирую программу по другому у меня случается ад в другом месте.
Там вон где-то выше по сообщениям напомнили про идею индексов и хранению объектов в другом месте и получать доступ по индексам, буду на днях думать можно ли перепроектировать все на такой подход.
В целом после перехода с С++ на раст, очень большая проблема в том чтобы наработать паттерны для решения задач именно на расте, если паттерны которые были наработаны на с++ плюс минус можно использовать в других языках, то в раст надо искать свой подход с ноля :( и зачастую искать самостоятельно путем проб и ошибок.
mayorovp
Индексы — глупость. Лучше уж сделайте у своих потребителей внутреннюю изменяемость, то есть объявите их поля как Cell. Тогда внешний RefCell можно будет убрать.
Pancir
Я правильно понимаю, что в случае массива мне придется писать что-то типа:
А еще то, что теперь при неверном соединении цепи, не будет никаких ошибок, но при этом цепь может спокойно оказать в не валидном (с точки зрения предметной области) состояние молча. Хмм… что-то мне подсказывает, что наверное лучше тогда оставить как есть сейчас, т.к. предложенный вами подход замаскирует проблему.
Если вдруг кто не понял почему, то при повторном попадание в эту функцию когда она еще не завершена при первом вызове, приведет к тому что Cell::take() вернет пустой массив (т.к оригинальный массив забрал первый вызов), и ничего не произойдёт, тогда как с RefCell второе попадание отловится рантаймом и выпадет паника (ну или нет если вы будете обрабатывать это самостоятельно с try_borrow()).
В любом случае спасибо за напоминание, что cуществует еще и Cell.
PsyHaSTe
Не знаю у кого индексы — глупость, арены — типовое решение проблемы раста с расшаренным мутабельным доступом.
DmitryOlkhovoi
Я частенько пытаюсь попробовать новый язык, и даже прочитал пол доки по Rust. Правда в итоге я так и не понял где же можно его использовать с преимуществом над c, python или тем же JS.
EragonRussia
Преимущество над C — безопасность памяти и потоков, что и является основной фичей языка; над Python и JS — многократно большая производительность. Хотя не совсем корректно сравнивать Rust с Python и JS — разные уровни абстракции.
AnthonyMikh
Согласен, Python и Javascript по выразительности далеко до Rust.
locutus
К минусам Rust следует отнести достаточно жесткую привязку к одной системе сборки, к одному тулчейну и к одному способу задания зависимостей. GCC front-end пока не достиг высокого уровня, при этом есть проблемы со стабильностью спецификации, что важно для авионики и пр. mission-critical.
Та же, например, Ada без освобождения памяти (а так обычно обстоят дела в авионике) гарантирует memory safety и thread safety (с использованием
protected
), и при этом обладает стабильной спецификацией. MISRA-C позволяет уменьшить число неявных ошибок программистов C и т.д. С точки зрения разработчиков и заказчиков таких систем, плюсы от перехода на rust не столь очевидны.Pancir
Я конечно понимаю каждой задаче свой инструмент, но очень часто в жизни, что для одних минус для других плюс. Для меня это наоборот плюс, я настолько устал от С++сного зоопарка, вы себе представить не можете, все эти, cmake, conan, scons, make… и каждый из них полноценная система на другом языке. Проблемы когда vs собирает твой проект нормально, а потом clang на Linux не собирает и т.д…
Мой личный опыт показывает что большая вариативность — это скорее минус чем плюс, под вариативностью я понимаю «17 способов инициализировать переменную в C++» В с++ такое везде, начина от компиляторов и систем сборок заканчивая самим языком, везде есть из чего выбрать, а на деле весь этот зоопарк вносить только хаус и проблемы. То что планировалось как гибкость на любой случай жизни превращает многое в ад. Меня до сих пор бросает в дрож когда я думаю о том, что мне надо пойти и создать проект для новой библиотеки на с++, а если мне еще и зависимости внешние нужны, нет, для меня лучше пусть будет одна система с чем то вроде «cargo new my_lib --lib».
Опять же я понимаю, что в других сферах этого может быть не достаточно, но прежде чем добавлять вариацию по моему мнению надо очень хорошо подумать.
d8veloper
Язык огонь и имеет замечательные перспективы. Конечно, в ближайшем будущем си/плюсы он не убьет, но познакомиться с ним однозначно стоит. Жаль, руки не доходят(
Baalskra
Хотелось бы, что бы язык активнее продвигался в направление встраиваемой электроники. И в целом порог вхождения в это направление уменьшался.
ChapayHabr
есть ли возможность писать кроссплатформенные либы под android/ios/windows/macos/linux?
сейчас такого рода вещи на с/c++ обычно делают
DirectoriX
Сам по себе Rust кроссплатформенный, Win/Linux/MacOS точно поддерживаются.
Ничего слишком серьёзного писать на нём не приходилось, но все мои «экспериментальные» программы без проблем собирались и работали и на Windows, и на Linux.
Единственное, с чем столкнулся: некоторые/многие крейты, связанные с GUI, могут не поддерживать всю тройку ОСей.
ChapayHabr
гуй не интересует, а про десктопы читал. нужны еще обилки
Есть вариант сдк написать под все платформы, локальная базы + сеть для пересылки
вот и думаю может кор залячкать на rust… )
но нужно чтобы это было официально поддерживаемо, а не колдовство )
потому как могут и сжечь ))
Gorthauer87
Сеть тут точно везде работает.
ChapayHabr
В этот раз поиск мне выдал что-то релевантное )
zen.yandex.ru/media/nuancesprog/rust-i-razrabotka-krossplatformennyh-reshenii-dlia-mobilnyh-ustroistv-5f4b737fbf0b1c1f8aac4617
пугает немного, что написано в разделе experiments )
mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.html
mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html
kay
А как у Rust с кроссплатформенностью? Можно и собрать бинарники для Windows/FreeBSD/Linux в, скажем, OpenBSD? Или придется сотни toolchain'ов искать и ставить непонятно откуда?
DirectoriX
Rust поддерживает кросскомпиляцию официально, а если воспользоватmся, например, cross — это даже довольно удобно (при условии, что у вас есть Docker/Podman).
kay
удобно, но не увидел тот же macos, который сейчас много где требуется для пользовательских приложений. может плохо смотрел?
humbug
https://doc.rust-lang.org/nightly/rustc/platform-support.html
tm1218
Cписок тех, кто использует Rust в продакшн
rubyrabbit
Расскажите пожалуйста об областях применения на практике
Например, что насчёт создания HTTP API.
Или создания микросервиса, который бы слушал из и писал в Kafka.
Спасибо
freecoder_xx
Про HTTP — смотрите сюда: https://actix.rs
Для Kafka есть обертка над librdkafka: https://github.com/fede1024/rust-rdkafka
freecoder_xx
Области применения весьма широкие. Например, вот что делал я на Rust:
Еще хочу попробовать мобильную разработку на Rust, но пока с этим есть некоторые трудности.
rubyrabbit
Спасибо большое
Есть пример веб-проекта, у которого бэкенд полностью был бы сделан на Rust?
Open-source bootstrap?
Может ли Rust быть заменой языкам высокого уровня?
Есть ли там стандартные обвязки типа ORM, логирование, сбор метрик в прометеус, работа с Redis, авторизация и т.п.
Несколько полна стандартная библиотека?
Прочесть файл, читать данные из TCP-запроса, держать HTTP-соединение и т.п.
freecoder_xx
Ну чтобы далеко не ходить — вот вам пример веб-приложения, бэкенд которого на Rust: https://utex.io/exchange-pro
Для подобных задач Rust показывает себя хорошей заменой C++ и Java/Kotlin. Вообще, я бы предпочел Rust любому высокоуровнему языку из мейнстримных сегодня. Но из-за незрелости в некоторых нишах его использование все еще затруднено (как, например, в мобильной разработке). Хотя именно с точки зрения языка нет принципиальных проблем для его распространения в эти ниши.
ORM в Rust отличается от того, как он реализуется в мейнстримных ООП-языках. Более простой и с растовой спецификой (многие вещи из рантайма выносятся на стадию компиляции). Самая близкая и зрелая реализация — это Diesel. Он может почти все, только плоховато документирован. Хотя для большого количества случаев достаточно sqlx.
Логирование — есть куча неплохих библиотек, полный список здесь. Сбор метрик в прометеус, работа с Redis и RocksDB и т.д. — все это есть, чаще в виде безопасных оберток над Сишными либами.
Стандартная библиотека очень хорошая, но она довольно минималистичная. Работа с сетью, с файловой системой — это, конечно, есть, но, например, полноценного веб-сервера вы в ней не найдете. Для этого есть отдельные крейты, такие как hyper, actix-web, tokio и так далее. Даже фасад для логирования, функционал сериализации/десериализации и работа с регулярными выражениями вынесены в отдельные крейты, которые становятся стандартами де-факто для использования: log, serde, regex и прочие. Вот список всех крейтов по количеству загрузок за все время.
rubyrabbit
Походил по ссылкам, почитал, добавил некоторые вещи в закладки, спасибо :)
Можете ли рассказать подробнее про использование Rust в utex.io?
Меня интересует архитектура вида
* API Gateway
* Kafka
* воркеры с бизнес-логикой (слушают стримы Kafka, и туда же пишут)
Не нашёл гейтвея на Rust, но туда вполне можно поставить опенсорс на Golang
А вот воркеры на первый взгляд как раз без проблем можно писать на Rust
JDTamerlan
По моему мнению примерно 85% текущей ценности Rust содержится именно в cargo.
Именно cargo делает ту работу, которая сейчас унифицировано не делается в C/C++:
1) центральный репозиторий модулей (crate), а не отдельно каждый модуль из интернета
2) сборка проекта с зависимостями (здесь очень важное, что все другие проекты тоже собираются тем же способом, а не разными make, cmake, qmake и т.п.)
3) встроенные: модульное тестирование, форматирование кода и статический анализатор