Приведение к универсальному типу
У меня есть вопрос новичка о дженериках в Rust (версия 1.0).
Допустим, я пишу обобщенную функцию для деления. Не берите в голову полезность такой функции; Это простая функция, чтобы держать этот вопрос простым.
fn divide<T: std::ops::Div>(a: T, b: T) -> T {
a / b
}
fn main() {
println!("{}", divide(42, 18))
}
Эта программа не компилируется.
src/main.rs:2:5: 2:10 error: mismatched types:
expected `T`,
found `<T as core::ops::Div>::Output`
(expected type parameter,
found associated type) [E0308]
src/main.rs:2 a / b
^~~~~
Я понимаю, что ошибка компилятора говорит мне, что результатом операции деления является тип Output
не T
и я вижу Output
введите в стандартной документации библиотеки.
Как мне конвертировать из Output
в T
? Я пытаюсь использовать as
транслировать.
fn divide<T: std::ops::Div>(a: T, b: T) -> T {
(a / b) as T
}
fn main() {
println!("{}", divide(42, 18))
}
Это вызывает другую ошибку компилятора.
src/main.rs:2:5: 2:17 error: non-scalar cast: `<T as core::ops::Div>::Output` as `T`
src/main.rs:2 (a / b) as T
^~~~~~~~~~~~
У меня нет идей сделать эту работу, и я понимаю, что мне не хватает понимания чего-то фундаментального в этом языке, но я даже не знаю, что искать, чтобы сделать эту работу. Помогите?
2 ответа
Вы просто должны указать T::Output
в качестве типа возврата функции:
fn divide<T: std::ops::Div>(a: T, b: T) -> T::Output {
a / b
}
Изменить, чтобы добавить больше объяснений, почему вы не можете выполнять приведение внутри функции
Когда вы НАХОДИТЕСЬ за разделение общей функции, компилятор еще не знает, что вы можете привести T
в T::Output
Таким образом, приведение неверно. Это универсальные типы, они могут быть любыми, как компилятор знает, что вы можете привести T
в T::Output
?
a / b
производит что-то типа T::Output
Таким образом, в решении выше не приведен, T::Output
это просто правильный тип.
Отредактируйте, чтобы добавить другое возможное решение, используя std::convert::From
Наиболее (я думаю) общая реализация - это когда вы знаете, что T::Output
до Т возможно. Вы можете связать T
реализовать From
за T::Output
, Это полный пример:
use std::ops::Div;
use std::convert::From;
fn divide<T: Div>(a: T, b: T) -> T
where T: From<<T as Div>::Output>
{
T::from(a / b)
}
#[derive(Debug)]
struct Bip(u32);
impl Div for Bip {
type Output = f32;
fn div(self, rhs: Bip) -> f32 {
(self.0 / rhs.0) as f32
}
}
impl From<f32> for Bip {
fn from(value: f32) -> Self {
Bip(value as u32)
}
}
fn main() {
println!("{:?}", divide(12, 4));
println!("{:?}", divide(Bip(12), Bip(4)));
}
Ответ Андреаса объясняет, почему приведение невозможно, и что создание функции как можно более универсальной решает эту проблему.
Однако это не единственное решение. Rust также поддерживает возможность ограничения связанных типов (Output
здесь) общей функции.
Альтернативой будет:
use std::ops::Div;
fn divide<T>(a: T, b: T) -> T
where T: Div<Output = T>
{
a / b
}
<Output = T>
бит инструктирует компилятор принимать только в этой функции типы T
для которого реализация Div
имеет Output
равно T
, Это, очевидно, более ограничивает, но позволяет гарантировать, что результат divide
имеет тип T
,