Как объявить функцию, которая принимает три разных типа generi c и возвращает сумму квадратов двух больших чисел? - PullRequest
1 голос
/ 21 января 2020

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

fn e3(n1: f32, n2: f32, n3: f32) -> f32 {
    match (n1 <= n2, n1 <= n3, n2 <= n1, n2 <= n3) {
        (true, true, _, _) => n2 * n2 + n3 * n3,
        (_, _, true, true) => n1 * n1 + n3 * n3,
        _ => n1 * n1 + n2 * n2,
    }
}

Однако это не будет работать с несколькими типами (например, i32, f32 или * 1006). *).

Для реализации обобщенной функции c я добавил следующие ограничения типа:

fn g_e3<T, U, V, R>(n1: T, n2: U, n3: V) -> R
where
    T: std::cmp::PartialOrd + std::ops::Mul + std::convert::From<U> + std::convert::From<V>,
    U: std::cmp::PartialOrd + std::ops::Mul + std::convert::From<T> + std::convert::From<V>,
    V: std::cmp::PartialOrd + std::ops::Mul + std::convert::From<T> + std::convert::From<U>,
    <T as std::ops::Mul>::Output: std::ops::Add,
    <U as std::ops::Mul>::Output: std::ops::Add,
{
    match (
        n1 <= n2.into(),
        n1 <= n3.into(),
        n2 <= n1.into(),
        n2 <= n3.into(),
    ) {
        (true, true, _, _) => n2 * n2 + n3 * n3,
        (_, _, true, true) => n1 * n1 + n3 * n3,
        _ => n1 * n1 + n2 * n2,
    }
}

Однако, чтобы описать тип результата R Мне нужно добавить R: <<U as std::ops::Mul>::Output as std::ops::Add>::Output, что недопустимо .

Чтобы обойти жесткость типов, я попытался определить макрос:

macro_rules! ge3 {
    ($n1:expr, $n2:expr, $n3:expr) => {
        {
            match ($n1 <= $n2.into(), $n1 <= $n3.into(), $n2 <= $n1.into(), $n2 <= $n3.into()) {
                (true, true, _, _) => $n2 * $n2 + ($n3 * $n3).into(),
                (_, _, true, true) => $n1 * $n1 + ($n3 * $n3).into(),
                _ => $n1 * $n1 + ($n2 * $n2).into(),
            }
        }
    };
}

Однако в Rust существует понятие тривиального (as) и нетривиального приведения (into). , Примитивное приведение не будет работать с into, вам нужно использовать as. Однако я не могу найти способ найти type определенного expr в макросе.

1 Ответ

3 голосов
/ 21 января 2020

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

Основная проблема вашего интерфейса, с которой вы сталкиваетесь, заключается в том, что черта Into не является коммутативной. Вы можете разыграть u8 в u32, но не наоборот. Лучше предварительно определить общий тип цели, сначала привести все целые числа к этому, а затем продолжить вычисление с этим типом. Более того, ваш пользователь может выбрать больший тип, потому что сумма в квадрате может переполниться даже самым большим предоставленным типом ввода.

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

fn squared_sum<Res, N1, N2, N3>(n1: N1, n2: N2, n3: N3) -> Res
where
    N1: std::convert::Into<Res>,
    N2: std::convert::Into<Res>,
    N3: std::convert::Into<Res>,
    Res: std::cmp::PartialOrd + std::ops::Mul<Output = Res> + std::ops::Add<Output = Res> + Copy,
{
    let n1: Res = n1.into();
    let n2: Res = n2.into();
    let n3: Res = n3.into();
    match (n1 <= n2, n1 <= n3, n2 <= n1, n2 <= n3) {
        (true, true, _, _) => n2 * n2 + n3 * n3,
        (_, _, true, true) => n1 * n1 + n3 * n3,
        _ => n1 * n1 + n2 * n2,
    }
}

fn main() {
    let result: i32 = squared_sum(8_u8, 10_u16, 10_i32);
    println!("{}", result);
}

Обратите внимание, что Вы можете вообще отказаться от N1, N2, N3 и привести аргументы n1: impl Into<Res>.

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