Rust — довольно большой и сложный язык программирования с множеством функций. Но у меня есть хорошие новости: менее 20% функций принесут вам более 80% результатов.

Вот функции, которые я считаю необходимым изучить при запуске Rust.

Готовы погрузиться? 🦀

Хотите узнать больше о Rust, прикладной криптографии и безопасности? Взгляните на мою книгу Черная шляпа ржавчины.
Получите скидку 42% до Четверг, 11 ноября с купоном 1311B892


перечисления

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

enum Result<T, E> {
   Ok(T),
   Err(E),
}

pub enum Option<T> {
    None,
    Some(T),
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

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

#[derive(Debug, Clone, Copy)]
enum Platform {
    Linux,
    MacOS,
    Windows,
    Unknown,
}

impl fmt::Display for Platform {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Platform::Linux => write!(f, "Linux"),
            Platform::Macos => write!(f, "macOS"),
            // Compile time error! We forgot Windows and Unknown
        }
    }
}
Войти в полноэкранный режим

Выйти из полноэкранного режима


асинхронное ожидание

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

Это проблемы, решаемые async-awaitи в то же время обеспечивает отличный опыт разработчика.

Вы можете узнать больше о том, как async-await работает в моих предыдущих постах: Асинхронный Rust: совместное планирование и упреждающее планирование и Асинхронный Rust: что такое среда выполнения? Вот как работает Токио под капотом.


Черты

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

Например, драйвер хранилища:

struct FilesystemStorage {
  get() // ...
  put() // ...
  delete() // ...
}

struct S3Storage {
  get() // ...
  put() // ...
  delete() // ...
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

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

trait Storage {
  get() // ...
  put() // ...
  delete() // ...
}

impl Storage for FilesystemStorage {
  // ...
}

impl Storage for S3Storage {
  // ...
}


fn use_storage<S: Storage>(storage: S) {
  // ...
}
Войти в полноэкранный режим

Выйти из полноэкранного режима


Умные указатели

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

Они также являются основой черты объектов которые позволяют вам выбрать правильную реализацию во время выполнения (вместо времени компиляции с дженериками).

struct MyService {
  db: Arc<DB>,
  mailer: Arc<dyn drivers::Mailer>,
  storage: Arc<dyn drivers::Storage>,
  other_service: Arc<other::Service>,
}
Войти в полноэкранный режим

Выйти из полноэкранного режима


Коллекции

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

let dedup_subdomains: HashSet<String> = subdomains.into_iter().collect();
Войти в полноэкранный режим

Выйти из полноэкранного режима


Итераторы

Ан Итератор — это объект, который позволяет разработчикам перемещаться по коллекциям. Их можно получить из большинства коллекций стандартной библиотеки.

fn filter() {
    let v = vec![-1, 2, -3, 4, 5].into_iter();

    let _positive_numbers: Vec<i32> = v.filter(|x: &i32| x.is_positive()).collect();
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Итераторы ленивы: они ничего не сделают, если их не использовать.


Комбинаторы

Комбинаторы — очень интересная тема. Почти все определения, которые вы найдете в Интернете, взорвут вашу голову 🤯, потому что они вызывают больше вопросов, чем дают ответов.

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

let sum: u64 = vec![1, 2, 3].into_iter().map(|x| x * x).sum();
Войти в полноэкранный режим

Выйти из полноэкранного режима

Вот еще примеры:

// Convert a `Result` to an `Option`
fn result_ok() {
    let _port: Option<String> = std::env::var("PORT").ok();
}

// Use a default `Result` if `Result` is `Err`
fn result_or() {
    let _port: Result<String, std::env::VarError> =
        std::env::var("PORT").or(Ok(String::from("8080")));
}

// Use a default value if empty, then apply a function
let http_port = std::env::var("PORT")
    .map_or(Ok(String::from("8080")), |env_val| env_val.parse::<u16>())?;

// Chain a function if `Result` is `Ok` or a different function if `Result` is `Err`
let master_key = std::env::var("MASTER_KEY")
    .map_err(|_| env_not_found("MASTER_KEY"))
    .map(base64::decode)??;
Войти в полноэкранный режим

Выйти из полноэкранного режима


Потоки

Потоки можно грубо определить как итераторы для async Мир.

Их следует использовать, когда вы хотите применить асинхронные операции к последовательности элементов одного типа, будь то сетевой сокет, файл или долгоживущий HTTP-запрос.

Все, что слишком велико, чтобы поместиться в памяти и, следовательно, должно быть разделено на более мелкие фрагменты, или что может появиться позже, но мы не знаем, когда, или это просто коллекция (коллекция Vec или HashMap например) к которому нам нужно применить async операции до.

Они также позволяют нам легко выполнять операции одновременно:

async fn compute_job(job: i64) -> i64 {
  // ...
}

#[tokio::main]
async fn main() {
    let jobs = 0..100;
    let concurrency = 42;

    stream::iter(jobs)
        .for_each_concurrent(concurrency, |job| compute_job(job)).await;
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Вы можете узнать больше об использовании потоков в качестве рабочих пулов в моем предыдущем посте: Как реализовать рабочие пулы в Rust.


no_std

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

Для этих случаев использования мы используем #![no_std] атрибут:

#![no_std]
#![no_main]

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
fn _start() {
  // ...
}
Войти в полноэкранный режим

Выйти из полноэкранного режима

Хотите узнать больше о Rust, прикладной криптографии и безопасности? Взгляните на мою книгу Черная шляпа ржавчины.
Получите скидку 42% до Четверг, 11 ноября с купоном 1311B892