2

I have an existing C# application, but some parts of it are poor in performance for a number of reasons. I want to rewrite those parts in Rust and interact with my new native library using P/Invoke. However, the parts of my code that I'm wondering about are the parts that made use of INotifyPropertyChanged or Observable<T>.

For reference, INotifyPropertyChanged is a pattern where the class that implements that interface (trait) will fire an event (property "y" on object x has changed) every time one of the class's properties is changed, regardless of what caused the change (the typical exception is for properties which affect each others' values, in order to avoid a stack overflow).

For my particular use case, I am trying to keep track of a tree structure that exists in my Rust code from C#. It would be ideal if these change notifications had a way of bubbling up to the root of the tree (i.e., if x.y[0].z changes, then y[0] fires an event, which is seen by x, and x fires an event).

Is there a recommended pattern for finding out when a property of an object is changed in Rust? For example, is there a way to know when a property of a struct is borrowed and fire a callback?

I've seen How can I implement the observer pattern in Rust?, but the accepted answer doesn't fully address what I'm wondering.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
laptou
  • 6,389
  • 2
  • 28
  • 59
  • so you are concerned about perf, but you still want to use something with a lot of overhead ? – Stargateur Jan 07 '19 at 07:11
  • Please pardon me for being naive, but what specifically are you referring to? @Stargateur – laptou Jan 07 '19 at 07:46
  • The idea is to rewrite the core functionality in Rust and have everything be managed there ... obviously making hundreds of P/Invoke calls per second won't be much better than what I started with – laptou Jan 07 '19 at 07:47
  • So the reason I want Observables is so that my C# code doesn't have to poll for changes in the state of the Rust core – laptou Jan 07 '19 at 07:48
  • @laptou: How do you handle multiple consecutive changes to different fields of a same struct? For example, what do you expect of `a.x = 3; a.y = 4;`: one event? two events? And what of `a = b;`, one event? Or one event per field of `a`? We don't know `INotifyPropertyChanged`, so please detail the behavior that are you looking for. – Matthieu M. Jan 07 '19 at 07:55
  • .NET's `INotifyPropertyChanged` fires an event every time a property on the class is changed, regardless of what prompted the change or its proximity to other changes. I could later bundle these events based on timing for efficiency, but for now I just want something rudimentary. @MatthieuM. – laptou Jan 07 '19 at 07:59
  • @laptou: Okay, then can you enlighten us on what the nature of these events are. Is this "One field change" or "The field (x) changed" or "The field (x) changed from (3) to (5)"? Do you need the address of the object for which the field changed or another ID? Also, please edit your questions with these details, so that new readers don't have to tread through the comments. – Matthieu M. Jan 07 '19 at 08:20
  • @MatthieuM. I apologise for my vagueness. I have updated the question – laptou Jan 07 '19 at 08:30
  • @laptou: No worries, we'll work it out together :) – Matthieu M. Jan 07 '19 at 09:43
  • @laptou: What happens for nested modifications; such as `a.x.foo = 3;` when `a` is being observed: is `a.x` implicitly observed (in which case it has its own notification), is a notification sent that `a.x` was modified, or is nothing sent? – Matthieu M. Jan 07 '19 at 10:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/186312/discussion-between-laptou-and-matthieu-m). – laptou Jan 07 '19 at 16:17

1 Answers1

2

Is there a recommended pattern for finding out when a property of an object is changed in Rust?

No, there is nothing like this in the Rust language. As far as I know, it's completely impossible to tell when a field changes.

You will need to write methods around the properties. These methods can perform whatever side effects you need when updating a value:

trait Observer {
    fn count_decreased(&mut self, example: &Example);
}

struct Example {
    name: String,
    count: u32,
}

impl Example {
    fn decrease_count(&mut self, observer: Option<impl Observer>) {
        self.count -= 1;
        if let Some(mut observer) = observer {
            observer.count_decreased(self);
        }
    }
}

It seems very plausible that a procedural macro could be written to automatically generate some or all aspects of this.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 4
    It's worth pointing out that there's also no way to find out when a field changes in .Net. The way `INotifyPropertyChanged` works is by using C# properties which are just syntax sugar for getter/setter methods. The coder is responsible for raising the property changed event when a setter is called (or using a codegen tool to do so). There's nothing inherently special about `INotifiyPropertyChanged` and the whole thing could probably be replicated in Rust 1:1 just without the syntax sugar of properties. – Wesley Wiser Jan 07 '19 at 16:58
  • 1
    It's also worth pointing out that the observer pattern (or more generally simple event subscription) is somewhat tricky to implement in Rust due to it usually involving long-lived references to some objects. – Sebastian Redl Jan 07 '19 at 18:18
  • @SebastianRedl I figured that was well-covered by the [accepted answer on the question the OP linked to](https://stackoverflow.com/a/37573345/155423). You'll note I avoided that whole problem by requiring the observer to be passed in to the function call. – Shepmaster Jan 07 '19 at 18:29