如何实现一个带有特征 MyTrait<A> 的结构?
How do implement a struct that takes a trait MyTrait<A>?
我定义了一个特征如下:
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()
}
编译通过。我在 impl 中尝试了同样的方法:
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
在这种情况下不受约束?如何解决这个问题,以便实施能够采取任何 Readable
?
让我们想象一下它确实编译了。这是一个注释示例,表明它可以让我们编写编译器无法进行类型检查的损坏代码:
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()
}
错误类型需要出现在 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()
}
独立的通用函数可以工作,因为我们在调用该函数时指定了错误类型:
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);
}
归根结底就是让你的类型对编译器可见。
我定义了一个特征如下:
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()
}
编译通过。我在 impl 中尝试了同样的方法:
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
在这种情况下不受约束?如何解决这个问题,以便实施能够采取任何 Readable
?
让我们想象一下它确实编译了。这是一个注释示例,表明它可以让我们编写编译器无法进行类型检查的损坏代码:
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()
}
错误类型需要出现在 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()
}
独立的通用函数可以工作,因为我们在调用该函数时指定了错误类型:
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);
}
归根结底就是让你的类型对编译器可见。