Let's say I have a trait like this:
trait InternalError {
fn internal(error: String) -> Self;
}
If I want to use this in a function, I can do so like this:
struct MyU16(pub u16);
fn my_try_from<E: InternalError>(value: u32) -> Result<MyU16, E> {
if value < u16::MAX as u32 {
Ok(MyU16(value as u16))
} else {
Err(E::internal("invalid".to_string()))
}
}
Now imagine instead of using my own function, I want to use TryFrom
:
impl<E: InternalError> TryFrom<u32> for MyU16 {
type Error = E;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if value < u16::MAX as u32 {
Ok(MyU16(value as u16))
} else {
Err(E::internal("invalid".to_string()))
}
}
}
This however does not work. I have tried various different ways to phrase this, but I can't figure out how to say "this TryFrom impl, in the error case, returns something that impls InternalError".
Any advice on how to do this properly would be much appreciated. Thanks!
this TryFrom impl, in the error case, returns something that impls InternalError
TryFrom
wants to know at compile time what kind of error is returned. You seem to want to decide on the type at runtime.
Let's assume FirstError
and SecondError
both implement InternalError
.
In this case
impl TryFrom<u32> for MyU16 {
type Error = FirstError;
fn try_from(value: u32) -> Result<Self, FirstError> {
/*...*/
}
}
Would compile, but of course try_from
can only ever return FirstError
.
If you want to decide at runtime between FirstError
and SecondError
however. The associated error type (not trait) needs to be able to hold both.
impl TryFrom<u32> for MyU16 {
type Error = Box<dyn InternalError>;
fn try_from(value: u32) -> Result<Self, Box<dyn InternalError>> {
/*...*/
Box::new(FirstError::internal("invalid".to_string()))
}
}
This also gives a hint of why enum
s are the idiomatic way to represent errors in Rust: Usually you do not know at compile time what is going to go wrong. So an enum
which can list all the states makes it clear for the user what could go wrong and in many situations allows you to create the error without an additional heap allocation.
The reason it won't compile is there is a rule that the type parameter in the impl
declaration has to appear in the implementing type ( impl<T> Foo<T>
) or the implemented trait ( impl<T> MyTrait<T> for Foo
). You can't just write impl<T> for Foo
and only mention T
again inside the impl
block.
As the expanded error suggests, there is a workaround introducing a phantom type to the implementing type, but you might not like how it makes your code look:
use std::marker::PhantomData;
struct MyU16<T>(pub u16, PhantomData<T>);
impl<E: InternalError> TryFrom<u32> for MyU16<E> {
type Error = E;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if value < u16::MAX as u32 {
Ok(MyU16(value as u16, PhantomData))
} else {
Err(E::internal("invalid".to_string()))
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.