泛型类型的泛型构造函数的显式类型注释

时间:2021-01-01 19:52:59

I'm writing a Rust binding for a C library. It implements an entity that can be constructed from different source entities, possibly saving some references internally. I want the Rust type to enforce a safe ownership policy, thus the wrapper struct is generic, parameterized by the type of the stored reference.

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

struct Foobar<T> {
    origin: T,
}

Then I implement some constructors for my Foobar type.

然后我为我的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 }
    }
}

And here comes the problem: both the struct and the constructor are independently parameterized. While the constructor type parameter can be inferred from its arguments, the struct type parameter is not used in the constructor and cannot be inferred. Thus, the naive way to call a constructor

这就出现了问题:结构和构造函数都是独立参数化的。虽然可以从其参数推断构造函数类型参数,但构造函数中不使用struct type参数,也无法推断它。因此,调用构造函数的天真方式

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

confuses 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();

It can be fixed by providing some arbitrary type parameter:

它可以通过提供一些任意类型参数来修复:

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

...which is all sorts of ugly. Another way to solve the problem would be to turn constructors into free functions, though it would be (kinda) non-idiomatic.

......这是各种丑陋的。解决问题的另一种方法是将构造函数转换为*函数,尽管它(有点)是非惯用的。

The question is, am I missing something? The design seems to be flawed in some way. What would be the proper way to design this type to get away with only one level of generics?

问题是,我错过了什么吗?设计似乎在某种程度上存在缺陷。设计这种类型的唯一方法是什么才能逃脱只有一个级别的泛型?


Minimal reproducible example on Rust playpen

Rust围栏的最小可重复性示例

For reference, my compiler version is:

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

$ rustc --version
rustc 1.1.0-dev (built 2015-04-26)

1 个解决方案

#1


As I understand it, your original code is parameterized on T, but you have methods that want to specify the parameter. The trick is to not have a generic for these cases. Instead, try creating specialized implementations for each interesting type:

据我了解,您的原始代码在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


As I understand it, your original code is parameterized on T, but you have methods that want to specify the parameter. The trick is to not have a generic for these cases. Instead, try creating specialized implementations for each interesting type:

据我了解,您的原始代码在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);
}