Как мне требовать, чтобы универсальный тип реализовал такую ​​операцию, как Add, Sub, Mul или Div в общей функции?

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

 use std::ops::Mul; fn cube(x: T) -> T { x * x * x } fn main() { println!("5^3 = {}", cube(5)); } 

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

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

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

Давайте немного разложим ваш пример:

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

Каковы типы a и b ? В этом случае тип a есть ::Output – звучит знакомо из сообщения об ошибке? Затем мы пытаемся снова умножить этот тип на x , но нет гарантии, что Output может быть умножен на что угодно!

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

 fn cube>(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(x: T) -> T where T: Mul + Copy { x * x * x } 

Оценка 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(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 + Mul<&T> и &T: Mul + Mul<&T> . Но на момент написания этого компилятор Rust, похоже, не справился с этим. По крайней мере, я не смог скомпилировать следующий код:

 use std::ops::Mul; pub trait Mul2 where Self: Mul, Self: for<'a> Mul<&'a Self, Output=Self>, for<'a> &'a Self: Mul, for<'a,'b> &'a Self: Mul<&'b Self, Output=Self> {} impl Mul2 for T where T: Mul, T: for<'a> Mul<&'a T, Output=T>, for<'a> &'a T: Mul, for<'a,'b> &'a T: Mul<&'b T, Output=T> {} fn cube(x: &T) -> T { x * x * x } fn main() { let c = cube(&2.3); println!("Hello, world! {}", c) } 

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

  • Когда реализуются блокировки Fn, FnMut и FnOnce?
  • Что делает что-то «признаком объекта»?
  • Есть ли способ подсчета с помощью макросов?
  • Как получить срез в виде массива в Rust?
  • не может считать `self.x` неизменным, потому что` * self` также заимствован как изменяемый
  • Почему моя переменная не живет достаточно долго?
  • Как реализовать черту, которой я не владею, для типа, которым я не владею?
  • Пожизненные проблемы, связанные ссылками между streamами
  • Почему нужны явные времена жизни в Rust?
  • Как использовать литералы целочисленного числа при использовании общих типов?
  • Итератор возвращает предметы по ссылке, срок службы
  • Давайте будем гением компьютера.