Looking for "blanket" implementation of the method(s) for trait.
Let's say for a trait
pub trait A {
fn do_a(&self);
}
want to have boxed method that wraps with box, without introducing any additional traits:
fn boxed(self) -> Box<Self>;
I can have another trait to achieve that (playground)
pub trait A {
fn do_a(&self);
}
pub trait Boxed {
fn boxed(self) -> Box<Self>;
}
impl<T> Boxed for T
where
T: A,
{
fn boxed(self) -> Box<Self> {
Box::new(self)
}
}
However, new trait Boxed is required for that.
You can add boxed directly to A with a default implementation so that structs won't need to implement it themselves:
trait A {
fn do_a(&self);
fn boxed (self) -> Box<Self>
where Self: Sized
{
Box::new (self)
}
}
struct Foo{}
impl A for Foo {
fn do_a (&self) {
todo!();
}
// No need to redefine `boxed` here
}
fn main() {
let foo = Foo{};
let _object: Box<dyn A> = foo.boxed();
}
Playground
Related
In the following example, is there any way to access the comp_field within the implementation of BaseTrait for MyStruct?
pub trait CompositionTrait {
//methods
}
pub trait BaseTrait {
fn get_comp_field(&self) -> Box<dyn CompositionTrait>;
}
pub struct MyStruct {
pub comp_field: Box<dyn CompositionTrait>
}
impl MyStruct {
pub fn new(config: Config) -> Result<Self, Error> {
// here based on config, comp_field is assigned with different structs, all of which implements CompositionTrait.
}
}
impl BaseTrait for MyStruct {
fn get_comp_field(&self) -> Box<dyn CompositionTrait> {
self.comp_field // Error
}
}
The following error is presented for the current implementation:
cannot move out of `self.comp_field` which is behind a shared reference
move occurs because `self.comp_field` has type `std::boxed::Box<dyn BaseTrait::CompositionTrait>`, which does not implement the `Copy` trait
The eventual need for such implementation would be accessing the methods of comp_field in the functions which are based on BaseTrait.
The error occurs because you're trying to move the box out of the struct, but the struct is not mutable (and even if it was you would need to put something else in place of the box you're removing). Instead you should return a reference to the CompositionTrait:
pub trait CompositionTrait {
//methods
}
pub trait BaseTrait {
fn get_comp_field(&self) -> &dyn CompositionTrait;
}
pub struct MyStruct {
pub comp_field: Box<dyn CompositionTrait>
}
impl BaseTrait for MyStruct {
fn get_comp_field(&self) -> &dyn CompositionTrait {
&*self.comp_field // Or self.comp_field.as_ref()
}
}
Playground
How can I attempt something like the following in Rust?
The builder class is a trait object which returns another trait object (type erasure) where the implementation that is selected is defined by the specific object of the builder trait that we are using.
trait Builder {
// I want this trait to return a trait object
fn commits(&self) -> dyn Commit;
fn finish(&self);
}
trait Commit {
}
struct FooBuilder {
}
struct FooCommit {
}
impl Builder for FooBuilder {
fn commits(&self) -> impl Commit {
FooCommit{ }
}
fn finish(&self) {
}
}
fn get_commits(b: &Builder) {
// trait object returns a trait
let c = b.commits();
}
fn main() {
let b = FooBuilder{};
get_commits(&b);
b.finish();
}
There is no problem returning trait objects from trait methods in Rust:
trait Foo {
fn bar(&self) -> Box<dyn Bar>;
}
One thing to notice is that you need to return Box<dyn Bar>, not dyn Bar, since size of dyn Bar is not known at compiled time, which renders it useless.
When you implement this trait, the signature has to match, so it should return Box<dyn Bar>, not impl Bar:
impl Foo for MyFoo {
fn bar(&self) -> Box<dyn Bar> {
Box::new(MyBar{})
}
}
This code works fine (playground):
use std::rc::Rc;
trait Foo {
fn foo(&self);
}
struct Bar<T> {
v: Rc<T>,
}
impl<T> Bar<T> where
T: Foo {
fn new(rhs: Rc<T>) -> Bar<T> {
Bar{v: rhs}
}
}
struct Zzz {
}
impl Zzz {
fn new() -> Zzz {
Zzz{}
}
}
impl Foo for Zzz {
fn foo(&self) {
println!("Zzz foo");
}
}
fn make_foo() -> Rc<Foo> {
Rc::new(Zzz{})
}
fn main() {
let a = Bar::new(Rc::new(Zzz::new()));
a.v.as_ref().foo()
}
but if I make a wrapper to generate Rc like below, the compiler complains about missing sized trait (playground)
fn make_foo() -> Rc<dyn Foo> {
Rc::new(Zzz::new())
}
fn main() {
let a = Bar::new(make_foo());
a.v.as_ref().foo()
}
in both cases, Bar::new received parameters with same type Rc, why the rust compiler reacts different?
By default, all type variables are assumed to be Sized. For example, in the definition of the Bar struct, there is an implicit Sized constraint, like this:
struct Bar<T: Sized> {
v: Rc<T>,
}
The object dyn Foo cannot be Sized since each possible implementation of Foo could have a different size, so there isn't one size that can be chosen. But you are trying to instantiate a Bar<dyn Foo>.
The fix is to opt out of the Sized trait for T:
struct Bar<T: ?Sized> {
v: Rc<T>,
}
And also in the context of the implementations:
impl<T: ?Sized> Bar<T>
where
T: Foo
?Sized is actually not a constraint, but relaxing the existing Sized constraint, so that it is not required.
A consequence of opting out of Sized is that none of Bar's methods from that impl block can use T, except by reference.
I have a trait in which I want to provide a method. The method is to be implemented in terms of some helpers that have no business being inside the trait and are non-trivial enough that dynamic polymorphism makes more sense than making them generic. So I have code along the lines of
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self);
}
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
Which, however, does not compile, with error:
error[E0277]: the trait bound `Self: std::marker::Sized` is not satisfied
--> <anon>:9:19
|
9 | use_trait(self);
| ^^^^ the trait `std::marker::Sized` is not implemented for `Self`
|
= help: consider adding a `where Self: std::marker::Sized` bound
= note: required for the cast to the object type `Trait`
I understand why—it is not guaranteed somebody won't implement the trait for an unsized type (converting from &T where T: Trait to &Trait requires T: Sized, but the declaration does not require that).
However, the advice will not do what I need. I can add
fn needed(&self) -> &str where Self: Sized
but then the needed() method won't be accessible on &Trait (because Trait : ?Sized), which renders the thing useless, because the type (the actual one that does something useful) is always handled as Arc<Trait>. And adding
trait Trait: Sized
is even worse, because that does not permit &Trait at all (Trait as a type is unsized, so Trait type does not implement trait Trait).
Of course I can simply make
fn use_trait<T: Trait>(x: &T)
but there is a lot behind it in the real code, so I don't want monomorphisation there especially since the trait is otherwise always handled as trait object.
Is there any way to tell Rust that all types that impl Trait must be sized and here is a definition of a method that should work for all of them?
You need an additional as_trait function on Trait and its implementations:
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self.as_trait());
}
fn as_trait(&self) -> &Trait;
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn as_trait(&self) -> &Trait {
self as &Trait
}
}
You can try it on the playground. (trait objects)
Enhanced version of #JoshuaEntrekin's answer:
The helper as_trait function can be put in an auxiliary trait that gets blanket implementation for all Sized types trying to implement Trait. Then the implementer of Trait does not have to do anything special and the conversion works.
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait : AsTrait {
fn needed(&self) -> &str;
fn provided(&self) where Self : AsTrait {
use_trait(self.as_trait());
}
}
trait AsTrait {
fn as_trait(&self) -> &Trait;
}
impl<T : Trait + Sized> AsTrait for T {
fn as_trait(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
(on play).
It would also be possible to simply put provided in the auxiliary trait, but then it would have to dynamically dispatch to the other methods of Self unnecessarily.
Update: Actually, the point is that it should still be possible to override provided.
Now the above can be improved further by making it generic. There is std::makrer::Unsize, which is unstable at the time of this writing. We can't make
trait Trait : Unsize<Trait>
because Rust does not allow CRTP, but fortunately it is enough to put the constraint on the method. So
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) where Self: AsObj<Trait> {
use_trait(self.as_obj());
}
}
trait AsObj<Tr: ?Sized> {
fn as_obj(&self) -> &Trait;
}
// For &'a Type for Sized Type
impl<Type: Trait> AsObj<Trait> for Type {
fn as_obj(&self) -> &Trait { self }
}
// For trait objects
impl AsObj<Trait> for Trait {
fn as_obj(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn provided(&self) {
println!("Aber dieses Objekt sagt Grüß Gott, Welt!"); // pardon my German, it is rusty.
}
}
fn main() {
let s: &Trait = &Struct();
s.provided();
}
(on play)
This finally makes it transparent for the implementors of other versions.
See also this users thread.
I have a trait that is generic: trait Trait<T> and I want to create another trait that specifies the generics: type Alias = Trait<String>. This would allow impl Alias for T and not have to specify the type parameters. I tried a couple ways of doing this and haven't found any that works.
This is not a duplicate of Type alias for multiple traits or Aliasing trait with associated types because doing trait Alias: Trait<T> requires people to implement Trait<T> anyway. I want to offer a trait that hides the generics.
A clearer code sample:
trait DefaultEvents = Events<UserStruct, ChannelStruct, IrcStruct>;
struct MyHandler;
impl DefaultEvents for MyHandler {
...
}
Here's my best suggestion, it's going to mean a bit more work on your part (with lots of manual trait inheritance), but it should achieve the user convenience that you want.
pub mod user_friendly {
pub trait GivesNum<T> {
fn get_num(&self) -> T;
}
pub trait GivesDouble {
fn get_double(&self) -> f64;
}
impl<S> GivesNum<f64> for S where S: GivesDouble {
fn get_num(&self) -> f64 { self.get_double() }
}
}
// now your library's user needs to do less
use user_friendly::*;
struct MyStruct { num: f64 }
impl GivesDouble for MyStruct {
fn get_double(&self) -> f64 { 2.0 * self.num }
}
fn main() {
let s = MyStruct{ num: 5.0 };
println!("MyStruct.get_num() = {}", s.get_num());
}
Try it on Rust Playground