Каков идиоматический способ необязательной нормализации значений, когда преобразование может потерпеть неудачу в Rust? - PullRequest
2 голосов
/ 14 июня 2019

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

  • Оба i64, оставьте в покое
  • Оба f64, оставь в покое
  • Один - i64, один - f64, измените i64 на f64
  • Другие правила для других типов

Например:

fn normalize(arg1: Option<MyValue>, arg2: Option<MyValue>) 
    -> (Option<MyValue>, Option<MyValue>) {
  ... do stuff...
}

Мой пример вернет кортеж с необязательно преобразованными значениями. MyValue не реализует Copy, но реализует Clone. Это перечисление, которое может содержать целое число, рациональное число или строку и т. Д.

Альтернативы, которые я вижу:

  1. Верните кортеж. Преобразуйте значение, которое нужно изменить, клонируйте другое, чтобы избежать проверки заимствования. Ошибки преобразования возвращаются как None.
  2. Вернуть кортеж. Преобразуйте значение, которое нужно изменить, верните другое без изменений, выясните, как переместить значение.
  3. Возврат (). Сделайте параметры &mut. Измените тот, который нужно изменить, если таковой имеется.
  4. Другой способ, о котором я не знаю, потому что я новичок в Rust.

Какой подход наиболее идиоматичен для Rust? Если я не клонирую, как мне записать подпись, чтобы успокоить заемщика?

Мое настоящее перечисление:

#[derive(Clone, PartialEq, Debug)]
pub enum ShyScalar {
    Boolean(bool),
    Integer(i64),
    Rational(f64),
    String(String),
    Error(String)
}

1 Ответ

0 голосов
/ 15 июня 2019

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

trait MapType {
    type Output;
}

impl MapType for f64 {
    type Output i64;
}

impl MapType for bool {
    type Output u8;
}

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

В вашем случае речь идет о парахтипов, и вы можете расширить идею выше, добавив параметр:

trait Normalize<T>: Sized {
    type Norm;
}

Каждая реализация Normalize создает уникальный тип Norm для комбинации двух типов, Self и T.

Но вам также понадобятся некоторые ограничения;в конце концов, вам нужно будет иметь возможность фактически конвертировать между этими типами.И если число слишком велико для преобразования, преобразование завершится неудачей, поэтому вам понадобятся дополнительные ограничения типов TryFrom и TryInto, чтобы указать, какие вещи можно преобразовать во что:

use std::convert::{TryFrom, TryInto};

trait Normalize<T>: Sized
where
    T: TryInto<Self::Norm>,
{
    type Norm: TryFrom<Self>;
}

Реализуйте его для пар типов, которые вы хотите:

impl Normalize<u32> for f64 {
    type Norm = f64;
}

impl Normalize<f64> for u32 {
    type Norm = f64;
}

А также для всех пар того же типа :

impl<X> Normalize<X> for X {
    type Norm = X;
}

Затем можно реализоватьnormalize вот так:

fn normalize<A, B>(arg1: Option<A>, arg2: Option<B>) -> (Option<A::Norm>, Option<A::Norm>) 
where
    A: Normalize<B>,
    A::Norm: TryFrom<B>,
{
    (
        arg1.and_then(|a| a.try_into().ok()),
        arg2.and_then(|b| b.try_into().ok())
    )
}

fn main() {
    println!("{:?}", normalize(Some(1u32), Some(1u32))); // (Some(1), Some(1))
    println!("{:?}", normalize(Some(1f64), Some(1u32))); // (Some(1.0), Some(1.0))
    println!("{:?}", normalize(Some(1u32), Some(1f64))); // (Some(1.0), Some(1.0))
    println!("{:?}", normalize(Some(1f64), Some(1f64))); // (Some(1.0), Some(1.0))
}

Вы быстро поймете, что я немного обманул здесь, используя u32 вместо u64.Это потому, что нет реализации TryFrom<u64> для f64.Если вам нужны эти пары типов, вы все равно можете придерживаться того же подхода, который я описал, но вам нужно будет определить собственную версию черт TryInto и TryFrom и реализовать их самостоятельно для всех пар типов, которые вы используете.необходимо.

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