如何实现带有特征 MyTrait<A> 的结构?

时间:2021-02-15 23:55:36

标签: generics rust polymorphism traits parametric-polymorphism

我定义了一个特征如下:

conda install -c conda-forge basemap

这个想法是用不同的后端返回不同的错误类型来实现它。我试图在一个函数中使用它:

trait Readable<E> {
    fn read_u8(&mut self) -> Result<u8, E>;
    fn read_u16be(&mut self) -> Result<u16, E>;
}

这就编译了。我在一个 impl 中尝试了同样的方法:

fn f<E, R: Readable<E>>(r: &mut R) -> Result<u8, E> {
    r.read_u8()
}

Playground

这失败了:

struct FileFormat<R> {
    r: R,
}

impl<E, R: Readable<E>> FileFormat<R> {
    fn f(&mut self) -> Result<u8, E> {
        self.r.read_u8()
    }
}

编译器建议使用 14 | impl<E, R: Readable<E>> FileFormat<R> { | ^ unconstrained type parameter 但恐怕我无法理解其中包含的答案(如果它在那里)。

为什么前者能编译而后者不能?为什么在这种情况下 rustc --explain E0207 是不受约束的?如何解决此问题,以便实现能够采用任何 E

1 个答案:

答案 0 :(得分:2)

让我们想象一下它确实编译了。这是一个带注释的示例,它表明它允许我们编写编译器无法进行类型检查的损坏代码:

trait Readable<E> {
    fn read_u8(&mut self) -> Result<u8, E>;
    fn read_u16be(&mut self) -> Result<u16, E>;
}

fn f<E, R: Readable<E>>(r: &mut R) -> Result<u8, E> {
    r.read_u8()
}

struct SomeError;
struct SomeOtherError;
struct SomeReadable;

impl Readable<SomeError> for SomeReadable {
    fn read_u8(&mut self) -> Result<u8, SomeError> {
        todo!()
    }
    fn read_u16be(&mut self) -> Result<u16, SomeError> {
        todo!()
    }
}

impl Readable<SomeOtherError> for SomeReadable {
    fn read_u8(&mut self) -> Result<u8, SomeOtherError> {
        todo!()
    }
    fn read_u16be(&mut self) -> Result<u16, SomeOtherError> {
        todo!()
    }
}

struct FileFormat<R> {
    r: R,
}

// let's pretend that this does compile
impl<E, R: Readable<E>> FileFormat<R> {
    fn f(&mut self) -> Result<u8, E> {
        self.r.read_u8()
    }
}

// it will now allow us to write this code
// which is impossible to type check so
// it's obviously broken
fn example(mut fr: FileFormat<SomeReadable>) -> Result<u8, ???> {
    // um, does this return Result<u8, SomeError>
    // or does it return Result<u8, SomeOtherError>???
    // it's impossible to know!
    fr.f()
}

playground

错误类型需要出现在 FileFormat 类型中的某处。修复方法很简单,只需将 PhantomData 成员添加到 FileFormat,这样您就可以“确定”特定的错误类型:

use core::marker::PhantomData;
trait Readable<E> {
    fn read_u8(&mut self) -> Result<u8, E>;
    fn read_u16be(&mut self) -> Result<u16, E>;
}

fn f<E, R: Readable<E>>(r: &mut R) -> Result<u8, E> {
    r.read_u8()
}

struct SomeError;
struct SomeOtherError;
struct SomeReadable;

impl Readable<SomeError> for SomeReadable {
    fn read_u8(&mut self) -> Result<u8, SomeError> {
        todo!()
    }
    fn read_u16be(&mut self) -> Result<u16, SomeError> {
        todo!()
    }
}

impl Readable<SomeOtherError> for SomeReadable {
    fn read_u8(&mut self) -> Result<u8, SomeOtherError> {
        todo!()
    }
    fn read_u16be(&mut self) -> Result<u16, SomeOtherError> {
        todo!()
    }
}

struct FileFormat<R, E> {
    r: R,
    e: PhantomData<E>,
}

// now compiles!
impl<E, R: Readable<E>> FileFormat<R, E> {
    fn f(&mut self) -> Result<u8, E> {
        self.r.read_u8()
    }
}

// now works!
fn example(mut fr: FileFormat<SomeReadable, SomeError>) -> Result<u8, SomeError> {
    fr.f()
}

// now also works!
fn other_example(mut fr: FileFormat<SomeReadable, SomeOtherError>) -> Result<u8, SomeOtherError> {
    fr.f()
}

playground

独立的泛型函数之所以起作用,是因为我们在调用函数时指定了错误类型:

trait Readable<E> {
    fn read_u8(&mut self) -> Result<u8, E>;
    fn read_u16be(&mut self) -> Result<u16, E>;
}

struct SomeError;
struct SomeOtherError;
struct SomeReadable;

impl Readable<SomeError> for SomeReadable {
    fn read_u8(&mut self) -> Result<u8, SomeError> {
        todo!()
    }
    fn read_u16be(&mut self) -> Result<u16, SomeError> {
        todo!()
    }
}

impl Readable<SomeOtherError> for SomeReadable {
    fn read_u8(&mut self) -> Result<u8, SomeOtherError> {
        todo!()
    }
    fn read_u16be(&mut self) -> Result<u16, SomeOtherError> {
        todo!()
    }
}

fn f<E, R: Readable<E>>(r: &mut R) -> Result<u8, E> {
    r.read_u8()
}

fn example() {
    let mut readable: SomeReadable = SomeReadable;
    // error type clarified to be SomeError here
    f::<SomeError, _>(&mut readable);

    let mut readable: SomeReadable = SomeReadable;
    // error type clarified to be SomeOtherError here
    f::<SomeOtherError, _>(&mut readable);
}

playground

这一切都归结为让您的类型对编译器可见。

相关问题