Требование реализации 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`
Что это значит? Я выбрал неправильную черту? Как я могу это решить?
Ответы
Ответ 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 не так хороша, как хотелось бы.