泛型类型的通用构造函数需要显式类型注释

6

我正在为一个C库编写Rust绑定。它实现了一个可以从不同源实体构建的实体,并可能在内部保存一些引用。我希望Rust类型能够强制执行安全的所有权策略,因此包装器结构是通用的,由存储引用类型的参数化。

struct Foobar<T> {
    origin: T,
}

然后,我为我的Foobar类型实现了一些构造函数。

impl<T> Foobar<T> {
    fn from_nowhere() -> Foobar<()> {
        Foobar { origin: () }
    }

    fn from_orange<F>(orange: &mut F) -> Foobar<&mut F>
        where F: Orange
    {
        Foobar { origin: orange }
    }

    fn from_callback<F>(callback: F) -> Foobar<F>
        where F: FnMut(u64) -> u64
    {
        Foobar { origin: callback }
    }
}

问题来了:结构体和构造函数都是独立参数化的。虽然构造函数类型参数可以从其参数中推断出来,但结构体类型参数在构造函数中没有使用,因此无法推断。因此,调用构造函数的朴素方法如下:

let a = Foobar::from_nowhere();
let b = Foobar::from_orange(&mut fruit);
let c = Foobar::from_callback(|x| x*x);

混淆了rustc:

    rustgen.rs:43:13: 43:33 error: unable to infer enough type information about `_`; type annotations required [E0282]
    rustgen.rs:43     let a = Foobar::from_nowhere();

可以通过提供一些任意的类型参数来解决这个问题:

let a = Foobar::<()>::from_nowhere();
let b = Foobar::<()>::from_orange(&mut fruit);
let c = Foobar::<()>::from_callback(|x| x*x);

......这很丑陋。另一种解决问题的方法是将构造函数转换为自由函数,但这可能不太符合惯例。

问题是,我是否漏掉了什么?这个设计似乎有些缺陷。如何正确地设计此类型以仅使用一级泛型?


Rust playpen上的最小可复现示例

作为参考,我的编译器版本是:

$ rustc --version
rustc 1.1.0-dev (built 2015-04-26)
1个回答

8

据我理解,您的原始代码是基于参数 T 的,但您有一些方法需要指定参数。诀窍在于不要使用泛型来处理这些情况。相反,尝试为每个有趣的类型创建专门的实现:

// this is just an example. suppress unrelated warnings
#![allow(dead_code, unused_variables)]

struct Foobar<T> {
    origin: T,
}

trait Orange {}

struct Grapefruit;

impl Orange for Grapefruit {}

impl Foobar<()> {
    fn from_nowhere() -> Foobar<()> {
        Foobar { origin: () }
    }
}

impl<'a, F> Foobar<&'a mut F>
    where F: Orange
{
    fn from_orange(orange: &'a mut F) -> Foobar<&'a mut F> {
        Foobar { origin: orange }
    }
}

impl<F> Foobar<F>
    where F: FnMut(u64) -> u64
{
    fn from_callback(callback: F) -> Foobar<F> {
        Foobar { origin: callback }
    }
}

fn main() {
    let mut fruit = Grapefruit;

    // What I actually wanted to do
    let a1 = Foobar::from_nowhere();
    let b1 = Foobar::from_orange(&mut fruit);
    let c1 = Foobar::from_callback(|x| x*x);
}

1
谢谢!我没有意识到的是,针对不同参数化,可以有多个“impl”子句。 - Mikhail Borisov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接