Object to trait conversion - rust

Assuming the following Rust code:
trait MyTrait {...}
struct MyStruct;
impl MyTrait for MyStruct {...}
impl MyStruct {
pub fn new() -> MyStruct { MyStruct{...} }
}
I'm able to write:
let foo: MyStruct = MyStruct::new();
However I can't
let bar: MyTrait = MyStruct::new();
Other interface-oriented programming languages can do that. Am I missing something? How can I pass various objects to a method that accepts only MyTrait? Do I need to use "templates" such as my_method<T: MyTrait>(param: T) {...}?

You don't need to define bar as a MyTrait object. (This is even the wrong thinking, because rust doesn't really have inheritance. Think of traits as more like interfaces in Java. In this case, it seems like you're treating MyTrait as a superclass of MyStruct)
You can do this
let bar = MyStruct::new();
and then just pass it in function defined like this:
fn my_method<T: MyTrait>(param: T) {...}
my_method(bar)
This works because Rust statically checks that bar, which is of type MyStruct, implements the trait MyTrait.
Side note: If you want to create a vector of trait objects, check this question.

This is because a trait may be implmented by many types and therefor the size of bar is unknown. However a reference's size would be know, so the following works.
trait MyTrait {}
struct MyStruct;
impl MyTrait for MyStruct {}
impl MyStruct {
fn new() -> Self {
MyStruct
}
}
fn main() {
let foo: MyStruct = MyStruct::new();
let bar: &MyTrait = &MyStruct::new();
}
Notice that bar isn't a MyTrait, it's a &MyTrait, this is called a trait object.

Related

Using trait objects on types with Default

I'm trying to use a trait object in a hashmap so I can call an update function.
This here is really the code I'm trying to write. I am pretty sure that I can change this code to get this to work the way that I want as-is, but I figured I can turn this into a learning opportunity because the Sized stuff is not quite clear to me.
let mut hm: HashMap<SomeEnum, Box<dyn SomeTrait>> = HashMap::new();
hm.entry(SomeEnum::Type1).and_modify(|b| b.some_other_func()).or_insert(/* HOW */ default());
But I don't really understand the Sized restrictions on trait objects or what Sized is (or why using Default as a Supertrait prevents the object from being Sized). If the entry does not exist in the hashmap, I would like to add the default version of that object to the entry.
Here's an reprex showing my issue.
use std::collections::HashMap;
#[derive(PartialEq, Eq, Hash)]
enum SomeEnum {
Type1,
Type2,
}
// SomeTrait is a subtrait of Default
// Because I want every type to implement Default
trait SomeTrait: Default {
fn new() -> Self {
Self {
..Default::default()
}
}
fn some_other_func(&self);
}
// SomeStruct implements Default and SomeTrait
struct SomeStruct {
foo: i32,
}
impl Default for SomeStruct {
fn default() -> Self {
SomeStruct { foo: 10 }
}
}
impl SomeTrait for SomeStruct {
fn some_other_func(&self) {}
}
fn main() {
let mut hm: HashMap<SomeEnum, Box<dyn SomeTrait>> = HashMap::new();
hm.entry(SomeEnum::Type1)
.and_modify(|b| b.some_other_func())
.or_insert(/* HOW */ default());
}
I cannot use a generic type instead of the trait object because I do/will have multiple implementations of this trait.
I have also tried creating a new trait that is a subtrait of both Default and the one I want:
trait AutoSomeType: SomeType + Default {}
trait SomeType {
fn new() -> Self
where
Self: Sized,
{
Self {
..Default::default()
}
}
fn some_other_func(&self);
}
I got here somehow based on a compiler recommendation, but I feel like this isn't on the right track to a solution.

Is it possible to declare a trait that accepts more methods that the ones declared in its body?

