Need help on the compiler errors when trying to make a observer pattern in rust

Hey there!

I'm just moving from Nodejs to Rust. Currently I'm trying to code the observer pattern in Rust.
In my old post, I messed up the code and make the post difficult to understand. Now I just figured out how to use rust playground and put all my code here:
Rust Playground (rust-lang.org)

If you run the code, you will see the error like:

error[E0782]: trait objects must include the `dyn` keyword
  --> src/main.rs:41:29
   |
41 |     resouce_a: Resource<'a, IDataHandler<Item=TestItemA>>,
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
help: add `dyn` keyword before this trait
   |
41 |     resouce_a: Resource<'a, dyn IDataHandler<Item=TestItemA>>,
   |                             +++
error[E0277]: the size for values of type `(dyn IDataHandler<Item = TestItemA> + 'static)` cannot be known at compilation time
  --> src/main.rs:41:16
   |
41 |     resouce_a: Resource<'a, IDataHandler<Item=TestItemA>>,
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `(dyn IDataHandler<Item = TestItemA> + 'static)`
note: required by a bound in `Resource`
  --> src/main.rs:1:21
   |
1  | struct Resource<'a, T: IDataHandler> {
   |                     ^ required by this bound in `Resource`
help: consider relaxing the implicit `Sized` restriction
   |
1  | struct Resource<'a, T: IDataHandler + ?Sized> {
   |                                     ++++++++

The first error lets me to use dyn in front of the trait object.
After adding dyn that, the error got resolved, but the second error was still there.
I know Rust could use BOX to get rid of the "sized" issue in dyn, but I was not able to make it work..

Please help if you have any ideas, many thanks !

References are a wrong tool for this. Refererences are temporary, bound to a single scope they can never escape, and this limitation infects everything they touch. References in structs are always almost a mistake (except for special cases like views/reprojections/guards).

You have to use Arc, and in practice Arc<Mutex<T>>. Arc is a non-temporary shared reference, and objects "by reference" in languages like JS/Java/C# are equivalent to Arc, not &.

3 Likes

What was the problem that you ran into when you tried this? Wrapping in a Box is a standard way to turn an unsized type to a sized one.

From the way you name the traits and the structs, I'd say you have a C# background as well.
Now, what that means is that you're probably used to an OOP stance towards problems. But Rust explicitly is not object-oriented. In particular, design patterns such as Observer pattern were created for OOP systems (the Gang of Four book is officially known as " Design Patterns: Elements of Reusable Object-Oriented Software". Note the "object-oriented"). So many patterns from the GOF book are either not applicable to Rust or are simply difficult to use.

What are you trying to code up? The observer pattern is only the means, not the end. What are you trying to achieve with the Observer pattern?

1 Like

Other observations:

  • If you want to be agnostic over the implementers of a trait, you should probably design around dyn Trait from the start, versus mixing dyn Trait with generic T: Trait bounds (on e.g. Resource or in the IObservable trait).
    • You will avoid some headaches around things like "Box<dyn Trait> does not implement Trait".
    • You will gain some new headaches like "comparing two Box<dyn Trait> for equality is not trivial to do correctly". (I say "gain" but you would have had to deal with it anyway.)
    • You can be generic over Item instead
  • Ownership is a core tenant of Rust, so you have to be explicit about giving up ownership, or making a clone, or being able to make a copy (cheap bit-wise clone), etc. Types that implement Copy basically declare "ownership doesn't matter (and I'm cheap to clone)".
    • In particular, your Item type is going to need to be Copy or at least Clone so you can pass it everywhere. If this is onerous for Item itself, options include passing a shared reference (which is Copy), or perhaps it's another case for an Arc (which you can clone).
1 Like

Thanks Kornel! I think I kind of understand what you mean here, I will look in the Arc pointer, I just followed by the hint from vscode which then added "dyn" for me .. I updated my code in the rust playground it will be much easier to read now, please check if you are interested..

Hi @RedDocMD , thanks for your reply!
I created a link which has all my example code. Basically I just try to do something like subscribing the change and do something.
I updated the code in the code, please check if you have time.. thanks!

(post deleted by author)

Thanks @quinedot !
I think I need more time to understand all the concept you and others mentioned, for example dyn Trait, arc.. I'm using dyn in my code because vscode let me do so.. I was not truly understand what's going on.

I just updated my code in the rust playground, if you are interested, please take a look.. thanks!

Rust Playground links are snapshots — editing isn't automatically shared. You'll need to share a new link to show us new code.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.