一个通用结构体的构造函数中出现了“预期类型参数”错误。

28

我正在尝试将活塞杆纹理存储在一个结构体中。

struct TextureFactory<R> where R: gfx::Resources {
    block_textures: Vec<Rc<Texture<R>>>,
}

impl<R> TextureFactory<R> where R: gfx::Resources  {
    fn new(window: PistonWindow) -> Self {
        let texture = Rc::new(gfx_texture::Texture::from_path(
            &mut *window.factory.borrow_mut(),
            "assets/element_red_square.png",
            Flip::None, &TextureSettings::new()
        ).unwrap());
        let block_textures = Vec::new();
        block_textures.push(texture);

        TextureFactory {
            block_textures: block_textures,
        }
    }
}

这段代码无法编译:

src/main.rs:37:9: 39:10 error: mismatched types:
 expected `TextureFactory<R>`,
    found `TextureFactory<gfx_device_gl::Resources>`
(expected type parameter,
    found enum `gfx_device_gl::Resources`)

gfx_device_gl::Resources 实现了 gfx::Resources(我认为这只是特定设备的实现)。我实际上并不关心它的类型,但我需要知道这样我才能将其存储在结构体中。

我在 Github 上创建了一个可编译的仓库

(我怀疑问题 Rust generics/traits: "expected 'Foo<B>', found 'Foo<Foo2>'" 是相同的问题,但我无法弄清如何将其应用于我的问题。)


可能是 http://stackoverflow.com/questions/31490913/rust-generics-expected-t-found-foo 或者 https://dev59.com/d4zda4cB1Zd3GeqPll8p 的重复问题。 - Shepmaster
你可以使用特质对象来实现你的代码所涉及的多态性。 - cheme
1个回答

41

以下是您错误的复现:

struct Foo<T> {
    val: T,
}

impl<T> Foo<T> {
    fn new() -> Self {
        Foo { val: true }
    }
}

fn main() {}

问题出现的原因是您试图欺骗编译器。这段代码:
impl<T> Foo<T> {
    fn new() -> Self {
        /* ... */
    }
}

说:“无论调用者选择什么T,我都会创建一个具有该类型的Foo”。然后你的实际实现会选择一个具体类型——在本例中是bool。并没有保证Tbool。请注意,你的new函数甚至不接受任何T类型的参数,这是非常可疑的,因为调用者99%的时间都是选择具体类型。
正确的说法应该是:
impl Foo<bool> {
    fn new() -> Self {
        Foo { val: true }
    }
}

尽管您可能希望选择一个更具体的名称而不是new,因为它看起来像您正在尝试使您的结构体通用化。想必会有其他使用不同类型的构造函数。

对于您的确切代码,您可能需要类似以下内容:

impl TextureFactory<gfx_device_gl::Resources> { /* ... */ }

另一种可能的解决方案是从你的结构体中移除通用类型参数。如果你只使用gfx_device_gl::Resources来构建它,那么就没有理由让它成为通用类型。

在其他情况下,你可能试图返回实现了某个trait的类型。对于这种情况,你可以使用一个装箱的trait对象:

impl Foo<Box<dyn std::fmt::Display>> {
    fn new() -> Self {
        Foo { val: Box::new(true) }
    }
}

在未来,您可能还可以使用impl Trait(也称为存在类型):

#![feature(type_alias_impl_trait)]

struct Foo<T> {
    val: T,
}

type SomeConcreteButOpaqueType = impl std::fmt::Display;

impl Foo<SomeConcreteButOpaqueType> {
    fn new() -> Self {
        Foo { val: true }
    }
}

另请参阅:


谢谢!我没有意识到我可以在 impl 中“专门化”事物。 - Xavier Shay

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