Как стать автором
Поиск
Написать публикацию
Обновить
7
0
Роман @rsk

Пользователь

Отправить сообщение

Инвертирование порядка сортировки в Rust

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

struct Note {
    imp: bool,
    title: &'static str,
}
let mut notes = vec![
    // ...
];
notes.sort_by_key(|k| k.imp);

Но инвертирование порядка сортировки с помощью ключа может быть не так очевидно, оно достигается с помощью вспомогательной структуры Reverse, вот как ее можно применить к приведенному выше примеру, чтобы важные заметки были в начале:

use std::cmp::Reverse;
// ...
notes.sort_by_key(|k| Reverse(k.imp));

Как это реализовано? Давайте взглянем на исходный код стандартной библиотеки Rust:

pub struct Reverse<T>(pub T);

impl<T: Ord> Ord for Reverse<T> {
    fn cmp(&self, other: &Reverse<T>) -> Ordering {
        other.0.cmp(&self.0)
    }
}

Все просто - это тип обертка (newtype) для исходного типа, и для типов с трейтом Ord она переопределяет Ord, но вместо сравнения self с other она сравнивает other с self, что означает - порядок в результате меняется на обратный. Просто и красиво!

Теги:
Рейтинг0
Комментарии5

Компактный match в Rust

match часто используется для работы с енумами, посмотрим на такой пример:

enum NextStep {
    TurnLeft,
    TurnRight
}

fn main() {
    let next_step = NextStep::TurnLeft;
    match next_step {
        NextStep::TurnLeft => println!("turn left"),
        NextStep::TurnRight => println!("turn right"),
    }
}

Это может быть не очевидно, но енумы в Rust можно принести в текущее пространство имен, это позволяет сделать код более компактным:

// ...
    use NextStep::*;
    match next_step {
        TurnLeft => println!("turn left"),
        TurnRight => println!("turn right"),
    }
// ...

Ну, а чтобы ограничить эффект только одним match, придется задать границы используя {} для дополнительного блока:

// ...
    {
        use NextStep::*;
        match next_step {
            // ...
        }
    }
// ...
Теги:
Всего голосов 4: ↑3 и ↓1+2
Комментарии3

Полезный антипаттерн в Rust

В Rust нередко используются типы обертки (newtype), которые помогают строже контролировать обрабатываемые данные.

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

struct Admins(Vec<u128>);

Теперь можно использовать этот тип:

fn print_admin_count(users: Admins) {
    println!("admins count: {}", users.0.len());
}

Это работает, но выглядит уже не так приятно, как если бы мы просто использовали массив users.len().

В Rust существуют типажи (traits) Deref/DerefMut, которые решают эту проблему, если мы реализуем их для типа обертки, то это позволит работать с внутренним типом напрямую:

impl Deref for Admins{
    //...
}

fn print_admin_count(users: Admins) {
    println!("admins count: {}", users.len());
}

Полный сниппет

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

Всего голосов 1: ↑1 и ↓0+1
Комментарии0

в Rust есть метод transpose, иногда бывает полезен, особенно хорошо работает с anyhow:

use std::str::FromStr;
use anyhow::Context;

fn add(a: Option<&str>, b: Option<&str>) -> anyhow::Result<f32> {
    let a = a.map(f32::from_str).transpose()?.context("not a value")?;
    let b = b.map(f32::from_str).transpose()?.context("not a value")?;
    Ok(a + b)
}

fn main() {
    println!("{:?}", add(Some("12"), Some("1.2")));
    println!("{:?}", add(Some("a"), Some("1.2")));
    println!("{:?}", add(None, Some("1.2")));
}

вывод:

Ok(13.2)
Err(invalid float literal)
Err(not a value)
Всего голосов 2: ↑2 и ↓0+2
Комментарии0

Информация

В рейтинге
Не участвует
Откуда
Россия
Зарегистрирован
Активность