Is there a simpler version of `Option::map(|o|o.into())`? [duplicate] - rust

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.

Related

generic From implementation in Rust

I have two structs A and B that are exactly similar. I am trying to convert both A and B to another type C. The definitions of A,B and C are given below.
pub struct A {
pub a: i32,
}
pub struct B {
pub a: i32,
}
pub struct C {
pub b: i32,
}
My implementation for converting from A to C is shown below:-
impl From<A> for C {
fn from(a: A) -> C {
C {b: a.a}
}
}
Since A and B both are similar, for converting from B to C, currently I have a duplicate
implementation of the From defined above.
I am looking for a way to make the From implementation generic and only bounded to use
A and B. My implementation here is as follows:-
trait Types {}
impl Types for A {}
impl Types for B {}
impl<T: Types> From<T> for C where T: Types {
fn from(entity: T) -> C {
C { b: entity.a }
}
}
But when I compile a program with the code above, I get the following error,
error[E0609]: no field `a` on type `T`
|
27 | impl<T: Types> From<T> for C where T: Types {
| - type parameter 'T' declared here
I would like to know a way to resolve this error since I have no other choice but to preserve A and B and avoid code duplication.
Usually this is done by macros:
macro_rules! to_C {
($t:ty) => {
impl From<$t> for C {
fn from(a: $t) -> C {
C{b: a.a}
}
}
};
}
to_C!{A}
to_C!{B}

Combine traits with associated types in rust

I'm trying to create a kind of wrapper trait that combines multiples traits and a function that return the associated implementation of the trait. It works well as long as you don't have associated types. I don't know how to refer to the Output type
I am aware of how to combine traits :
Is there a way to combine multiple traits in order to define a new trait?
Is there any way to create a type alias for multiple traits?
But unfortunately, I've not found anything with associated types
Here is a summarized example of where I'm stucked
use std::ops::Add;
pub trait Hello {
fn hello(&self);
}
pub trait Goodbye {
fn goodbye(&self);
}
struct Suffix {
suffix: String,
}
pub struct World {
content: String,
}
impl World {
fn new(content: String) -> Self {
World { content: content }
}
}
impl Hello for World {
fn hello(&self) {
println!("Hello {}", self.content)
}
}
impl Goodbye for World {
fn goodbye(&self) {
println!("Goodbye {}", self.content)
}
}
impl Add<Suffix> for World {
type Output = World;
fn add(self, other: Suffix) -> World {
let suffixed: String = self.content + &other.suffix;
World::new(suffixed)
}
}
trait HelloGoodbye: Hello + Goodbye {}
impl<T> HelloGoodbye for T where T: Hello + Goodbye {}
fn get_hello_goodbye() -> Box<dyn HelloGoodbye> {
Box::new(World::new("Everyone".to_string()))
}
trait SuffixableHello: Hello + Add<Suffix, Output = Self> {}
impl<T> SuffixableHello for T where T: Hello + Add<Suffix, Output = Self> {}
fn get_suffixable_hello() -> Box<dyn SuffixableHello> {
Box::new(World::new("Everyone".to_string()))
}
fn main() {
// This works
let hello_goodbye = get_hello_goodbye();
hello_goodbye.hello();
hello_goodbye.goodbye();
// This does not work
let suffixable_hello = get_suffixable_hello();
suffixable_hello.hello()
}
I got this compilation error :
49 | fn get_suffixable_hello() -> Box<dyn SuffixableHello> {
| ^^^^^^^^^^^^^^^ help: specify the associated type: `SuffixableHello<Output = Type>`
What am I supposed to put there ?
What I've try so far :
Make trait generic
trait SuffixableHello<T>: Hello + Add<Suffix, Output = T> {}
impl<T, U> SuffixableHello<T> for T where T: Hello + Add<Suffix, Output = Self> {}
And I get
|
49 | fn get_suffixable_hello() -> Box<dyn SuffixableHello<T>> {
| ~~~~~~~~~~~~~~~~~~
Where am I supposed to add this T generic ? Does my fucntion need to be generic ?
Only add Output to implementation
trait SuffixableHello: Hello + Add<Suffix> {}
impl<T> SuffixableHello for T where T: Hello + Add<Suffix, Output = T> {}
But I get :
the value of the associated type `Output` (from trait `Add`) must be specified
This makes sense since Output is not declared in the trait.
Replacing World by Self in the impl Add<Suffix> for World
Am I missing something here ?
Thank you
Also, what if we want to return from the get_suffixable_hello() one of two Hello implementations, let's say World and World2 to cite #cadolphs like in this doc https://doc.rust-lang.org/rust-by-example/trait/dyn.html
fn get_suffixable_hello() -> Box<dyn SuffixableHello<Output=World>> {
Box::new(World::new("Everyone".to_string()))
}
does the trick.
EDIT: Longer explanation. Your first initial compiler error tells you that you can't just return Box<dyn SuffixableHello>. That's because having the same trait but with different associated types isn't allowed, because the signatures of associated methods would be different.
So. We need to put a type there. And what should that type be? Well, given that you're explicitly calling World::new, there's really only one type that makes sense here, and that's World.
In your toy example that makes it of course a bit silly and redundant because there's only one struct that implements all those traits.
If you had another struct, World2, you could not have it be returned by get_suffixable_hello next to World, because World2 would have associated type Output=World2 and hence
wouldn't match the Output=World associated type.

How to solve "expected struct, found type parameter" on assignment?

Just started working with Rust a couple of days ago. I'm porting some C++ code right now, and this question seems to be the reverse of the common "expected struct, got type" sort. This code involves two classes, a container class A and a client class B.
use std::vec::Vec;
struct A<T:FooTrait> {
children: Vec<*mut T>
}
impl <T:FooTrait> A<T> {
fn insert(&mut self, val: &mut T) -> Handle<T> {
self.children.push(val);
return Handle{owner: self};
}
}
struct B {
handle: Handle<B>
}
trait FooTrait {
fn set_handle<T:FooTrait>(&mut self, h: Handle<T>);
}
impl FooTrait for B {
fn set_handle<B:FooTrait>(&mut self, h: Handle<B>) {
self.handle = h; // <-- Here is the problem
}
}
struct Handle<T:FooTrait> {
owner: *mut A<T>
}
impl <T:FooTrait> Default for Handle<T> {
fn default()->Handle<T> {
Handle {
owner: std::ptr::null_mut()
}
}
}
fn main() {
let mut foo = A::<B> { children: Default::default() };
let mut b = B{handle: Default::default()};
b.handle = foo.insert(&mut b);
}
Getting the error:
error[E0308]: mismatched types
--> src/main.rs:23:23
|
22 | fn set_handle<B:FooTrait>(&mut self, h: Handle<B>) {
| - this type parameter
23 | self.handle = h;
| ^ expected struct `B`, found type parameter `B`
|
= note: expected struct `Handle<B>` (struct `B`)
found struct `Handle<B>` (type parameter `B`)
Simplified version (playground):
use std::marker::PhantomData;
struct B {
handle: PhantomData<B>,
}
trait FooTrait {
fn set_handle<T: FooTrait>(&mut self, h: PhantomData<T>);
}
impl FooTrait for B {
fn set_handle<BType: FooTrait>(&mut self, h: PhantomData<BType>) {
self.handle = h;
}
}
Note that I've changed the name of the type parameter in set_handle. Now the error is more clear:
error[E0308]: mismatched types
--> src/lib.rs:13:23
|
12 | fn set_handle<BType: FooTrait>(&mut self, h: PhantomData<BType>) {
| ----- this type parameter
13 | self.handle = h;
| ^ expected struct `B`, found type parameter `BType`
|
= note: expected struct `std::marker::PhantomData<B>`
found struct `std::marker::PhantomData<BType>`
In your case, the error is essentially the same, since the generic parameter is a new type, which shadowed the global struct B.
Now, what to do? It depends on what do you want to get.
If the struct B definition is correct and set_handle need to handle only Bs, just remove the generic parameter from set_handle (playground):
trait FooTrait {
fn set_handle(&mut self, h: PhantomData<B>);
}
impl FooTrait for B {
fn set_handle(&mut self, h: PhantomData<B>) {
self.handle = h;
}
}
If the struct B definition is correct, but set_handle must be able to use different handler types depending on Self, use an associated type (playground):
trait FooTrait {
type T: FooTrait;
fn set_handle(&mut self, h: PhantomData<Self::T>);
}
impl FooTrait for B {
type T = B;
fn set_handle(&mut self, h: PhantomData<B>) {
self.handle = h;
}
}
Now the implementation block will choose what kind of argument (handler, in your case) it will get.
If the set_handle definition is correct, i.e. the caller can choose the type of handler, then struct B must be generic, too. However, in this case you essentially can't use the trait-based approach, since the trait has to be generic too, and you will not be able to simply use it in any generic bound without providing parameters (which has to be bound too, ad infinitum).
There are two different B here. The original B you defined in struct B, and the second <B:FooTrait> type parameter. The second is shadowing the first. Here is how the compiler sees it:
impl FooTrait for B {
fn set_handle<AnyFoo:FooTrait>(&mut self, h: Handle<AnyFoo>) {
self.handle = h; // <-- Here is the problem
}
}
The way you've defined things, FooTrait::set_handle is supposed to work with any type that implements FooTrait, but the handle field of B can only store a Handle<B>. You need to rethink what you want FooTrait to do and how B satisfies it.

Implementing From on a polymorphic type

I am writing a struct that functions as a thin wrapper over an existing struct. The existing struct isn't fixed, so I've written it as a type parameter (see struct B below). I would like B to preserve convertibility (i.e. From/Into) based on the internal wrapped type, i.e. if A1 is convertible to A then B<A1> should be convertible to B<A> as well.
I tried the following code:
pub struct A {}
pub struct A1 {}
impl From<A1> for A {
fn from(a1: A1) -> Self {
Self {}
}
}
pub struct B<T> {
t: T,
other_stuff: String,
}
impl<T, U: Into<T>> From<B<U>> for B<T> {
fn from(b: B<U>) -> Self {
Self {
t: b.t.into(),
other_stuff: b.other_stuff,
}
}
}
pub fn main() {
// I would like this to work:
let ba1: B<A1> = B{ t: A1{}, other_stuff: "abc".to_owned() };
let ba: B<A> = ba1.into();
}
It produces a compilation error:
error[E0119]: conflicting implementations of trait `std::convert::From<B<_>>` for type `B<_>`:
--> src/main.rs:13:1
|
13 | impl<T, U: Into<T>> From<B<U>> for B<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> std::convert::From<T> for T;
I think I understand why the error arises (because converting B<A> to B<A> is already defined), but how may I rewrite my code to avoid the error?

Why must the associated type be specified in a collection of trait object references?

Here is an offending example (Playground):
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
// Struct that holds a collection of these traits.
struct Example<'a> {
behaviours: Vec<&'a dyn Behaviour>,
}
impl<'a> Example<'a> {
fn add_behaviour<T: Behaviour>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
I get:
error[E0191]: the value of the associated type `Sub` (from trait `Behaviour`) must be specified
--> src/main.rs:17:29
|
3 | type Sub: SubBehaviour;
| ----------------------- `Sub` defined here
...
17 | behaviours: Vec<&'a dyn Behaviour>,
| ^^^^^^^^^ help: specify the associated type: `Behaviour<Sub = Type>`
Why must this type must be specified, particularly in this case where we are only storing a reference to the object? How can I get this code to work?
All types must be statically known at compile time. If Rust would allow different associated types for elements of a Vec, type information could depend on indices which are only known at runtime.
I find it helpful to consider a smaller example:
trait Behaviour {
type T;
fn make_t(&self) -> T;
}
fn foo(my_vec: Vec<&dyn Behaviour>, index: usize) {
let t = my_vec[index].make_t(); //Type of t depends on index
}
You were on the right track to fixing this though. I assume you introduced the SubBehaviour trait because you realized you need to put restrictions of what T can be. The thing is, in that case you don't need an associated type anymore.
trait SubBehaviour {}
trait Behaviour {
fn make_t(&self) -> Box<dyn SubBehaviour>;
fn ref_t(&self) -> &dyn SubBehaviour; // also fine
}
fn some_function(my_vec: Vec<&dyn Behaviour>, index: usize) {
let t1 = my_vec[index].make_t();
}
The only limitation is that in your definition of Behaviour you can not do anything which would depend on the size of T, (like allocating it on the stack or moving it) since the size of T can not be specified by the SubBehaviour trait.
You need to specify the associated type of the trait (i.e. Behavior<Sub = ???>).
When adding the associated type at all places, it compiles:
struct Example<'a, S: SubBehaviour + 'a> {
behaviours: Vec<&'a Behaviour<Sub = S>>,
}
impl<'a, S: SubBehaviour> Example<'a, S> {
fn add_behaviour<T: Behaviour<Sub = S>>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
See this in action on the Playground
So the answer to your first question is covered by Tim's answer and is correct, you might not want your Example to be generic. In that case, you need to use some sort of type erasure:
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
struct AnyBehaviour {
closure: Box<Fn()>,
}
impl AnyBehaviour {
fn new<U: SubBehaviour, T: Behaviour<Sub = U>>(b: &T) -> Self {
let closure = || {
//let sub = T::Sub::new();
println!("Can use T here");
};
AnyBehaviour {
closure: Box::new(closure),
}
}
}
// Struct that holds a collection of these traits.
struct Example {
behaviours: Vec<AnyBehaviour>,
}
impl Example {
fn add_behaviour<U: SubBehaviour, T: Behaviour<Sub = U>>(&mut self, b: &T) {
self.behaviours.push(AnyBehaviour::new(b));
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
Within the closure, you have access to all the types needed call the traits functions with whatever subtype needed.
Why this happens, is mostly because you actually need a definition of the associated type in order for the trait to be "complete" so the compiler can work with it. Tim's answer answers that by the definition to be higher up in the chain (outside of Example) instead of inside.

Resources