Something like:
pub trait MyTrait {
// No methods defined/declared
}
impl MyTrait for MyType {
fn method_one_not_declared_neither_defined(&self) {
// body of trait method impl here
}
fn method_two_not_declared_neither_defined(&self) {
// body of trait method impl here
}
}
While the trait itself cannot be extended, you can still add behaviour to all types which implement a trait. This can be done using extension traits and blanket implementations.
trait MyTrait {}
struct MyType;
impl MyTrait for MyType {}
trait TraitExt {
fn foo(&self) {
println!("Foo");
}
}
impl<T> TraitExt for T where T: MyTrait {}
fn main() {
let t = MyType {};
t.foo();
}
Playground
This is not the same as extending MyTrait however - TraitExt must be in scope for foo to be called.
No. Only methods declared in trait definition might be implemented when implementing trait for a type. You can add this methods as normal type's methods, but not as trait's. Think of a trait as a public interface. You must implement all of trait's methods (besides those with default implementation) and cannot add anything more.

How to return a box to self?

I'm new to rust and I experience difficulties to develop my personal first project.
To get the essential my code looks like this:
pub trait MyTrait {
type Out: MyTrait;
fn foo(&self) -> Box<Self::Out>;
}
pub struct MyStruct<T: MyTrait>(Box<T>);
impl<T: MyTrait> MyTrait for MyStruct<T> {
type Out = Self <>;
fn foo(&self) -> Box<Self::Out> {
Box::new(self)
}
}
The error message I get is :
expected struct MyStruct<T> found reference &MyStruct<T>
I tried many things but I can't find how to return a box to self...
I have another question: actually MyTrait extends another trait and I have many struct that implement MyTrait so in my code I have many impl, one for each trait by struct and I find it really verbose, isn't it possible to implement all methods (from a trait and all its parents) at once (like in java) ? Or is there syntactic sugar to implement methods with closures ?
Thanks
Box needs to own self. You cannot pass a reference into it, it has to be the actual self object.
This works:
pub trait MyTrait {
type Out: MyTrait;
fn foo(self) -> Box<Self::Out>;
}
pub struct MyStruct<T: MyTrait>(Box<T>);
impl<T: MyTrait> MyTrait for MyStruct<T> {
type Out = Self;
fn foo(self) -> Box<Self::Out> {
Box::new(self)
}
}
Further remarks:
I'm unsure why you need that function, but it seems to me like you are trying to downcast from a boxed dyn trait to an actual type.
Sadly, this won't work this way. It's impossible for the compiler to figure out which type was the original type if your Box<dyn MyTrait> doesn't contain that information any more. That's why you need the Out associated type here, but you probably didn't yet realize that this means you can no longer store a Box<dyn MyTrait> object. It's now a Box<dyn MyTrait<Out = MyStruct>> object, which cannot be mixed with other MyTrait types any more.
If you really want to achieve that, there are several options. RTTI is one option, with the Any trait.
Another one would be a Visitor pattern, which could resolve this:
pub trait Visitor {
fn visit_mystruct(&mut self, s: &mut MyStruct);
}
pub struct MyVisitor;
impl Visitor for MyVisitor {
fn visit_mystruct(&mut self, s: &mut MyStruct) {
println!("Visited MyStruct: {:?}", s.0);
}
}
pub trait MyTrait {
fn visit(&mut self, visitor: &mut dyn Visitor);
}
pub struct MyStruct(i32);
impl MyTrait for MyStruct {
fn visit(&mut self, visitor: &mut dyn Visitor) {
visitor.visit_mystruct(self);
}
}
fn main() {
let mut obj: Box<dyn MyTrait> = Box::new(MyStruct(42)) as Box<dyn MyTrait>;
let mut visitor = MyVisitor;
// Here is no information any more about the actual type of `obj`.
// Visitor gets the type resolved again
obj.visit(&mut visitor);
}
Visited MyStruct: 42
A visitor pattern is especially useful in cases like tree structures, because it can be recursively applied to children. It is commonly used in compilers.
Another alternative would be to use an Enum instead of a Box<dyn Trait>.
Although those are all just based on assumptions now.

Copying std::thread::spawn and Send behavior, but with a different trait and function

