Эта статья — для тех, кто только присматривается к Rust или слышал о нем лишь обрывки восторженных или невосторженных отзывов. Если вы не до конца понимаете, зачем миру понадобился еще один системный язык, какие «боли» C++ он лечит и где реально используется сегодня, — здесь вы найдете ответы на эти вопросы. Мы постарались структурированно представить информацию, чтобы у вас сложилась полная картина: что это за язык, зачем его учить и с чего начать освоение. Сразу оговоримся: если «The Book» давно стала вашей настольной книгой, вы уже собаку съели на управлении памятью и знаете все о владении и заимствовании, эта статья вряд ли вас удивит. Остальным же — добро пожаловать.

В апреле 2026 года произошло сразу два события, заставивших вновь говорить о Rust. 16 апреля вышел очередной стабильный релиз — Rust 1.95.0. А вскоре после этого Илон Маск заявил, что новый мессенджер XChat построен на Rust и «whole new architecture».

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

Что такое Rust, если объяснить в одном абзаце

Rust — это системный язык программирования, созданный для разработки высокопроизводительного и отказоустойчивого софта. Он компилируется в машинный код, не требует виртуальной машины вроде JVM и не использует сборщик мусора как Java, Go, JavaScript или Python.

Главная особенность Rust — безопасность памяти без garbage collector. То есть язык старается не допустить use‑after‑free, dangling pointer, double free, data race и другие неприятности еще на этапе компиляции.

Если совсем коротко:

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

Почему Rust вообще появился

Rust был создан Грейдоном Хоаром в Mozilla в период с 2006 по 2010 год. Хоар работал над языком в личное время, а затем Mozilla поддержала проект как перспективную платформу для разработки браузерных движков. Стабильная версия Rust 1.0 вышла 15 мая 2015 года.

Уже тогда команда формулировала задачу языка довольно четко: помочь строить надежные и эффективные системы, сочетая низкоуровневый контроль с высокоуровневыми гарантиями безопасности — без обязательного garbage collector и runtime.

Чтобы понять, зачем Rust был нужен, нужно вспомнить реальность системного программирования. Огромная часть критически важного ПО десятилетиями писалась на C и C++: браузеры, ядра ОС, драйверы, базы данных, сетевые сервисы. Эти языки невероятно мощные, но они дают разработчику очень много свободы. А свобода в работе с памятью часто означает, что программа может сделать что‑то опасное.

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

Chromium Project писал, что около 70% high severity security bugs в Chromium были memory unsafety problems, то есть ошибками работы с C/C++ pointers. Половина из них — use‑after‑free. Это не «70% всех багов Chrome», а именно серьезные security bugs, но цифра все равно очень показательная.

Rust появился как попытка закрыть эту инженерную дыру:

  • дать производительность уровня C/C++;

  • убрать значительную часть ошибок памяти;

  • не вводить GC;

  • сделать современный toolchain (менеджер пакетов, тестирование, документация);

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

Короткая формула:

Rust появился не потому, что миру не хватало еще одного языка программирования. Он появился потому, что у старых подходов был болезненный компромисс: либо быстро, но опасно; либо безопаснее, но с runtime, GC и меньшим контролем.

Какие проблемы других языков Rust пытался решить

Чтобы понять ценность Rust, нужно посмотреть на недостатки существующих языков.

C/C++: быстро, но опасно

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

Это делает C/C++ быстрыми и гибкими. Но цена — высокая вероятность ошибок.

Основные проблемы:

  • Memory safety (безопасность работы с памятью) полностью лежит на плечах разработчика.

  • Buffer overflow и buffer over‑read — запись или чтение за пределами выделенного буфера.

  • Use‑after‑free — использование указателя после освобождения памяти.

  • Double free — повторное освобождение одной и той же области памяти.

  • Dangling pointer — указатель, который больше не указывает на валидный объект.

Пример на C:

#include <stdio.h>
#include <string.h>

int main() {
    char name[8];

    strcpy(name, "very very long name");

    printf("%s\n", name);
    return 0;
}

Здесь массив name рассчитан на 8 символов, но мы записываем туда строку сильно длиннее. В C это может привести к повреждению памяти. Иногда программа просто упадет. Иногда будет вести себя странно. Иногда такая ошибка станет частью exploit chain.

Rust не разрешает такой стиль работы в safe‑коде. Например, если мы работаем с вектором, доступ по индексу проверяется:

fn main() {
    let items = vec![1, 2, 3];

    // println!("{}", items[10]); // panic: индекс вне границ
    println!("{:?}", items.get(10)); // None
}

items[10] приведtт к panic, потому что индекс вне границ. А items.get(10) вернет None, то есть безопасно скажет: «такого элемента нет».

Хороший исторический пример — Heartbleed в OpenSSL, 2014 год. В реализации механизма heartbeat для проверки активности соединения в OpenSSL отсутствовала корректная проверка границ. Клиент мог запросить «отправь мне 500 байт», а отправить только 1 байт данных. Библиотека на C честно читала 500 байт из памяти — в том числе чужие ключи шифрования, пароли, сессионные cookie. Технически это buffer over‑read (чтение за пределы), но корень тот же: отсутствие проверки границ.

Rust не делает невозможной любую уязвимость, но он резко снижает вероятность целого класса memory‑safety ошибок в safe‑коде.

Еще один пример — dangling pointer. В C++ можно случайно сохранить указатель на объект, который уже уничтожен:

#include <iostream>

int* make_number() {
    int value = 42;
    return &value; // опасно: возвращаем адрес локальной переменной
}

int main() {
    int* p = make_number();
    std::cout << *p << std::endl; // неопределенное поведение
}

value живет только внутри функции make_number. После выхода из функции память больше невалидна, но указатель p все еще «куда‑то» указывает.

Rust такой код в safe‑варианте не пропустит:

fn make_number() -> &i32 {
    let value = 42;
    &value
}

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

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

Java: безопаснее, но есть JVM и GC

Java решила многие проблемы C/C++ другим способом. Разработчик не освобождает память вручную. Этим занимается garbage collector — сборщик мусора.

  • JVM — Java Virtual Machine, виртуальная машина, внутри которой выполняется Java‑программа.

  • Runtime — дополнительное программное обеспечение, которое работает параллельно с программой и управляет ее выполнением.

  • GC — garbage collector, механизм автоматического удаления объектов, которые больше не используются.

  • Pause time — паузы, которые могут возникать из‑за работы GC. Самый заметный тип таких пауз — Stop‑the‑world (STW): моменты, когда JVM временно приостанавливает выполнение потоков приложения, чтобы выполнить часть работы сборщика мусора.

Для enterprise/backend Java — прекрасный выбор. Она зрелая, стабильная, с огромной экосистемой. Но для драйверов, ядра ОС, embedded, криптографии, некоторых low‑latency систем и очень чувствительных к задержкам компонентов JVM, GC и возможные STW‑паузы могут быть сильным ограничением.

Например, в Java можно получить NullPointerException:

class Main {
    static String findUserName(int id) {
        if (id == 1) {
            return "Alice";
        }
        return null;
    }

    public static void main(String[] args) {
        String name = findUserName(2);
        System.out.println(name.length()); // NullPointerException
    }
}

Проблема не в том, что Java плохая. Проблема в том, что null — это особое значение, которое может оказаться почти где угодно, если API это допускает.

В Rust отсутствие значения выражается типом:

fn find_user_name(id: u32) -> Option<String> {
    if id == 1 {
        Some(String::from("Alice"))
    } else {
        None
    }
}

fn main() {
    match find_user_name(2) {
        Some(name) => println!("{}", name.len()),
        None => println!("user not found"),
    }
}

Option<T> означает: значение либо есть (Some), либо его нет (None). Компилятор заставляет обработать оба случая. Это не гарантия, что в программе не будет логических ошибок, но шанс забыть про «пустое значение» становится гораздо ниже.

Go: проще, но меньше контроля

Go — отличный язык для написания инфраструктурного кода. Он проще Rust, имеет удобный garbage collector и отличную поддержку concurrency через горутины.

Однако Go тоже использует GC, а значит, имеет паузы и меньший контроль над памятью. Кроме того, Go не предотвращает data race (гонки данных) на этапе компиляции.

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

Пример идеи на Rust:

use std::thread;

fn main() {
    let mut counter = 0;

    thread::scope(|s| {
        s.spawn(|| {
            counter += 1;
        });

        s.spawn(|| {
            counter += 1;
        });
    });

    println!("{counter}");
}

Такой код не должен компилироваться: две задачи пытаются одновременно получить изменяемый доступ к counter.

