Подтвердить что ты не робот

Требование реализации Mul в общей функции

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

use std::ops::Mul;

fn cube<T: Mul>(x: T) -> T {
    x * x * x
}

fn main() {
    println!("5^3 = {}", cube(5));
}

При компиляции я получаю эту ошибку:

error[E0369]: binary operation `*` cannot be applied to type `<T as std::ops::Mul>::Output`
 --> src/main.rs:4:5
  |
4 |     x * x * x
  |     ^^^^^^^^^
  |
  = note: an implementation of `std::ops::Mul` might be missing for `<T as std::ops::Mul>::Output`

Что это значит? Я выбрал неправильную черту? Как я могу это решить?

4b9b3361

Ответ 1

Немного сломайте свой пример:

fn cube<T: Mul>(x: T) -> T {
    let a = x * x;
    let b = a * x;
    b
}

Каковы типы a и b? В этом случае тип a - <T as std::ops::Mul>::Output - звучит знакомо из сообщения об ошибке? Затем мы пытаемся снова умножить этот тип на x, но нет гарантии, что Output может быть умножено на что-либо!

Сделайте простейшую вещь и скажем, что T * T должно привести к T:

fn cube<T: Mul<Output = T>>(x: T) -> T {
    x * x * x
}

К сожалению, это дает две аналогичные ошибки:

error[E0382]: use of moved value: `x`
 --> src/lib.rs:6:9
  |
6 |     x * x * x
  |     -   ^ value used here after move
  |     |
  |     value moved here
  |
  = note: move occurs because `x` has type `T`, which does not implement the `Copy` trait

Это потому, что черта Mul принимает аргументы по значению, поэтому мы добавляем Copy, чтобы мы могли дублировать значения.

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

fn cube<T>(x: T) -> T
where
    T: Mul<Output = T> + Copy
{
    x * x * x
}

Ответ 2

Оценка T: Mul не означает, что результат двоичного оператора также имеет тип T. Тип результата - связанный тип этого признака: Output.

Другая проблема заключается в том, что в некоторый момент времени признаки оператора переходят из pass-by-referece в pass-by-value. В общем коде это может быть немного боли в прикладе (по крайней мере сейчас), потому что эти операторы потребляют свои операнды, если вы также не требуете, чтобы типы были Copy.

Просто для полноты (если вам не нравится Copy), позвольте мне добавить некоторую информацию о возможном альтернативном направлении.

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

 f64 implements Mul< f64>
 f64 implements Mul<&f64>
&f64 implements Mul< f64>
&f64 implements Mul<&f64>

причем каждая реализация имеет f64 как Output. Но использование этих признаков напрямую не очень хорошо:

fn cube<T>(x: &T) -> T
    where for<'a> T: Mul<&'a T, Output = T>,
          for<'a,'b> &'a T: Mul<&'b T, Output = T>
{
    x * x * x
}

В конце концов, мы можем получить некоторые (слегка) черты более высокого уровня, что уменьшит шум. Например: T: Mul2 может означать T: Mul<T> + Mul<&T> и &T: Mul<T> + Mul<&T>. Но на момент написания этого компилятор Rust, похоже, не справился с этим. По крайней мере, я не смог скомпилировать следующий код:

use std::ops::Mul;

pub trait Mul2 where
    Self: Mul<Self, Output=Self>,
    Self: for<'a> Mul<&'a Self, Output=Self>,
    for<'a> &'a Self: Mul<Self, Output=Self>,
    for<'a,'b> &'a Self: Mul<&'b Self, Output=Self> {}

impl<T> Mul2 for T where
    T: Mul<T, Output=T>,
    T: for<'a> Mul<&'a T, Output=T>,
    for<'a> &'a T: Mul<T, Output=T>,
    for<'a,'b> &'a T: Mul<&'b T, Output=T> {}

fn cube<T: Mul2>(x: &T) -> T {
    x * x * x
}

fn main() {
    let c = cube(&2.3);
    println!("Hello, world! {}", c)
}

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