Reference to trait objects and lifetime issues for GUI code

Hello everyone! I recently joined the Rustacean club and I'm really excited to learn Rust. Currently I'm still trying to understand some of its intricacies.

I'm currently trying to understand a Rust way of doing something similar to the OOP equivalent of "abstract function" implementation in the context of Win32 GUI drawing by using Trait objects.

I've already gone through Using Trait Objects That Allow for Values of Different Types - The Rust Programming Language, but in my case I don't think it's possible for me to move the ownership of my_wnd: MyWindow into the Window itself, but maybe I'm wrong. So here I am asking for your help with the lifetime fun!

So I'm having some trouble solving this reference lifetime issue, and this is the problematic line: my_wnd.window.set_painter(&my_wnd);
I tried experimenting with Boxed types too and I'm pretty sure there's probably a way of avoiding or solving this problem, but I'm not sure how to solve this.

Here's a rough idea of what I'm trying to do:

// window.rs
const WM_PAINT: u32 = 15u32;

trait Paintable {
    fn paint(&self) -> ();
}

struct Window {
    // Using 'static only to avoid using lifetime generics to keep the example simple enough
    painter: Option<&'static dyn Paintable>,
}

impl Window {
    fn message_handler(&mut self, message: u32) {
        match message {
            WM_PAINT => { self.paint(); },
            _ => {}
        }
    }
    
    fn set_painter(&mut self, painter: &'static impl Paintable) {
        self.painter = Some(painter);
    }
    
    fn paint(&self) {
        if let Some(painter) = self.painter {
            painter.paint();
        } else {
            println!("Painter not implemented");
        }
    }
}


// my-window.rs
struct MyWindow {
    window: Window,
    data: u32,
}

impl MyWindow {
    fn new() -> Self {
        let mut my_wnd = Self {
            window: Window { painter: None },
            data: 42,
        };

        // I need to tell Window to use MyWindow impl for Paintable
        // This is obviously WRONG, but you get the idea!
        my_wnd.window.set_painter(&my_wnd);

        my_wnd
    }
}

impl Paintable for MyWindow {
    fn paint(&self) {
        println!("Hello MyWindow with data {}", self.data);
    }
}


fn main() {
    let mut my_wnd = MyWindow::new();
    
    // This is triggered from some other place
    my_wnd.window.message_handler(WM_PAINT);
    
    // I want to tell MyWindow to use its paint() when Window makes a paint, and
    // that would result in the following output: "Hello MyWindow with data 42"
}

(Playground)

Error detail:

error[E0502]: cannot borrow `my_wnd.window` as mutable because it is also borrowed as immutable
  --> src/main.rs:50:9

So I ended up asking for some help on the Rust Community Discord server and with some amazing help we found a way around this.

The idea is to make Window generic over the Paintable trait and then implement the trait using a separate struct for the data in order to break the cyclic reference.

Is this actually the best way to model this?

Here's the code I ended up with:

// window.rs
const WM_PAINT: u32 = 15u32;

trait Paintable {
    fn paint(&self);
}

struct Window<P: Paintable> {
    painter: P,
}

impl<P: Paintable> Window<P> {
    fn new(painter: P) -> Self {
        Self { painter }
    }
}

impl<P: Paintable> Window<P> {
    fn message_handler(&mut self, message: u32) {
        match message {
            WM_PAINT => { self.on_paint(); },
            _ => {}
        }
    }
    
    fn on_paint(&self) {
        self.painter.paint();
    }
}


// my-window.rs
struct MyPainter {
    data: u32,
}
impl Paintable for MyPainter {
    fn paint(&self) {
        println!("Hello MyWindow with data {}", self.data);
    }
}

struct MyWindow {
    window: Window<MyPainter>,
}

impl MyWindow {
    fn new() -> Self {
        let painter = MyPainter { data: 42 };
        let window = Window::new(painter);
        Self { window }
    }
}


fn main() {
    let mut my_wnd = MyWindow::new();
    
    // This is triggered from some other place
    my_wnd.window.message_handler(WM_PAINT);
    
    // I want to tell MyWindow to use its paint() when Window makes a paint, and
    // that would result in the following output: "Hello MyWindow with data 42"
}