Правильный вариант — явно использовать синхронизацию:

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));

    let mut handles = vec![];

    for _ in 0..2 {
        let counter = Arc::clone(&counter);

        handles.push(thread::spawn(move || {
            let mut value = counter.lock().unwrap();
            *value += 1;
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("{}", *counter.lock().unwrap());
}

Arc — атомарный счетчик ссылок для совместного владения между потоками.
Mutex — механизм, который разрешает доступ к данным только одному потоку за раз.

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

Python: быстро писать, но не для системного уровня

Python прекрасен для скриптов, data science, automation, backend и прототипов. Но он не создан для низкоуровневого управления памятью и предсказуемой производительности.

В реальных проектах часто встречается гибридный подход: бизнес‑логика, orchestration или data science‑пайплайн пишутся на Python, а performance‑critical части выносятся в C, C++ или Rust.

Хороший пример направления — data processing. В экосистеме Rust есть Polars и DataFusion: быстрые инструменты обработки данных, которые часто используют как альтернативу или дополнение к Python‑пайплайнам.

Как работает Rust? Основные концепции

Теперь к самому важному.

Главная идея Rust: ownership

Ownership — владение. В Rust у каждого значения есть ровно один владелец. Когда владелец выходит из области видимости, значение уничтожается, а ресурсы освобождаются. Это основной механизм, благодаря которому Rust управляет памятью без GC.

fn main() {
    let string1 = String::from("hello");
    let string2 = string1;

    // println!("{}", string1); // ошибка компиляции
    println!("{}", string2);
}

Что произошло? String хранит данные в куче. Когда мы пишем let string2 = string1, владение строкой переходит от string1 к string2. После этого string1 больше нельзя использовать.

В JavaScript, Java или Python такое поведение может показаться странным: «почему я не могу использовать старую переменную?» Но Rust делает это не из вредности. Он предотвращает ситуацию, когда две переменные считают себя владельцами одного ресурса и пытаются освободить его дважды.

Для простых типов вроде i32 все иначе:

fn main() {
    let x = 42;
    let y = x;

    println!("{x}");
    println!("{y}");
}

i32 копируется дешево, поэтому здесь работает Copy: обе переменные имеют собственное значение.

Borrowing: можно не отдавать, а одолжить

Если бы каждое использование значения требовало передачи владения, писать программы было бы неудобно. Поэтому в Rust есть borrowing — заимствование.

Заимствование — это временный доступ к значению без передачи владения.

fn main() {
    let name = String::from("Alice");

    print_name(&name);

    println!("{}", name);
}

fn print_name(value: &String) {
    println!("{}", value);
}

name владеет строкой. &name — ссылка, то есть временное заимствование. print_name не забирает строку себе. После вызова name все еще можно использовать.

Главное правило заимствования: можно иметь сколько угодно неизменяемых ссылок (&T) или одну изменяемую ссылку (&mut T), но не то и другое одновременно;

Lifetimes: ссылки не должны жить дольше данных

Lifetime — время жизни ссылки. Это период, в течение которого ссылка остается валидной.

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

fn longest<'a>(first: &'a str, second: &'a str) -> &'a str {
    if first.len() > second.len() {
        first
    } else {
        second
    }
}

'a означает: возвращаемая ссылка живет не дольше, чем входные ссылки. То есть Rust не позволит вернуть ссылку на данные, которые уже уничтожены.

Сначала lifetimes выглядят немного непонятно. Но идея простая: ссылка не должна пережить значение, на которое она указывает.

Borrow checker: строгий компилятор‑друг

Borrow checker — часть компилятора Rust, которая проверяет правила владения, заимствования и времён жизни.

Новички часто воспринимают его как врага. Пишешь простой код — компилятор ругается. Передаешь значение — оно «переехало». Берешь две mutable‑ссылки — нельзя. Возвращаешь ссылку — lifetime не подходит.

Но через какое‑то время становится понятно: borrow checker не мешает писать код. Он мешает писать код, о котором вы пожалеете через полгода.

Почему Rust не нужен garbage collector

В языках с GC объект живёт до тех пор, пока сборщик мусора не решит, что объект больше недостижим.

В Rust значение уничтожается, когда выходит из области видимости:

fn main() {
    {
        let message = String::from("hello");
        println!("{}", message);
    }

    // здесь message уже уничтожен
}

Здесь message живет только внутри блока { ... }. Когда блок заканчивается, Rust вызывает деструктор и освобождает ресурсы.

Это похоже на автоматическое управление памятью, но без отдельного GC‑процесса. Rust заранее проверяет, что после уничтожения значение больше не используется.

Stack и heap простыми словами

Чтобы понять Rust, полезно вспомнить разницу между stack и heap.

Stack — стек. Быстрая область памяти для локальных значений фиксированного размера. Работает по принципу LIFO: last in, first out. Функция вызвалась — ее локальные переменные появились на стеке. Функция завершилась — они исчезли.

fn sum(a: i32, b: i32) -> i32 {
    let result = a + b;
    result
}

a, b, result — простые значения, которые обычно живут на стеке.

Heap — куча. Область памяти для данных динамического размера: строк, векторов, деревьев, объектов, размер которых может быть неизвестен на этапе компиляции.

fn main() {
    let text = String::from("hello");
    let numbers = vec![1, 2, 3];
}

String и Vec хранят метаданные на стеке, а реальные данные — в куче.

Для явного размещения значения в куче есть Box<T>:

fn main() {
    let number = Box::new(10);

    println!("{}", number);
}

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

Option вместо null

В Rust нет привычного null.

Если значения может не быть, это должно быть видно в типе:

fn find_user(id: u32) -> Option<String> {
    if id == 1 {
        Some(String::from("Alice"))
    } else {
        None
    }
}

fn main() {
    let user = find_user(2);

    match user {
        Some(name) => println!("User: {}", name),
        None => println!("User not found"),
    }
}

Option<T> — это enum:

enum Option<T> {
    Some(T),
    None,
}

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

В Java вы часто узнаёте о null уже в runtime. В Rust отсутствие значения — часть модели типов, и компилятор заставляет с этим считаться.

Result вместо исключений

Rust не использует исключения как основной способ обработки ошибок. Для восстанавливаемых ошибок есть Result<T, E>.

fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("division by zero"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    match divide(10, 0) {
        Ok(value) => println!("Result: {}", value),
        Err(error) => println!("Error: {}", error),
    }
}

Result<T, E> означает: либо Ok(T), либо Err(E).

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

Для короткой передачи ошибки наверх используется оператор ?:

use std::fs;

fn read_config() -> Result<String, std::io::Error> {
    let content = fs::read_to_string("config.txt")?;
    Ok(content)
}

Если read_to_string вернёт Ok(String), строка попадет в content. Если вернет Err(std::io::Error), ошибка сразу вернется из read_config.

? заменяет типичный шаблон match Ok/Err и делает код короче без скрытых исключений.

match: не switch, а мощнее

match похож на switch, но намного сильнее. Он работает с enum, структурами, кортежами, диапазонами и шаблонами.

fn main() {
    let status = divide(10, 2);

    match status {
        Ok(value) => println!("Result: {}", value),
        Err(error) => println!("Error: {}", error),
    }
}

Если у enum есть несколько вариантов, Rust заставляет обработать все. Это снижает вероятность забыть какой‑то сценарий.

Например:

enum PaymentStatus {
    Paid,
    Pending,
    Failed(String),
}

fn print_status(status: PaymentStatus) {
    match status {
        PaymentStatus::Paid => println!("paid"),
        PaymentStatus::Pending => println!("pending"),
        PaymentStatus::Failed(reason) => println!("failed: {}", reason),
    }
}

Если завтра мы добавим новый вариант Refunded, компилятор покажет места, где match больше не покрывает все варианты. Для поддержки кода это очень сильная вещь.

Traits: интерфейсы, но не совсем

trait в Rust похож на интерфейс в Java или TypeScript: он описывает поведение, которое должен реализовать тип.

trait Drawable {
    fn draw(&self);
}

struct Button {
    label: String,
}

impl Drawable for Button {
    fn draw(&self) {
        println!("Drawing button: {}", self.label);
    }
}

Теперь можно написать функцию, которая принимает все, что умеет рисоваться:

fn render(item: &impl Drawable) {
    item.draw();
}

Функции важно не то, какой конкретно тип передали, а какое поведение он умеет предоставлять.

Fearless Concurrency (бесстрашная многопоточность)

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

Zero‑cost abstractions: высокоуровневый код без лишней цены

Один из принципов Rust — zero‑cost abstractions.

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

Например, итераторы:

fn sum_even_squares(values: &[i32]) -> i32 {
    values
        .iter()
        .filter(|x| **x % 2 == 0)
        .map(|x| x * x)
        .sum()
}

Код выглядит высокоуровневым: filter, map, sum. Но компилятор может оптимизировать такую цепочку в эффективный машинный код. Операции вроде map, filter, reduce должны создавать код, столь же эффективный, как эквивалентные императивные реализации.

Unsafe Rust: если Rust безопасный, зачем ему unsafe

Rust не запрещает низкоуровневое программирование. Для задач, где компилятор не может полностью доказать безопасность, есть unsafe.

Официальная книга Rust описывает пять возможностей unsafe: разыменование raw pointer, вызов unsafe‑функций, доступ или изменение mutable static, реализация unsafe trait и доступ к полям union. При этом unsafe не выключает весь borrow checker и все проверки Rust: оно только дает доступ к операциям, безопасность которых должен гарантировать сам разработчик.

Зачем это нужно? Например:

  • писать драйверы;

  • работать с C‑библиотеками через FFI;

  • реализовывать низкоуровневые структуры данных;

  • писать ядро ОС;

  • оптимизировать горячие участки кода;

  • взаимодействовать с железом.

Пример raw pointer:

fn main() {
    let mut value = 10;

    let ptr = &mut value as *mut i32;

    unsafe {
        *ptr += 1;
    }

    println!("{}", value);
}

В safe Rust мы работаем со ссылками &T и &mut T. Raw pointers const T и mut T похожи на указатели C/C++, но Rust не гарантирует их безопасность. Создать raw pointer можно в safe‑коде, а вот разыменование требует unsafe.

Хорошая практика: unsafe‑кода должно быть мало, он должен быть изолирован и закрыт безопасным API.

Как работает Rust‑программа

Rust — компилируемый язык

Rust — компилируемый язык. У него нет интерпретатора в стиле Python или JavaScript. Вы пишете код, компилятор превращает его в машинный код (через промежуточное представление LLVM), и на выходе получается готовый исполняемый файл (бинарник).

Ключевые термины:

  • компилятор — программа, которая переводит исходный код в исполняемый файл (rustc);

  • интерпретатор — программа, которая выполняет код напрямую (Python, Ruby, JS);

  • LLVM — инфраструктура компиляции, используемая Rust, Swift, Clang и другими для генерации оптимизированного кода под разные архитектуры;

  • crate — минимальная единица компиляции и базовый элемент модульной системы языка. Если проводить аналогии с другими языками, то это аналог «библиотеки» (library) или «пакета» (package). Все, что вы пишете на Rust, в конечном итоге упаковывается в крейт.

  • Cargo — система сборки и менеджер зависимостей.

Два типа крейтов

Крейты делятся на две основные категории в зависимости от их назначения:

  • Binary Crate (Бинарный крейт): Это программа, которую можно скомпилировать в исполняемый файл. Такой крейт обязательно содержит функцию main(), которая является точкой входа. Обычно основным файлом такого крейта является src/main.rs.

  • Library Crate (Библиотечный крейт): Это набор кода (функций, типов, структур), предназначенный для повторного использования в других проектах. В нем нет функции main(), и он не запускается сам по себе. Основной файл — src/lib.rs.

Cargo: почему tooling Rust так хвалят

Cargo — это package manager, build system, test runner и инструмент публикации пакетов в одном. Официальная документация Cargo описывает его как менеджер пакетов Rust, который скачивает зависимости, компилирует пакеты, делает distributable packages и загружает их на crates.io.

Для разработчика из JS‑мира Cargo можно грубо представить как «npm + build tool + test runner + documentation tool». Но в отличие от многих экосистем, здесь это стандартный путь, а не набор разрозненных решений.

Stack Overflow Developer Survey 2025 отдельно отмечает Cargo как самый admired cloud development and infrastructure tool с показателем около 71%. Rust там же снова назван самым admired programming language с показателем около 72%.

Основные команды:

  • создание нового проекта (cargo new);

  • сборка (cargo buildcargo build --release);

  • запуск (cargo run);

  • тестирование (cargo test);

  • форматирование кода (cargo fmt);

  • линтинг (cargo clippy);

  • генерация документации (cargo doc).

Crates: библиотеки Rust

Crate — пакет или библиотека в Rust.

crates.io — официальный реестр пакетов.

Rust‑экосистема меньше, чем npm, Maven или PyPI, но во многих направлениях она уже очень зрелая.

Чаще всего в реальных проектах встречаются:

  • serde, serde_json — сериализация и десериализация;

  • tokio, async-std — async runtime;

  • axum, actix-web, rocket — web backend;

  • reqwest, hyper — HTTP;

  • clap — CLI‑аргументы;

  • anyhow, thiserror — ошибки;

  • tracing — логирование и observability;

  • sqlx, diesel, sea-orm — базы данных;

  • wasm-bindgen, wasm-pack — WebAssembly;

  • tauri, egui — desktop/cross‑platform UI;

  • polars, datafusion — обработка данных;

  • rayon — параллельные вычисления.

Итого, что предотвращает и не предотвращает Rust

Rust не делает невозможными все ошибки, но резко снижает класс memory safety проблем.

Он предотвращает на этапе компиляции:

  • use‑after‑free,

  • buffer overflow/over‑read (в safe‑коде),

  • data race в многопоточном коде,

  • dangling pointers и так далее

Однако важно понимать: Rust ≠ полная безопасность приложения.

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

  • криптографический протокол;

  • бизнес‑логику;

  • хранение ключей;

  • privacy‑модель;

  • архитектуру доступа;

  • уязвимости типа логических ошибок, инъекций или неверных разрешений.

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

Плюсы и минусы Rust

Плюсы Rust

Главные плюсы Rust:

  • безопасность памяти без GC;

  • высокая производительность;

  • отсутствие обязательного runtime;

  • строгая система типов;

  • Option<T> вместо null;

  • Result<T, E> вместо скрытых исключений;

  • безопасная многопоточность;

  • современный tooling;

  • Cargo и crates.io;

  • хорошая совместимость с C через FFI;

  • сильные возможности для системного программирования;

  • хорошие сообщения компилятора;

  • встроенная поддержка тестирования и документации.

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

Минусы Rust

Минусы тоже реальные.

Самые заметные:

  • высокий порог входа;

  • ownership и borrowing раздражают новичков;

  • lifetimes могут быть сложными;

  • компиляция иногда долгая;

  • async Rust требует привыкания;

  • меньше библиотек, чем у Java/Node/Python в некоторых доменах;

  • меньше вакансий, чем для массовых языков;

  • не всегда подходит для быстрой продуктовой разработки;

  • unsafe‑код требует дисциплины;

  • некоторые структуры данных, например графы с двунаправленными ссылками, писать сложнее, чем в языках с GC.

Rust удобен не во всем: двунаправленные ссылки, Rc, Weak, RefCell, внутренняя изменяемость и графовые структуры требуют понимания модели владения. Это не «невозможно», но точно сложнее, чем просто хранить ссылки в языке с GC.

Почему компании переписывают части систем на Rust

Обычно компании не переписывают весь продукт на Rust. Это дорого, рискованно и часто не нужно.

Гораздо чаще Rust внедряют точечно: в сетевые компоненты, парсеры, sandbox, криптографию, CLI, агенты, runtime‑компоненты, драйверы, сервисы с высокой нагрузкой.

Причины обычно прагматичные:

  • Снижение memory‑safety рисков — меньше уязвимостей, с которыми приходится жить годами.

  • Выше надежность — Rust‑код реже падает в production из‑за неопределённого поведения.

  • Меньше инцидентов — команды тратят меньше времени на отладку багов, связанных с указателями.

  • Лучший контроль ресурсов — отсутствие GC позволяет точно предсказывать потребление памяти и CPU.

  • Экономия на инфраструктуре — оптимизированный код может обрабатывать ту же нагрузку на меньшем количестве серверов.

Google пишет, что в Android подход с Rust уже дал серьезный эффект: в данных 2025 года memory safety vulnerabilities впервые упали ниже 20% от общего числа уязвимостей, а Rust‑код показал заметно меньшую density memory‑safety уязвимостей по сравнению с C/C++.

В Linux kernel есть официальная документация по Rust внутри ядра. Это не значит, что ядро Linux переписывают на Rust целиком, но Rust стал официально поддерживаемым направлением для части kernel‑разработки.

Microsoft также развивает направление Rust for Windows Drivers: в 2025 году Microsoft писала о состоянии Rust для Windows‑драйверов и своём видении развития этого направления.

AWS Firecracker — пример инфраструктурного проекта, написанного на Rust: AWS описывала Firecracker как lightweight virtualization technology для serverless workloads, а в анонсах подчеркивала выбор Rust из‑за thread safety и предотвращения многих buffer overrun ошибок.

Cloudflare использует Rust в Pingora — своем фреймворке для быстрых и надежных сетевых систем. Cloudflare писала, что Pingora, построенный на Rust, обслуживает значительную часть инфраструктурного трафика компании.

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

Где Rust лучше всего использовать

Rust особенно силен там, где одновременно важны скорость, надежность и контроль.

Хорошие сценарии:

  • backend и высоконагруженные сервисы;

  • API, proxy, realtime‑сервисы, очереди;

  • CLI‑инструменты;

  • системное программирование;

  • драйверы, ядро, низкоуровневые библиотеки;

  • embedded и IoT;

  • WebAssembly;

  • криптография и security‑sensitive software;

  • парсеры;

  • data processing;

  • blockchain и инфраструктура.

Где Rust может быть плохим выбором

Rust не нужен везде.

Он может быть плохим выбором, если вы делаете MVP за два дня, простую админку, обычный CRUD‑backend или продуктовую фичу, где команда гораздо сильнее в Java, Node.js, Go или Python.

Rust также может быть избыточен, если главная задача — быстро нанять команду, быстро проверить гипотезу или использовать экосистему, где Python/Java/JS дают готовые решения быстрее.

Еще один честный минус — learning curve. Ownership, borrowing и lifetimes сначала непривычны. Разработка на старте может идти медленнее. Компиляция больших проектов может занимать заметное время. Async Rust сложнее, чем async в JS или Go.

Поэтому Rust — не универсальная замена всем языкам. Его сила проявляется там, где цена ошибки, лишней памяти или непредсказуемой производительности действительно высока.

Rust в 2026 году: статистика

Rust уже не экспериментальный язык, но и не массовый язык уровня JavaScript, Python или Java.

По Stack Overflow Developer Survey 2025 Rust снова стал самым admired programming language с показателем около 72%, но доля worked‑with среди всех респондентов — 14.8%. То есть Rust очень любят те, кто с ним работает, но он пока не стал массовым языком для всех подряд.

TIOBE Index за апрель 2026 показывает Rust в top 20, но отмечает замедление роста: после пика на #13 в начале года Rust опустился к #16. Сам TIOBE подчёркивает, что индекс показывает популярность, а не качество языка.

PYPL показывает Rust примерно в районе top 10 по tutorial/search interest: интерес к изучению сохраняется, хотя Rust не обгоняет массовые языки.

State of Rust Survey 2025, опубликованный в марте 2026 года, собрал 7156 ответов и показывает зрелое сообщество, но также фиксирует знакомые проблемы: сложность языка, ожидания вокруг compiler performance и развитие экосистемы.

Итог простой: Rust — не хайп на один сезон. Но его роль не в том, чтобы заменить все языки, а в том, чтобы занять сильные ниши: infrastructure, backend, systems, security, embedded, WebAssembly и performance‑critical компоненты.

Как изучать Rust

Лучше не начинать с async, unsafe и макросов. Сначала нужно понять базу.

С чего начать:

  • базовый синтаксис (переменные, циклы, функции);

  • structenummatch;

  • модули;

  • Option и Result;

  • ownership, borrowing, lifetimes.

Лучшие ресурсы:

  • «The Rust Programming Language» (официальная книга, «The Book») — бесплатно онлайн, есть русский перевод. Обязательна к прочтению.

  • Rust by Example — учимся на готовых примерах (тоже переведена на русский).

  • Rustlings — маленькие упражнения в терминале, чтобы закрепить материал.

  • The Cargo Book — документация по Cargo.

  • Rustonomicon — для тех, кто хочет глубоко разобраться в unsafe.

  • Tokio Tutorial — асинхронное программирование.

  • «Zero To Production In Rust» — для backend‑разработчиков.

  • «Command‑Line Rust» — для тех, кто хочет делать CLI‑утилиты.

  • «Programming Rust» (ОʼReilly) — более глубокая книга.

  • «Rust for Rustaceans» — для тех, кто уже знает основы.

Rust лучше изучать через практику. Сначала CLI‑проекты, потом backend, WASM, embedded или системные задачи — в зависимости от интереса.

Хорошие первые проекты: 

  1. CLI todo‑list.

  2. Mini grep (поиск строк в файле).

  3. Парсер логов (например, веб‑сервера) с агрегацией статистики.

  4. JSON/CSV‑парсер.

  5. HTTP API на Axum (CRUD по пользователям).

  6. Telegram/Discord бот.

  7. Многопоточный парсер логов (каждый файл в отдельном потоке, сбор статистики через mpsc).

  8. WebAssembly‑модуль, вызываемый из JavaScript.

  9. Небольшой прокси‑сервер (HTTP или TCP).

  10. Маленький embedded‑проект (если есть интерес к железу, например, мигание светодиодом на STM32 или Raspberry Pi Pico).

Стоит ли учить Rust

Да, если:

  • Вы хотите понимать системное программирование на глубоком уровне: как работают память, указатели, стек, куча.

  • Работаете с высоконагруженным бэкендом, микросервисами, нуждаетесь в предсказуемой производительности.

  • Вам интересны безопасность, инфраструктура, блокчейн, embedded, WebAssembly.

  • Хотите прокачать мышление о памяти, типах, владении и конкурентности — эти навыки пригодятся в любом языке.

  • Вы готовы потратить несколько недель на преодоление начальной сложности.

Не обязательно прямо сейчас, если:

  • Вы занимаетесь только простым фронтендом (React/Vue/Svelte) и не планируете использовать WASM или bэкенд.

  • Ваш текущий стек (Java enterprise,.NET, Go) полностью закрывает потребности компании, и у вас нет задачи внедрять низкоуровневые оптимизации.

  • Команда не готова к переходу на Rust.

Rust не заменит JavaScript во frontend. Не заменит Python в data science. Не заменит Java во всем enterprise. Не заменит Go во всех микросервисах. И не обязан.

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

Заключение

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

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

XChat — хороший повод снова поговорить о Rust. Но главный вывод не в том, что «раз Маск выбрал Rust, значит всем нужно срочно переписывать проекты». Главный вывод в другом: если новая система должна быть быстрой, надежной, безопасной по памяти и готовой к высокой нагрузке, Rust становится очень сильным кандидатом.

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

Комментарии (57)


  1. liquidgel
    07.05.2026 15:07

    Проблему с unsafe как-то решили? Дико смешно было лет 8 назад читать как они настолько сильно упоролись безопасностью что без unsafe нельзя было написать примерно никакой серьезный софт, а любые подтянутые в проект либы (разумеется unsafe) по цепочке делали и твой софт unsafe. В итоге единственное конкурентное преимущество языка его же и хоронило


    1. Boneyan
      07.05.2026 15:07

      Нет проблемы с unsafe - это легитимная часть языка и никто от неё избавляться не собирается. Не для всех вещей компилятор может доказать безопасность, в таких случаях используется unsafe. unsafe не делает “по цепочке” весь остальной код unsafe. unsafe код можно вызывать внутри safe кода. Обычно это небольшие небезопасные участки, обёрнутые в безопасное api.


      1. TimurZhoraev
        07.05.2026 15:07

        Вообще говоря safe/unsafe это маркетинговый ход нежели технические подробности. Язык должен предоставить какое-либо число, чтобы это измерить. Например, в случае гонки - какие объекты пытаются обратиться к шаре, проблема чтение-модификация-запись при статическом анализе, теоретическая глубина стека при рекурсии или измеренная в реалтайм, сведения о динамических аллокаторах особенно в многопоточке, может даже что-то связанное с DMA, то есть если язык не поддерживает такого рода числа - то это сомнительное преимущество по сравнению с явным указанием флажков, счётчиков ссылок, дополнительных состояний объекта итд. Да это неудобно, но зато прозрачно и позволяет выявить при дебаге всевозможные комбинации. Тем более как только появляется FFI/mmap особенно в ембедде - даже на одном таком пороге ломается вся концепция. По сути язык стал обвязкой вокруг некоего memory and object manager. Но что-то явных преимуществ нет. То есть универсальная попытка встать между компиляцией и интерпретацией скорее всего тупиковая.


      1. liquidgel
        07.05.2026 15:07

        Нет проблемы с unsafe - это легитимная часть языка и никто от неё избавляться не собирается

        Т.е. язык как был мертворожденным, так им и остался


    1. ReadOnlySadUser
      07.05.2026 15:07

      Я не пишу ничего не Rust, но так уж вышло, что более или менее его знаю.

      В чем проблема с unsafe? Ничего страшного в unsafe нет)

      В сущности, это просто маркер мест в коде, которые могут покорраптить память и просто упрощают отладку этого класса проблем.

      Почти любой unsafe можно спрятать за safe API, просто обеспечив гарантию инвариантов, делающих unsafe - безопасным) Это же главная задача и смысл unsafe блоков)


      1. evgen_hi
        07.05.2026 15:07

        Не всякий unsafe можно сделать безопасным. Особенно если речь идёт о FFI. Если ошибка вскроется, тот тут не нога полетит, а вся голова


    1. 00Kirill00
      07.05.2026 15:07

      Ансейф изолируется в маленьких модулях под капотом библиотек. Тебе в прикладном коде вообще не нужно трогать его руками


  1. TimurZhoraev
    07.05.2026 15:07

    Проблема главная и немаловажная - нет стандарта по которому можно получить гарантированные компиляторы. Это же касается и Питона. Версии 2.7 и 3 стали именем нарицательным. Выйдет Rust 2.0 вот и придётся думать что там нужно прогнать через Perl/Python Regexp-ом чтобы починить кодовую базу. Думается что с LLM актуальность не стандартизированных языков существенно снизилась, потому как для работы нужны предсказуемые инструменты а не синтаксический сахар, который уже LLM инкапсулирует до уровня отсутствия в нём необходимости, включая покрытие тестами и технический регламент (саммари). Вообщем останется только Python и K&R C или Страуструп С++ образца начала 90-х, включая диалекты близкие к синтаксису Java/OpenCL(MP)/CUDA, благодаря феноменальной наработанной кодовой базе по существу (может даже ещё Fortran), а не трансляторами Java/Python/C++ -> Rust. Да, Go/Ruby/Rust и др современные, но их начало совпало как раз с этими кодогенераторами, вообщем те языки разработка которых пришлась где-то на 2015-20й год скорее всего будут свёрнуты, так как датасетов для претренинга от них маловато, они достаточно далеки от железа и имеют коммьюнити с минимальной поддержкой, язык нужно учить, развивать и делать это промышленным способом а не энтузиазмом разработчиков.


    1. Dhwtj
      07.05.2026 15:07

      Rust обещает полную совместимость внутри edition

      Это лучше чем ломающие изменения питон и лучше чем дряхлый C++

      А вообще, несёте херню


      1. TimurZhoraev
        07.05.2026 15:07

        Rust обещает полную совместимость

        Разработчики языка могут этим заниматься сколько угодно, может появится 3 ветки языка Rust% какой-нибудь. Важно то, что дряхлые плюсы всё-таки имеют ISO/IEC 14882, это позволяет исключить бардак с инструментами и библиотеками. Во всяком случае можно апеллировать к документу уровня ГОСТ, описывающему язык. У Питона с этим тоже беда, но он по крайней мере стал BASIC-ом 21го века благодаря грамматике, умещающейся на тетрадный лист. Любой Bison/Flex влезит в контекст даже самой захудалой LLM-ки, это его очень хорошо вытаскивает + Pip-ы на все случаи жизни. Раст не может похвастать таким коммьюнити, так как язык скорее нишевый чем общего назначения. Если для Питона или того же Perl есть pip и Cpan, то что ищется для Раста с первой же строчки поисковика? Что там нужно набрать "rust library modules", "rust library hub" (по иронии попадаем сюда), "rust modules library collection"... все пути ведут к git. Язык без поддержки порталом по скачиванию чего угодно минуя заклинания git встроенными средствами - это путь к забвению. Мне охота высветить что-то на выводы малины нолики-единички. Куда обратиться кроме частных лиц (обратите внимание - автор свернул проект в архив)? Для питонов делаю в один клик. Для С коммьюнити куда больше так как там вся обвязка и есть "протухший" С. Тем более можно забыть что LLM уже генерит на С даже для эмбеда и микроконтроллеров-восьмибиток, там причёсывание из разряда поправить порядок инициализации перифирии и пару бит. Нашёл тут как-то либу на Раст для энкодера полнорегистровую, заставил LLM переписать на С, чтобы управлять скудной памятью самостоятельно, ESP32 это из пушки по воробьям. Прекрасно получилось хоть для атмелки хоть для кортекса м0/3.


        1. Dhwtj
          07.05.2026 15:07

          апеллировать к документу уровня ГОСТ, описывающему язык

          Не пойму к чему вы

          Вот проявится у Rust второй компилятор тогда может и понадобится что-то вроде ГОСТ

          Да и неопределенное поведение в c++ штука неприятная при наличии всех типа ГОСТ

          rust library modules

          https://crates.io/


          1. TimurZhoraev
            07.05.2026 15:07

            Craits.io - такое ощущение что это запрятано от всех поисковиков, а если более точно то https://crates.io/. Я захожу на главный портал языка https://rust-lang.org/ - и собственно где ссылка на это? Захожу сюда https://rust-lang.org/community/ - где этот кратэс... или разработчики его сами боятся. Я нашёл его только в https://rust-lang.org/tools/ мелким шрифтом где-то в глубине фрейма. Вообщем отношение такое к пользователю из разряда вот мы тут что-то придумали хорошее, но то, как это хорошо использовать - додумывайте сами. В плюсах и сях неопределённое поведение уже давным давно решается определёнными вставками. Любой санитайзер выловит любые утечки, зато это можно записать для любой платформы начиная от Спектрумов до ПЛИС-ов. Тем более с LLM-кой которая качественно обёртывает тестами своё творчество контроль за памятью уже не проблема как год или даже полтора.


            1. sdramare
              07.05.2026 15:07

              где этот кратэс.  или разработчики его сами боятся

              Первая же ссылка на "главном портале" ведет на страницу документации "The Rust Programming Language", подразумевая что взрослый человек, который решил изучать новый язык программирования, не будет тыкаться как слепой котенок по "порталу" и поисковикам, а откроет официальный учебник по языку и прочитает ее. В главе 14 мы видим

              More About Cargo and Crates.io

              So far, we’ve used only the most basic features of Cargo to build, run, and test our code, but it can do a lot more. In this chapter, we’ll discuss some of its other, more advanced features to show you how to do the following:

              • Customize your build through release profiles.

              • Publish libraries on crates.io.

              • Organize large projects with workspaces.

              • Install binaries from crates.io.

              • Extend Cargo using custom commands.

              https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html

              Более того, взрослый человек еще обычно в состоянии открыть документацию на toolchain, который он использует. Что же мы видим на первой странице документации утилиты "cargo":

              Cargo is the Rust package manager. Cargo downloads your Rust package’s dependencies, compiles your packages, makes distributable packages, and uploads them to crates.io, the Rust community’s package registry. You can contribute to this book on GitHub.

              https://doc.rust-lang.org/cargo/

              Так что и от кого тут спрятали?


              1. TimurZhoraev
                07.05.2026 15:07

                 В главе 14 мы видим

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

                Так что и от кого тут спрятали?

                На основном портале языка необходимо догадываться где искать хаб с библиотеками? Я захожу на Perl - вижу CPAN, на Python - PyPl сходу. На первый взгляд Cargo - это какой-то диалект вроде Raku для Perl.

                Вообщем невнимательное отношение к пользователю в мелочах. А раз на мелочи нет времени (и средств), что вполне очевидно, то это означает, что сейчас просто снимаются сливки за счёт остатков старожил, как это происходит с некогда взлетевшим Delphi, компании передаются из рук в руки, разработка уходит куда-то в какие-то ниши (похоже закончится всё панелями операторов и SCADA, но там уже LabView подъест довольно хорошо, так как язык сопровождается стандартами и аппаратурой), но на общее применение уже рассчитывать не приходится.

                Язык не хороший, не плохой, он другой и взлетел с опозданием благодаря энтузиастам. Тем более необходимо тратить время на изучение (даже не обучение - его нет среди вырванных кусков примеров). Без стандартизации его могут не принять на полномасштабный претренинг LLM, а без этого сейчас уже практически никуда, каждый год уже наверное за 5 лет идёт. Плюс устаревшая инфраструктура по сборке, особенно отладке из середины 90-х похожая на CMake. Про debug ни одного слова в разделах инструкции! Так вот в этом у Rust провал с таким отношением к пользователю, но это простительно, потому как скорее всего нет столько средств для полномасштабной поддержки. Cargo-механизмы практически главное, это как часть языка, но они упоминаются лишь как командная строка. Как я этот карго могу затолкать в имеющиеся системы сборки итд, как прикрутить автоматизацию.

                Сейчас язык - не основное, важна инфраструктура и коммьюнити вокруг него. Все эти фишки уже никого не интересуют, тем более что сейчас уже более менее развился многоязыковый подход, даже поддерживаемый IDE. Если что необходимо сделать быстро и прототип - Python, ближе к железу - C/С++ и его диалекты, посчитать формулы - Maxima/Matlab, для дремучих CAD - AutoLisp, файлодробилка - make или bash, для всего остального - кодогенерация и мета-языки сделанные по месту. Какая ещё документация на toolchain? Опять лезть в man-ы в 21м веке? Даже для дремучего С все эти флажки автоматизировали до уровня локального поисковика, по С++ есть и интерактивные и RAG/MCP/векторные с прикруткой к агентам, да, для раста это тоже есть но какие-то единичные проекты.

                Все вопросы которые должны быть первые они в на последних страницах. Как запустить как отладить, где скачать, где пример по ошибке компилятора, как исправить, и всё это без костылей и поиска копипастом или ссылками на Рэдит или Соурсфорж. Язык попытался объять необъятное с сомнительным штучным преимуществом. Колоссальная работа, но получился Вавилон. Решение вопросов не должно перекладываться на пользователей. Ну и наличие микса safe-unsafe 50/50 как-то несколько подавляет впечатление о преимуществах, то есть пользователь должен заранее это предполагать, то есть помогать компилятору, обычно наоборот.

                Вообще говоря специально потратил тут карму чтобы в большей мере уяснить, что политика Хабра - не очень соответствует дискуссионной. Писать только то что хотят прочитать и комментировать только то что не вызывает сомнений. Скатится рано или поздно всё в унылость, уже обратил внимание что самые кармистые обычно избегают диалогов в режиме объективного (!) спора, в котором рождается истина, и прибегают к игре в поддавки.


                1. AndreyDmitriev
                  07.05.2026 15:07

                  Тут как раз много о чём можно подискутировать. Вот моя точка зрения. Я учил программирование в Политехе больше тридцати лет назад начав с Фортрана и затем Паскаль. Эти два языка были стандартны для двух семестров. В основном численные методы (это был физтех), всякие Рунге-Кутта на Фортране да "Жизнь" Конвея на Паскале. Затем в лаборатории Физтеха я нашёл компилятор Си на ДВК и взял в библиотеке Кернигана и Ричи. Я читал эту книгу взахлёб, это было офигенно после Паскаля и я написал программу для дифрактометра на Си (и ассемблере MACRO-11), это было реально круто, я открывал всё новые стороны языка и железки, сидел по ночам в лаборатории, меня было за уши не оттащить от компьютера. Это начало девяностых где-то. Си++ мне "не зашёл", я честно взял Страуструпа в читальном зале, и эта книга "вынесла мне мозг", зато я "потрогал" Модулу-2 и Оберон, и это было хорошо, свежая струя после Паскаля. Так уж получилось, что я не стал физиком, а стал программистом и в коммерческой разработке ваял на Дельфи, потом на LabVIEW (это меняет сознание) и на Си да Ассемблере. За это всё неплохо платят.

                  Я всегда интересовался другими языками, особенно функциональными (Scala, Erlang, Haskell) чисто для кругозора, Раст "зашёл" со второй попытки, когда я наконец "вкурил" его основы. Он заметно отличается от Си идеологически, хоть и позиционируется как замена. Но теперь меня не оттащить от Раста, как когда-то от Си. Только Си образца девяностых по сравнению с Растом 2024 года прост как пять копеек, так что количество моих "открытий" тут зашкаливает. Что-то нравится, что-то не очень, но это те же ощущения, что от Си, только ня порядок мощнее.

                  Хорошая литература важна, я бы бесусловно порекомендовал The Rust Programming Language, при этом именно третье, последнее издание, она только что вышла:

                  И мне ещё понравились немецкие книжки, я их случайно в местной библиотеке взял (да, я тот динозавр, что ходит в городскую библиотеку), та, что поменьше, повторяет предыдущее издание Клабника, а вот та, что побольше свежая и хорошо его дополняет (на немецком мне проще, я больше 25 лет живу в Германии), два месяца я их читал:

                  Что б вы понимали масштаб бедствия — у меня в библиотеке почти полсотни книжек по Расту:

                  И в каждой есть свои "зёрна". Я это всё к тому, что "орешек знанья твёрд, но всё же мы не привыкли отступать..."

                  Ну а что касается прикладных фишек, вот смотрите — мы в соседней статье упражнялись в изучении особенностей статической линковки С++, вы, вроде, там тоже были. Я изучал зависимости собранных файлов и в какой то момент меня инструмент, которым я обычно пользовался (на .net) подзадолбал, так что я открыл терминал, набрал там cargo new r-depend... Затем сходил в копилот и попросил его набросать код, который взял бы DLL или исполняемый файл и показал бы зависимости, он выдал это, двадцать строчек кода:

                  use pelite::pe64::*;
                  use std::{env, error::Error, fs, path::Path};
                  
                  fn main() -> Result<(), Box<dyn Error>> {
                      let path = env::args().nth(1)
                          .ok_or("Usage: dep_viewer <path_to_exe_or_dll>")?;
                  
                      let data = fs::read(Path::new(&path))?;
                      let pe = PeFile::from_bytes(&data)?;
                      let imports = pe.imports()?;
                  
                      println!("Dependencies for {}:", path);
                  
                      for import in imports {
                          println!("  {}", import.dll_name()?);
                      }
                  
                      Ok(())
                  }

                  Теперь осталось cargo add pelite — тут используется крейт PeLite и следом собираем cargo build -r и наступает счастье:

                  >r-depend.exe r-depend.exe
                  Dependencies for r-depend.exe:
                    api-ms-win-core-synch-l1-2-0.dll
                    KERNEL32.dll
                    ntdll.dll
                    VCRUNTIME140.dll
                    api-ms-win-crt-runtime-l1-1-0.dll
                    api-ms-win-crt-math-l1-1-0.dll
                    api-ms-win-crt-stdio-l1-1-0.dll
                    api-ms-win-crt-locale-l1-1-0.dll
                    api-ms-win-crt-heap-l1-1-0.dll

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


          1. TimurZhoraev
            07.05.2026 15:07

            Что касается ГОСТ-а на язык коим является ISO/IEC на протяжении уже треть века или даже половины уже - то это гарантия что в энтерпрайзе есть что брать за основу не вдаваясь в подробности реализации - если соответствует - действуй. Это позволяет избежать vendor-lock или привязки к мыслям что будет со второй версией чего-то там. Комитет - это некая письменная захардкоженная гарантия результата. Разработчики же не могут дать такую бумагу. Только некие протоколы тестирования в частном порядке согласно каким-то там внутренним регламентам. Сегодня там есть этот прогер/архитектор, завтра женился, ушёл в Тибет, забил, стало душно, не интересно итд. Вот чтобы этого избежать и придуманы стандарты, фиксирующие в определённый момент времени то то необходимо. Горизонт планирования разработки без стандартов - вчера и сегодня. То есть для хобби и экспериментов. Поэтому Линус совершенно прав, пуская новые языки и фреймворки в ядро только на альтеративные функции, во всяком случае дубляж на стандартном языке имеется точно. Либо уже в комитете лежит драфт, но что-то сомнительно, хотя может и пришло время.


            1. black_warlock_iv
              07.05.2026 15:07

              То, к какому аду приводит стандарт хорошо видно на примере C++. Буквально на днях выяснилось, что контейнер, запланированный в новом стандарте, будет в 2 раза медленнее аналогичного контейнера в буст. А история с std::views::please_work, ой простите std::views::as_input, без которого std::views::filter тотально сломан? Это всё оттуда же идёт, от стандартизации и комитета.

              Стандартизация C++ — это не благо, а вынужденная мера из-за расплодившихся несовместимых реализаций. Rust идёт по пути референсного компилятора, это гораздо лучше. А к чему приводит искусственная стандартизация при наличии референсного компилятора известно на примере Haskell: всем плевать на стандарт и все пишут код конкретно для GHC.


              1. TimurZhoraev
                07.05.2026 15:07

                будет в 2 раза медленнее

                Это не проблема стандарта. Если он будет в 2 раза надёжнее, проще в восприятии, реализовывать необходимый функционал с заданной степенью защит, быстрее компилироваться, не иметь проблем с совместимостью - то пожалуйста.
                Допустим владелец энтерпрайза по претренингу LLM, берёт у трудящихся датасеты. Один прогон - это квартал непрерывных обсчётов и порядка 1.5 мегабаксов (есть даже техотчёт DeepSeek где это прямо указано на arXiv-е). И тут выбегают фреймворки и языки и говорят давайте мы ещё туда включим свои поделки, которые мы завтра вычеркнем если что пойдёт не так. Ну и что делать? Лучше взять датасеты подготовленные в соответствии с тем, что потенциально ещё будет использоваться 3-5+ лет, чем то, что могут сломать уже завтра до нуля. Потом будут все хаять модели, мол, они, дурные такие, не понимают мой запрос и генерируют глюки. RAG/MCP должен помогать шлифовать свежее а не реализовывать основной функционал. Причём каждая итерация претренинга для улучшения требует кратного увеличения затрат. Так что скорее всего сейчас подведут черту под всеми не стандартными фишками и закроют этот убегающий вагон.


            1. KiddingBanana
              07.05.2026 15:07

              Простите, но чем стандарт С++ принципиально отличается от editiions в rust? Стандарты С++ тоже несовместимы между собой - часть библитечного кода или семантики меняется. Есть множество историй, как больно происходит переход с условного С++14 на С++20.

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

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


              1. TimurZhoraev
                07.05.2026 15:07

                Проблемы исполнителей как говорится шерифа не интересуют. Стандарт - это то на что может опереться энтерпрайз принимая решения. Там до кода никакого дела нет. Особенно сейчас когда качественные, human made датасеты для претренингов очень дороги. Именно там основные человеко-часы. Официально, я могу запросить то, что editiions соответствуют каким-то фиксам специфиации? Вообще где спецификация на язык. Кто может поставить под ней подпись что этот компилер оттестирован, прошёл соответствующие тесты итд. Как я это могу применить в какой-нибудь серьёзный эмбед если будет ответ в виде "эксперт под ником FunAnonym15342 на нашей площадке провёл тест и показал 100% что всё работает, исходники выложил на гит и результаты тоже". Пока что сходу видно вот это и вот это. То есть это некая декларация, мануал по использованию, затравка для Bison/Flex в виде файла грамматики но никак не спецификация. Тем более с отсылкой на нормативы вроде ISO/IEC 25010 и прочая и прочая как здесь например. То есть там совершенно гигантский объём работ - и именно этот документ и есть язык программирования а не компилятор к нему, по сути инфраструктура Rust крутится вокруг него безотносительно к внешним ограничениям. Даже если есть опенсоурс компилятор всегда можно его протестировать на соответствие стандартам любой организацией и по результатам собственно принять работу. На основе чего тестировать Rust? На общих основаниях? Какую версию брать за основу. То есть в этом плане проще взять дуболомный но стандартный язык, чем то что поддерживается по сути группой энтузиастов на донатах, сегодня они есть завтра скажут - мы идём в LLM с ISO, и всё на этом.


          1. domix32
            07.05.2026 15:07

            Вот проявится у Rust второй компилятор тогда может и понадобится что-то вроде ГОСТ

            Ferrous System уже ведёт работу по стандартизации по крайней мере некоторого сабсета языка. Ну и стоило попросить человека посмотреть дату появления языков С и С++ и появления их официального междунардного стандарта.


        1. Quintanar
          07.05.2026 15:07

          Важно то, что дряхлые плюсы всё-таки имеют ISO/IEC 14882, это позволяет исключить бардак с инструментами и библиотеками. 

          Это шутка что-ли? Именно С++ хорошо известен тем, что там полный разброд во всех направлениях. Недавно видео вышло от опытного разработчика на полтора часа, где фактически только проблемы разброда и обсуждаются. Раст просто на два порядка круче в плане и инструментов и библиотек (не количества, а простоты работы).


          1. TimurZhoraev
            07.05.2026 15:07

            Это проблема тех кто синхронизирует доки компилятора со Стандартом. Сам Стандарт заданной практически исключает какой-либо разброд. Там проблема уже в культуре реализации с той и с другой стороны. Вообще говоря очень мало прогеров которые в принципе хотя бы по диагонали смотрели в Стандарт, обычно сразу во флажки компилера, всякие git-хаболабы с примерами, инструкции из форумов и прочие сугубо прикладные выкрутасы. Потом оказывается что документ описывает всё не так как кто-то когда-то прочитал в man или доках или скопипастил сосед. Компилятор, в котором есть соответствие нормативам обычно лишён каких-либо выкрутасов. UB это для каких-то совсем вопиющих случаев. Тем более что есть и gcc, и intel и расширения Nvidia nvcc и прочая и прочая - есть выбор, это кстати также преимущество. Rust может повторить судьбу Delphi на этой почве несмотря на Open Source в отличие от этой пропиретарки. Крайний и, скорее, последний стандарт Паскаля был в 1993м, это уже очень далеко.


  1. Dhwtj
    07.05.2026 15:07

    Rust также хорош для моделирования предметной области, наравне со scala. Но мне почему-то никто не верит.


  1. QtRoS
    07.05.2026 15:07

    В целом article получилась not bad, по сложности affordable для junior developers, хотя порой не хватает details. Думаю, Rust вполне может стать timeless, как C++


    1. d3d14
      07.05.2026 15:07

      Chromium Project писал, что около 70% high severity security bugs в Chromium были memory unsafety problems, то есть ошибками работы с C/C++ pointers. Еще один честный минус — learning curve.


  1. artden111
    07.05.2026 15:07

    Ну наконец-то нормальная статья про назначение rust, а не нейрослоп из рекламного булшита. Добавил в закладки, чтобы просто бросить ссылку, если кто-то начнёт у меня спрашивать для чего нужен раст
    PS: я бы дополнил статью ссылками на полезные проекты, которые написаны на раст


  1. vanxant
    07.05.2026 15:07

    В недостатки стОит добавить местами вырвиглазный синтаксис. Как будто кто-то взял нормальный текст и рандомно накидал в него случайных значков типа !, ?, &, ' и прочую mutь.

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

    В любом случае, в языке много банальной неконсистентности. Почему Result и Option пишутся целиком, а fn, mut и Err сокращённо? Зачем вообще нужно объявлять тип Result в качестве возвращаемого функцией? Это должно быть просто на уровне ABI...

    Всё вместе даёт такой запах гаражного поделия. Может и интересно, но тащить такое в прод и инвестировать своё время не хочется.


    1. Boneyan
      07.05.2026 15:07

      Как будто кто-то взял нормальный текст и рандомно накидал в него случайных значков типа !, ?, &, ’ и прочую mutь

      На это часто жалуются новички, но…это ведь не понятно только для тех кто язык не знает, если ты язнык знаешь, то это для тебя не “случайные символы”. Я разве что могу понять претензию к тому что символ легко глазами пропустить. Я бы например предпочёл использовать not как в python вместо !. Ну и непонятна ваша претензия к ! и & - они в куче языком используются точно в том же смысле что и в расте (для отрицания и для взятия ссылки)

      Зачем вообще нужно объявлять тип Result в качестве возвращаемого функцией?

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


      1. vanxant
        07.05.2026 15:07

        Про ! имелось ввиду println! - зачем выделять макросы отдельно кроме как для удобства разработчиков компиляторов? Это грубая ошибка дизайна.

        В большинстве языков обходятся без & для указания ссылок; в этом случае амперсанд можно было бы использовать вместо mut. Тоже косяк в пользу компилятора


        1. Boneyan
          07.05.2026 15:07

          зачем выделять макросы отдельно

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

          в этом случае амперсанд можно было бы использовать вместо mut

          Не понял ваш посыл. &x и &mut x это два разных типа ссылок. И это отдельные типы от x и mut x.


        1. TimurZhoraev
          07.05.2026 15:07

          да, всё верно, ! и не равно != сбивают без подсветки синтаксиса. Синтаксис должен помогать а не вносить неясности, тем более это то ради чего язык собственно и учат. Там не должно быть преимуществ реализации компилятора - в первую очередь - удобство для пользователя. Тому же Perl простительно всюду $ и @ с %, но сразу зато видно - где скаляр а где контейнер а где хеш. Синтаксически язык перегружен, причём, операторы слишком общие и неоднозначно трактуются. Конечно, синтаксис разработан так чтобы можно было сделать single line inliner в отличие от Питона например где табуляция с энтером редактором в принципе отображается как следующая строка. Но опять-таки, это чем-то должно быть лучше плюсов, шарпов, той же Javы или банального Питона. Функционально они имеют всё что нужно.


          1. AndreyDmitriev
            07.05.2026 15:07

            да, всё верно, ! и не равно != сбивают без подсветки синтаксиса.

            Ну тут можно выкрутиться, типа вместо

            let x = true;
            let y = !x;

            писать

            use std::ops::Not;
            
            let x = true;
            let y = x.not();

            И с != либо функцией-помощником, либо макросом, но в общем это меньшее, что в Расте беспокоит, дело вкуса.


    1. TimurZhoraev
      07.05.2026 15:07

      всё верно, язык выглядит просто как надстройка над неким сгенерированным по шаблону менеджером памяти и многопоточности. Подход был нормальный, когда Страуструп С++ изначально сделал как обёртку на K&R С, просто транслировав на этот язык синтаксис и далее запускал С компилятор. Но это 80-е прошлого века и получилось весьма удачно так как изменения гармонично вписались в С инфраструктуру.

      Достаточно посмотреть ключевой момент сюда. Делаем классическое AST и отправляем его... в gcc или в llvm через промежуточное представление MIR. По крайней мере на первый взгляд выглядит как-то так. Проблема safe решается кодогенерацией по шаблонам. По крайней мере похвально, то что язык компилирует сам себя, то есть является self-hosted. Однако... где в сырцах компилятора те самые фишки самого языка, которые отличают его не то что от С++ даже от С, визуально выглядит как Pascal обёрнутый вместо begin-end фигурными скобками с возможностью LISP прятать в [] и (). То есть информационная ёмкость как была так и осталась примерно одинаковая со "старым"и языками. Главное преимущество будет это safe (не очевидное в эпоху LLM) или какие-то хитрости из разряда regexp в Perl, которые часть самого языка и он идеален для обработки текста, но это ниша. Похоже что единственный стандарт это что то вроде Unsafe Extern Blocks RFC (RFC 3484), причём каждая такая внешняя заглушка может ломать любое преимущество. Вообщем кодогенерация по шаблонам не есть какое-то существенное превосходство в чём-то, особенно когда оно неуправляемо затягивает различные менеджеры не свойственные прямой компиляции. Напоминает helloworld.exe объёмом в 50 килобайт под DOS супротив helloworld.com в сотню байт. Тут есть свои + и -, но когда речь идёт о языке общего назначения, это должно всё быть под контролем и настраиваемым


    1. KiddingBanana
      07.05.2026 15:07

      Если сравнивать Rust с С++ (чьи проблемы он в основном и решает), то сравнение синтаксиса явно не в пользу конкурентов. Только за счет смены модификаторов по умолчанию (все const по умолчанию, move вместо копирования и т.д.) синтаксис становится проще и код читабельнее.

      Зачем вообще нужно объявлять тип Result в качестве возвращаемого функцией Потому что мы можем реализовать другой Result под задачу. Это часть библиотеки, а не прибито гвоздями


      1. vanxant
        07.05.2026 15:07

        Потому что мы можем реализовать другой Result под задачу. Это часть библиотеки, а не прибито гвоздями

        Это глупость и чисто С++way, где у вас на каждый чих есть 5 несовместимых способов реализации, начиная с указателей, массивов и строк. И всё это ради легаси и мифической ситуации, когда кто-то напишет свой супер-дупер аллокатор с перегрузкой операторов * и ,

        Конкретно возврат значения из функции это часть calling convention. И раз уж оно заведомо не совместимо с С (например, там внутре могут быть указатели со значением 7), почему бы не внести полноценный Result в этот самый cc. Тем более, в языке без исключений, но с возвратом ошибок, где он будет использоваться примерно всегда. Условно, постановить, что на амд64 результат (если есть) возвращается в RAX, ошибка (если есть) — в RDX или там REX. Любители написать свой Result идут развлекаться с диспетчеризацией, трейтами, несовместимостью со стандартной библиотекой куда-нибудь ещё, например в плюсы.


        1. AndreyDmitriev
          07.05.2026 15:07

          Конкретно возврат значения из функции это часть calling convention. И раз уж оно заведомо не совместимо с С (например, там внутре могут быть указатели со значением 7), почему бы не внести полноценный Result в этот самый cc. 

          Вообще это то, что мне очень нравится в Расте после LabVIEW. Там есть такое понятие как DataFlow, и виртуальные инструменты "нанизаны" на кластер ошибки (это такой design pattern), и вот в Расте оно примерно также устроено, типа:

          use anyhow::{Result, Context, anyhow};
          
          fn step1() -> Result<i32> {
              Ok(10)
          }
          
          fn step2(x: i32) -> Result<i32> {
              Ok(x + 5)
          }
          
          fn step3(x: i32) -> Result<i32> {
              if x > 20 {
                  Err(anyhow!("Value too big"))
              } else {
                  Ok(x * 2)
              }
          }
          
          fn workflow() -> Result<i32> {
              let a = step1().context("step1 failed")?;
              let b = step2(a).context("step2 failed")?;
              let c = step3(b).context("step3 failed")?;
              Ok(c)
          }
          
          fn main() {
              match workflow() {
                  Ok(_) => println!("Success"),
                  Err(e) => println!("Error: {:#}", e), // pretty print with chain
              }
          }

          Тут можно ещё "идиоматичнее" сделать, но даже так очень норм, как по мне, новичку.


          1. TimurZhoraev
            07.05.2026 15:07

            Для языка общего назначения такое нанизывание - ну это так себе, особенно если есть ограничения по стеку, плюс ещё что там происходит с типами данных на этой почве. Язык позиционирует себя safe, но это не должно быть дорогим удовольствием. Тем более не говоря уже о точках восстановления в таких исключениях. Для простейшей замены switch-case - пойдёт.


        1. black_warlock_iv
          07.05.2026 15:07

          Чем меньше магии, тем лучше. Тот же Паскаль не в последнюю очередь проиграл, потому что содержал функции с переменным числом аргументов, но не давал программисту возможность делать свои такие функции.

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


    1. domix32
      07.05.2026 15:07

      в него случайных значков типа !, ?, &, ' и прочую mutь.

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

      Почему Result и Option пишутся целиком, а fn, mut и Err сокращённо?

      ну fn и mut это ключевые слова и хорошо, что их не стали делать как в каком-нибудь JS с его function или Java c кучей public static final . А если опять же сравнивать с плюсами, то там как и в си есть и const enum и mutable explicit - тоже получается не очень консистентно и длинно. Ну а Result/Option и Err это названия классов/полей и называть можно как угодно. Учитывая что Err почти всегда с собой несёт какие-нибудь данные об ошибке не слишком полезная для этого сигнатура типа должна быть покороче имхо. Можно написать собственную библиотеку ошибок, по аналогии anyhow или thiserror, и назвать типы как вам хочется- хоть плюсами прикиньтесь с его std::expected и std::optional. А вообще вроде как опциональные типы калькированы из того же OCaml, на котором писались первые версии компилятора, и, скорее всего, они в таком виде оттуда перекочевали. Возвращаясь к миру си - есть же всякие errno и прочие - тоже сокращенные по целому ряду причин - надо перечислять?

      Всё вместе даёт такой запах гаражного поделия.

      да чтоб все такие поделия творили. Если это гаражные поделия, то поделия уровня макинтошей в гаражное время джобса. Вообще интересно с чем вы сравниваете это поделие, что оно вызывает такие ощущения.


      1. TimurZhoraev
        07.05.2026 15:07

        проблема в балансе семантики синтаксиса. public static final можно сделать PSF и далее перед компиляцией просто развёртывать это дело питоном (утрированно). Любое ключевое слово переключает стейт-машину компилятора - это упрощает реализацию языка, но ломает глаза разработчика. изобилие повторяющихся элементов вроде let особенно если есть правило назад (а-ля goto) довольно утомительно. С в этом плане более чем сбалансирован - повторяющиеся элементы по сути математика как и должно быть. Переменные объявил - и далее как угодно. Питон вообще где поставил табуляцию там и область видимости после двоеточия. То есть в Расте с этим какая-то недоработка, необходимо соблюдать определённый ритуал со скобками, а значит, должна быть Раст-совместимая IDE, которая генерит это удобство. Наподобие мучений в VDHL с этими самыми => в инстансах-entity элементов. Но там это простительно так как это другой класс языков и можно это нагенерить из схемы. Нет конечно можно if let Some(stmt) = self.basic_blocks[from].statements.first_mut() стрелять в пятку делая = вместе с == в if, но опять-таки синтаксис должен помогать а не вносить дополнительную нагрузку. Тем более _ и .. крайне неудобно читать - это или точки или пробел или ещё что. Когда пиксель был в полэкрана в разрешении 640 на 480 на 13'' дисплее это было супер, когда же мониторы почти уже 4к, нужно иметь микроскоп. На Питоне этот символ может быть любым, здесь же это синтаксис.


        1. domix32
          07.05.2026 15:07

          Раст-совместимая IDE

          Для этого собственно и придумали LSP, который прикручивается к любой IDE имеющей с ними работать. Поддержку языковых серверов и формата завезли ещё до релиза 1.0.

          С VHDL не работал, но по гугловому примеру напоминает работа со стримами в с++ а ля std::cout << "asd"; std::cin >> y; . Мозолит глаза, но вроде семантически понятно - в какую сторону стрелки, туда и текут данные.

          стрелять в пятку делая = вместе с ==

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

          let item = self
            .basic_blocks[from]
            .statements
            .first_mut();
          if let Some(el) = item { /*...*/ };

          И совершенно неважно было ли там обращение к членам или цепочка вызовов filter/map/fold.

          когда же мониторы почти уже 4к, нужно иметь микроскоп.

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


        1. AndreyDmitriev
          07.05.2026 15:07

          должна быть Раст-совместимая IDE

          Ну RustRover от JetBrains вроде норм. Ресурсов, конечно, жрёт как не в себя, и мог бы быть порезвее, но там много чего именно под Раст заточено (cargo и всё такое).


    1. 00Kirill00
      07.05.2026 15:07

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


      1. Astrowalk
        07.05.2026 15:07

        Ну да. Количество ASCII-символов ограничено, и любой богатый фичами язык так или иначе будет громоздить их. По сравнению с C++ или Перлом ещё всё неплохо.


    1. sdramare
      07.05.2026 15:07

      Потому что раст идёт от ветки языка ML, а java и C++ от B. То, что вы не знаете синтакс ML, ocaml, Haskell и прочее, это не проблема этих языков, а только вашего узкого кругозора.


  1. Apoheliy
    07.05.2026 15:07

    Позвольте несколько вопросов про rust (пытаюсь понять, стоит его изучать или ну-нафик? Сам пишу на Си и C++ и некоторые подходы языка вызывают вопросы):

    = Как-то по типу можно можно узнать, что он с владением или нет? Например, преобразуем пример из статьи в такой вид:

    let string1 = ...
    let string2 = string1;
    
        // println!("{}", string1); // ошибка компиляции

    в string1 присваивается тип (как результат функции или др.), который меняется от всего подряд: настроек компилятора, флагов, дефайнов и др. Например, присвоение в string1 он может быть в одном случае String, в другом i32. Можно ли заранее проверить в коде, будет ошибка компиляции на println или нет (типа sfinae в плюсах)? Или подмены типов (универсальные библиотеки и т.п.) так не работают?

    = Result<T, E> это гвоздями приколоченный синтаксис на уровне языка? Или можно создать свой SemiResult<T, S, E>, который присваивать Err("blah-blah"), Ok("good"), Semi("so-so") (и как-то указать, что Semi это ещё и автоматический выход из функции)? И в продолжение вопроса: про оператор ? - его функционал высечен в граните (на наличие второго типа выходим из функции) или можно как-то "подрихтовать"? Обработать несколько типов или выходить по наличию первого типа?

    = Есть ли офлайновый вариант работы, без подкачки всякого? Т.е. какой-то комплект компилятора/библиотек/др - собрали-установили, и оно НЕ МЕНЯЕТСЯ? В нашем продакшене очень желательна стабильность: 5 лет назад собрали приложение - выдали в работу (на некотором оборудовании/окружении); сейчас нашли багу в-том-самом-старом-коде; её исправили - и нужно собрать на том же самом окружении, что было 5 лет назад (и должно работать на том самом оборудовании/окружении);

    = Какое-то подобие пользовательского деструктора? Например, есть некая структура, к ней приколочен файл (открыт дескриптор). Хочется при разрушении структуры в файле дописывать данные перед закрытием (например в заголовке обновлять размер и контрольную сумму)? Смотрю примеры на rust и с деструкторами как-то не балуются.


    1. Boneyan
      07.05.2026 15:07

      Можно ли заранее проверить в коде, будет ошибка компиляции на println или нет

      Присваивание всегда передаёт владение. То есть владеет ресурсом тот, кому последнему его присвоили. Чтобы в приведённом примере владение не передавалось можно взять ссылку (что не передаёт владение) или сделать глубокую копию (что создаёт новый ресурс и берёт владение над ним).

      let string2 = &string1; // ссылка
      // ...
      let string2 = string1.clone(); // копия
      

      Или можно создать свой SemiResult<T, S, E>

      Да, можно. Result это такой же енам как все остальные, пользоваться им не обязательно. Разве что да, к нему приделан синтаксический сахар в виде ?. По сути

      fallible_call()?;
      

      это то же самое что

      if let Err(e) = fallible_call() {
        return Err(e.into());
      }
      

      можно рассматривать это так. То есть возвращает ошибку выше, с возможность конвертации в ваш тип ошибки (если вы такое реализовали).

      Есть ли офлайновый вариант работы, без подкачки всякого

      Библиотеки можно подключать локально по указанию пути, можно разместить в вашем приватном репозитории и указать ссылку - как вам удобнее. Со стабильностью всё ок.

      Какое-то подобие пользовательского деструктора?

      Есть. Для типа можно имплементировать трейт Drop. Как раз ваш пример с файлом можно сделать. На нём построено многое в стандартной библиотеке - освобождение памяти, мьютексов и тд https://doc.rust-lang.org/std/ops/trait.Drop.html


    1. AndreyDmitriev
      07.05.2026 15:07

      Ну по первым двум вопросам проще всего сослаться на третью/четвёртую и девятую главы The Rust Programming Language - там как раз про заимствование и владение и обработку ошибок. Вообще именно это "переломный" момент при переходе с Си на Раст, если его "прочувствовать", то потом всё относительно несложно.

      По поводу оффлайнового режима - есть cargo-vendor, как раз для этого — все зависимости будут сложены в ваш проект в папку vendor.

      А пользовательский деструктор - drop и RAII ваше всё.


    1. domix32
      07.05.2026 15:07

      Как-то по типу можно можно узнать, что он с владением или нет?

      В Rust по-умолчанию действует move семантика, а не как в плюсах copy by default. Аналогичным образом по-умолчанию переменные неизменяемые, а любые изменяемые работают по принципу "многие читают, один пишет". Всё проверяется на этапе компиляции - тот самый механизм проверки заимствований. Компилятор сам расскажет, где заимствоваание может приводить к проблемам и даже покажет места "откуда были удары".

      Result<T, E> это гвоздями приколоченный синтаксис на уровне языка?

      В стандартной библиотеке они прибиты. Но ничто не мешает написать собственный тип. Многие используют крейты thiserror и anyhow для более приятной работы с ошибками. Под капотом там уже настроена конвертация в нужные типы из стандартных. Вы также можете написать конвертацию через реализацию некоторых трейтов типа Into<T> или From<T> и работа будет довольно бесшовной. Ну а работа с таким типом там максимально приятная:

      match semi_result {
        Ok(msg) => println!("gratz, you got {msg}"),
        Semi(msg) => println!("better luck next time with {msg}"),
        Err(msg) => println!("well, shiiiiii. its {msg}"),
      };
      // ну или если прикрутите монадический интерфейс
      semi_result
      .then(|msg| println!("gratz, you got {msg}"))
      .well_then(|msg|println!("better luck next time with {msg}"))
      .map_err(|err|println!("well, shiiiiii. its {err}"));

      Есть ли офлайновый вариант работы, без подкачки всякого?

      если есть собственный registry то можно и с ним работать. Либо указывать для своих зависимостей собственные же репозитории где-нибудь на корпоративных серверах. По-умолчанию cargo смотрит в публичный регистри на crates.io. Есть lock файлы, позволяющие фиксировать версии зависимостей для герметичных сборок, есть инфраструктура для кэширования зависимостей, аудита и верификации этих зависимостей и много чего ещё связаанного с безопасностью и воспроизводимостью сборки. Обычно всё прячется за какой-нибудь одной командой в cargo. В плане CI/CD это просто огромная отдушина в сравнении с тем что есть у плюсов. Если за 5 лет фиксы не потребовали бампа версии компилятора, то проблем не возникнет. Собственно с какого-то момента ментейнеры крейтов внедрили политику MSRV - minimal supported rust version. То есть если версия компилятора позволяет использовать фичи конкретного крейта, то можно не обновлять версию. Обратная совместимость у Rust обещается существовать всегда вплоть до 1.0 версии.

      Какое-то подобие пользовательского деструктора

      под капотом компилятор дешугарит уничтожение объекта, как вызов obj.drop(), так что если имплементировать трейт Drop, то поведение можно расширять. Аналогичная история с перегрузкой операторов да и вообще любым поведением - имплементация соотвествующих трейтов. Не балуются обычно за ненадобностью - идеология "make invalid states unrepresentable" обычно позволяет не заниматься подобными извращениями. Для примеров можно посмотреть как например происходит работа с файловыми дескрипторами или сокетами - там обычно приходится их доимплементировать.


    1. Jijiki
      07.05.2026 15:07

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

      можно определить свою сигнатуру обработки ошибок, про ? не смотрел, но в документации наверняка о нём есть описания.

      по поводу офлайна, скачиваю rustup, в данный момент у меня окно, математика и gl с минимальными зависимостями(без зависимостей в прямом понимании слова зависимости, там зависимости только под биндген), они оформлены библиотеками lib.rs, чтобы избавиться от bindgen вам придётся писать этот функционал с нуля, если у вас задача консервация и цель только компилятор.

      пользовательский деструктор это определение

      pub fn calc() {
          let test_line: Vec<&str> = "1 / 20".trim().split_whitespace().collect();
          let mut count = 0; // 1 2 3 4
          for (i, o) in test_line.iter().enumerate() {
              if *o == "+" || *o == "-" || *o == "*" || *o == "/" {
                  if *o == "+" {
                      count = 1;
                  } else if *o == "-" {
                      count = 2;
                  } else if *o == "*" {
                      count = 3;
                  } else if *o == "/" {
                      count = 4;
                  }
                  let temp_res1 = &test_line[0..i].to_vec();
                  let temp_res2 = &test_line[i + 1..].to_vec();
                  let op1 = temp_res1.get(0).expect("l").parse::<f32>().expect("msg");
                  let op2 = temp_res2.get(0).expect("l").parse::<f32>().expect("msg");
                  let res: f32 = match count {
                      1 => op1 + op2,
                      2 => op1 - op2,
                      3 => op1 * op2,
                      4 => op1 / op2,
                      _ => -1.0,
                  };
                  println!("{:?} {:?} {:?}", op1, op2, res);
              }
          }
      }
      ...
      struct MyError {
          mes: String,
          code: i32,
      }
      impl fmt::Display for MyError {
          fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
              write!(f, "Error {}: {}", self.code, self.mes)
          }
      }
      fn do_something() -> Result<(), MyError> {
          Err(MyError {
              mes: String::from("Something went wrong"),
              code: "anynumber",
          })
      }
      
      ...
      impl<'a> Drop for Cube<'a> {
          fn drop(&mut self) {
              self.gl.delete_vertex_arrays(1, &self.vao);
              self.gl.delete_buffers(1, &self.vbo);
              self.gl.delete_buffers(1, &self.ebo);
              self.gl.delete_textures(1, &self.texture);
          }
      }
      ...
      impl Drop for Object {
          fn drop(&mut self) {
            дропаем владения структуры под именем Object
          }
      }
      ...
      предположим у нас есть такое
      #[derive(Debug, Clone)]
      pub struct GlWindow<'a> {
          display: &'a XDisplay,
          window: sys::Window,
          gl_context: sys::GLXContext,
          colormap: sys::Colormap,
      }
      
      тогда чтобы была цепочка вызовов над текущим владением
      pub trait GlDebugExt: Sized {
          fn display_version_debug(self) -> Self;
      }
      
      impl GlDebugExt for GlWindow<'_> {
          fn display_version_debug(self) -> Self {
              unsafe {
                  // Используем стандартные вызовы OpenGL через ваши биндинги
                  let version = sys::glGetString(sys::GL_VERSION);
                  let renderer = sys::glGetString(sys::GL_RENDERER);
                  let vendor = sys::glGetString(sys::GL_VENDOR);
      
                  if !version.is_null() {
                      let v_str = std::ffi::CStr::from_ptr(version as *const i8);
                      let r_str = std::ffi::CStr::from_ptr(renderer as *const i8);
                      let ven_str = std::ffi::CStr::from_ptr(vendor as *const i8);
      
                      println!("--- OpenGL Debug Info ---");
                      println!("Vendor:   {}", ven_str.to_string_lossy());
                      println!("Renderer: {}", r_str.to_string_lossy());
                      println!("Version:  {}", v_str.to_string_lossy());
                      println!("-------------------------");
                  }
              }
              // Возвращаем объект обратно, чтобы цепочка не прервалась
              self
          }
      }
      ...
      и можно будет вызвать через цепочку 
      в момент создания по цепочке дебаг вывод
              let window = GlWindow::new(аргументы)
                  .expect("Не удалось открыть window")
                  .display_version_debug();

      temp.md вот задачки потыкайте, тут по нарастающей в целом изи и изящно выходит


  1. AndroDaPooh
    07.05.2026 15:07

    Rust помогает писать многопоточный код безопаснее.

    А можно подробнее? Автоматизация atomic семафоров?

    Как человек пишущий на С/С++ 11 я честно не вижу смысла в Rust по крайней мере потому что в С++11 (умные указатели + почти любой стат анализатор) ловит все перечисленные в статье проблемы. Единственная проблема у С++, лично у меня - это что каждый новый стандарт добавляет ряд вещей которые иногда радикально усложняют понимание кода ради экономии строчек. Еще иногда попытки обмануть компилятор "С" ради ускорения оборачиваются большой потерей времени из-за того, что разработчики компилятора уже придумали как ускорить исходный честный вариант.


    1. AndreyDmitriev
      07.05.2026 15:07

      Ну наверное если совсем примитивно и "в лоб", то вот если вы на С++ напрограммируете состояние гонки, типа такого, то компилятор даже не чихнёт:

      #include <thread>
      
      int counter = 0;
      
      void increment() {
          for (int i = 0; i < 1000; i++) {
              counter++;
          }
      }
      
      int main() {
          std::thread t1(increment);
          std::thread t2(increment);
      
          t1.join();
          t2.join();
      
          std::cout << "Counter: " << counter << std::endl;
          return 0;
      }

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

      use std::thread;
      
      static mut counter:i32 = 0;
      
      fn increment() {
          for _ in 0..1000 {
              counter += 1;
          }
      }
      
      fn main() {
      
          let t1 = thread::spawn(|| {
              increment();
          });
      
          let t2 = thread::spawn(|| {
              increment();
          });
      
          t1.join().unwrap();
          t2.join().unwrap();
      
          println!("Сounter: {}", counter );
      }

      и он заставит вас вкорячить unsafe, и тогда вы сами себе злобный Буратино, либо использовать мьютекс и лок, как положено


    1. il4enkodev
      07.05.2026 15:07

      В Rust mutex - контейнер:

      let data: Mutex<i32> = Mutex::new(0);
      let mut num: MutexGuard<i32> = data.lock().unwrap();
      // компилятор не позволит сохранить ссылку на i32, так чтобы
      // она пережила Mutex или MutexGuard
      // MutextGuard - смартпоинтер, при выходе со скоупа автоматически снимает лок

      Также система типов позволяет описать "потокобезопасность" типов:

      // многопоточный оператор for_each на итераторе принимает замыкание, которое  
      // обязано захватывать переменные с окружения исключительно по 
      // "константной ссылке", а также может быть безопасно смувлено в другой поток  
      // или безопасно переданно по ссылке в другой поток
      // все эти инварианты проверяет компилятор и все это описано в сигнатуре ф-ции
      fn for_each(&mut self, closure: F) where F: Fn(Self::Item) + Sync + Send {
        // ....
      }


    1. domix32
      07.05.2026 15:07

      очти любой стат анализатор

      из open source, то бишь бесплатных у плюсов считай только cppcheck и есть. За остальные надо башлять. Прочая инфраструктура работает только как DAST (asan, tsan, ubsan и прочие), то есть нужно писать очень много тестов для покрытия, чтобы действительно отлавливать то, что ржавый компилятор разрешает на этапе компиляции. Новые фишки языка - типа концептов и контрактов - конечно помогут, но ещё не скоро ибо их вот только-только стандартизировали и толком ещё не приняли на вооружение. Но это и стандарт надо до C++20 поднимать как минимум. Для вариантов use-after-free или double-free статических решений не существует в принципе. Я конечно видел как чуваки пытались Lifetime на шаблонах скрафтить, но пользоваться этим крайне неудобно, да и идея была скорее для хаба "ненормальное программирование", нежели как production ready решение.

      А можно подробнее? Автоматизация atomic семафоров?

      скорее расскажет, где вы мьютекс забыли на пошаренных данных взвести. А если оно не пошаренное как надо - но использованное в другом потоке то и в это укажет мол заверните в mutex или arc.

      Еще иногда попытки обмануть компилятор "С" ради ускорения оборачиваются большой потерей времени из-за того

      как-то так и ждём нормальных модулей в С++. Тем временем у Rust они уже из коробки и без горожения килобайтных описаний порядка компиляции оных в Cmake.


  1. 00Kirill00
    07.05.2026 15:07

    Очередная статья о том, как раст спасет мир, вылечит рак и заставит Маска написать идеальный мессенджер

    Жаль только что компилироваться весь этот праздник будет до второго пришествия)


    1. artden111
      07.05.2026 15:07

      Нет. Статья на удивление удачная и хорошая. Советую прочитать её дальше заголовка