I have in my project a struct A
which is logically related to a struct B
from a different crate. Both have internally an optional sub-struct (C
/ D
).
Let's say for this example they have this struct definition:
struct D {
name: Option<String>
}
struct B {
spec: Option<D>
}
struct C {
name: Option<String>
}
struct A {
spec: Option<C>
}
Now I want to implement the Into
-trait on A
into B
:
impl Into<D> for C {
fn into(self) -> D {
D {
name: self.name
}
}
}
impl Into<B> for A {
fn into(self) -> B {
B {
spec: self.spec.into()
}
}
}
But rust does not allow it:
error[E0277]: the trait bound `std::option::Option<D>: From<std::option::Option<C>>` is not satisfied
--> src\model\k8s.rs:615:29
|
615 | spec: self.spec.into()
| ^^^^ the trait `From<std::option::Option<C>>` is not implemented for `std::option::Option<D>`
|
= help: the following implementations were found:
<std::option::Option<&'a T> as From<&'a std::option::Option<T>>>
<std::option::Option<&'a mut T> as From<&'a mut std::option::Option<T>>>
<std::option::Option<&'a tracing_core::span::Id> as From<&'a tracing::span::EnteredSpan>>
<std::option::Option<&'a tracing_core::span::Id> as From<&'a tracing::span::Span>>
and 10 others
= note: required because of the requirements on the impl of `Into<std::option::Option<D>>` for `std::option::Option<C>`
Although I provide a custom implementation for Into
on C
it only checks for From
. Which I can't provide as D
is another crate. I have to write this:
spec: if let Some(v) = self.spec { Some(v.into()) } else { None }
Now the question: Is there a better way I am missing? If not, why is it such a hassle to into()
Options?
The issue is that you're calling Into::into
on the Option<C>
type rather than the type the Option
holds (C
).
You can use the Option::map
method which operates on the inner type of the Option
:
impl Into<B> for A {
fn into(self) -> B {
B {
spec: self.spec.map(Into::into)
}
}
}
There is no blanket impl<T, U: Into<T>> Into<Option<T>> for Option<U>
(or the From
equivalent) in the standard library, that's why you can't use Into
trait to turn Option<T>
into Option<U>
directly on the Option
and have to rely on Option::map
or some other way (like your last snippet) of extracting the inner type instead.