转换为泛型类型

Cra*_*urg 11 rust

我有一个关于Rust中的泛型的新手问题(版本1.0).

假设我写了一个通用函数来进行除法.别担心这种功能的用处; 这个问题很简单,这是一个简单的功能.

fn divide<T: std::ops::Div>(a: T, b: T) -> T {
    a / b
}

fn main() {
    println!("{}", divide(42, 18))
}
Run Code Online (Sandbox Code Playgroud)

该程序无法编译.

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
                  ^~~~~
Run Code Online (Sandbox Code Playgroud)

我知道编译器错误告诉我除法运算的结果是类型Output,而不是T,我Output标准库文档中看到了类型.

我如何转换OutputT?我试着as用来演员.

fn divide<T: std::ops::Div>(a: T, b: T) -> T {
    (a / b) as T
}

fn main() {
    println!("{}", divide(42, 18))
}
Run Code Online (Sandbox Code Playgroud)

这会导致不同的编译器错误.

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
                  ^~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

我没有想法让这项工作成为现实,我意识到我对这里的语言基本缺乏了解,但我甚至不知道该怎么做才能使这项工作成功.救命?

eul*_*isk 11

您只需指定T::Output函数的返回类型:

fn divide<T: std::ops::Div>(a: T, b: T) -> T::Output {
    a / b
}
Run Code Online (Sandbox Code Playgroud)

编辑以添加有关您无法在函数内执行转换的原因的更多说明

当你处于通用函数除法时,编译器还不知道你可以转换TT::Output,所以转换是无效的.它们是泛型类型,它们可以是任何东西,编译器如何知道你可以转换TT::Output

a / b产生类型的东西T::Output,所以在上面的解决方案中没有强制转换,T::Output只是正确的类型.

编辑以使用std :: convert :: From添加另一个可能的解决方案

最常见的(我认为)通用实现是当你知道从T::OutputT到T的转换是可能的.您可以结合T来实现FromT::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)));
}
Run Code Online (Sandbox Code Playgroud)

  • 因为当你在你的泛型函数`divide`中时,编译器还不知道你可以将`T`强制转换为'T:Output`.(它们是通用的,它们可以是任何东西,甚至是无法转换的东西)`a/b`产生类型为`T :: Output`的东西,所以在上面的解决方案中没有强制转换,`T :: Output`就是简单的正确的类型. (2认同)

Mat*_* M. 6

安德烈亚斯的回答解释了为什么演员阵容是不可能的,并且尽可能使函数尽可能通用解决了这个问题.

然而,它不是唯一的解决方案.Rust还支持限制Output泛型函数的相关类型(此处)的能力.

另一种选择是:

use std::ops::Div;

fn divide<T>(a: T, b: T) -> T
    where T: Div<Output = T>
{
    a / b
}
Run Code Online (Sandbox Code Playgroud)

<Output = T>位指示编译器仅接受此T实现Div具有Output等于的函数类型T.这显然更具限制性,但它允许确保结果divide类型T.

  • 关于`=`约束的好点.我在官方文档中找不到太多,你能给我们一些参考吗?然而,要添加更多的东西,就有这样的情况,你需要为`T`实现`From`特性.(我再次编辑了我的回复) (3认同)