I am learning Rust and have run into the following problem, which is not obvious to me.
I saw a std::thread::spawn in the library, looked at the implementation, and saw that some type requires an implementation of the Send trait to be able to send something to another thread.
I'm trying to replicate the behavior with my own function and my own trait, but the compiler successfully compiles the code without complaining that the trait is not implemented for my types.
What am I missing?
pub trait SomeTrait {
fn bar(&self);
}
#[derive(Debug)]
struct StructWithoutTrait {
_data: Box<i32>
}
#[derive(Debug)]
struct StructWithTrait {
_data: Box<i32>
}
impl SomeTrait for StructWithTrait {
fn bar(&self) {}
}
fn foo<F, T>(f: F) -> T
where
F: FnOnce() -> T,
F: SomeTrait + 'static,
T: SomeTrait + 'static
{
f.bar();
f()
}
impl<F, T> SomeTrait for F
where
F: FnOnce() -> T,
T: SomeTrait
{
fn bar(&self) {}
}
fn main() {
let without_trait = StructWithoutTrait { _data: Box::new(1) };
let with_trait = StructWithTrait { _data: Box::new(2) };
let x = std::rc::Rc::new(1);
foo(move || {
println!("{:?}", without_trait);
println!("{:?}", x);
with_trait
});
}
Send is an auto trait. Auto-traits are somewhat like compiler-implemented; the steps of determining whether a type T implements an auto trait AutoTrait are the following:
If there is an explicit impl AutoTrait for T, then T always impls AutoTrait (note that because Send is an unsafe trait you need unsafe impl Send for T, but the general principle stays the same).
Otherwise, if there is a negative impl impl !AutoTrait for T, then T does not implement AutoTrait. Note that this is an error to have both positive and negative impl of the same trait for the same type.
Otherwise, if any of the fields of T does not implement AutoTrait, T does not implement it either. For example, if T is defined as struct T(U);, and there's impl !AutoTrait for U, then T does not implement AutoTrait.
Otherwise, T implements AutoTrait.
Both auto traits and negative impls are highly unstable features, and not intended to use as of now outside of the standard library. If you really want, you can, although because the compiler must be able to implement the trait automatically, it cannot contain any associated item (methods, associated types, associated consts). If you remove the bar() method, this is how your code will look like:
#![feature(auto_traits, negative_impls)]
pub auto trait SomeTrait {}
#[derive(Debug)]
struct StructWithoutTrait {
_data: Box<i32>
}
impl !SomeTrait for StructWithoutTrait {}
#[derive(Debug)]
struct StructWithTrait {
_data: Box<i32>
}
fn foo<F, T>(f: F) -> T
where
F: FnOnce() -> T,
F: SomeTrait + 'static,
T: SomeTrait + 'static
{
f()
}
fn main() {
let without_trait = StructWithoutTrait { _data: Box::new(1) };
let with_trait = StructWithTrait { _data: Box::new(2) };
let x = std::rc::Rc::new(1);
foo(move || {
println!("{:?}", without_trait);
println!("{:?}", x);
with_trait
});
}
Playground.
I am pretty sure that is compiler behaviour:
A closure is Send if all variables captured by non-unique immutable reference are Sync, and all values captured by unique immutable or mutable reference, copy, or move are Send.
~ https://doc.rust-lang.org/reference/types/closure.html
It is therefore not something you can really implement right now with how closure types are completely untransparent
Currently Send is handled specially by the the compiler. Quoting from the documentation:
This trait is automatically implemented when the compiler determines it’s appropriate.
There are plans to move the implementation fully into the standard library (tracking issue), which should make it possible to have similar user-defined marker traits. There is a chance that when this feature gets stabilized you might be able to write:
auto trait SomeTrait {}
struct StructWithTrait {}
struct StructWithoutTrait {}
impl !SomeTrait for StructWithoutTrait {}
and get a behaviour similar to Send.

How to extend IntoIterator by another trait?

I'm trying to define a trait that extends IntoIterator, to achieve something similar to the code bellow, but "associated type defaults are unstable" (https://github.com/rust-lang/rust/issues/29661).
Is there another way to achieve it?
pub trait MyTrait : IntoIterator{
type Item = i32;
fn foo(&self);
}
pub fn run<M: MyTrait>(my : M){
my.foo();
for a in my {
println!("{}", a);
}
}
I think what you want is this:
trait MyTrait: IntoIterator<Item = i32> {
fn foo(&self);
}
This means: everything that implements your trait also implements IntoIterator where the Item is i32. Or put differently: all implementors of MyTrait can also be turned into an iterator over i32s.

Resources