Rust on Haiku: the Case of the Disappearing Deceased Threads | Haiku Project

Summer! The time to slow down, relax, go to strange places, and do the projects that are long overdue. This summer I had the joy of spending my time in a lovely house near Lyon in France. In many ways the summer was like others, meaning there was plenty of wine and a lot of relaxing activities. At the same time, the Covid situation did give me a lot of reasons to scale back exploratory activities at the tourist hot spots, and instead focus on activities close to home. I decided to seize the opportunity and to see if I could dive into one of my long-standing pet peeves in the Haiku ecosystem.


This is a companion discussion topic for the original entry at https://www.haiku-os.org/blog/nielx/2020-09-06_rust_on_haiku_the_case_of_the_disappearing_deceased_threads/
8 Likes

Nice work! Iā€™m excited to have improved Rust support under Haiku. Iā€™m finding myself enjoying Rust and all of itā€™s quirks a lot more than C/C++ these days :slight_smile:

@nielx , iā€™ve been pushing on https://github.com/deprecrated/net2-rs/pull/103 to get it merged. A lot of rust code still depends on net2-rs :expressionless:

1 Like

Very interesting article, thanks for writing it up. I also like Rust and think it could be a very useful application language on Haiku. But we would really need a Rust version of the Interface Kit and probably other kits which might be a fair amount of work. But the Rust GUI story isnā€™t very good on most platforms so this could even be an area Haiku could stand out.

Also looks like that net2-rs PR got merged and a new version was released to Crates.io, so that helps.

I think there still is a major open question whether or not the Rust fundamentals are a good fit for a GUI model. There are several discussions in the blogs and on reddit about this. With all its flaws in many other applications, the OOP model with inheritance and complex hierarchies with crosslinks between the objects seem to be a good fit for the GUI model.

This does not mean it cannot be done in Rust, but it does mean that at points one may fight against the language. What makes the situation for Haiku more complex is that the app_server and the application mirror the view hierarchy, which makes it more difficult to make a more Rust-like framework for the app developer.

Having said that, there are alternatives to solving the problem. The Haiku-Rust API could just be a data-driven UI framework that works on top of the current C++ framework. You could compare this to how React is a data-driven framework on top of the OOP-oriented DOM.

An interesting Rust-first implementation of a UI toolkit is druid.

1 Like

Yeah I have been following some of Raph Levienā€™s work and his recent discussions on GUI in Rust. I definitely agree with your thoughts.

I do like the React model a lot, and Iā€™ve used the ReasonML (alternative OCaml grammar) framework Revery a bit when helping on the editor Onivim and I found it pretty nice. I donā€™t think they are using too much OO style code in that. To be honest I would not mind getting OCaml/ReasonML and that Revery framework going on Haiku. It uses SDL and Skia so in theory it should be possible, just obviously without any GPL acceleration.

But there are a lot of other things I would want to work on first and my time is limited these days.

Either way I really appreciate your work on the Haiku Rust port and it sounds like Haiku is close to being a first class platform for Rust?

Since Iā€™m too old to think of a graphic interface in any other terms than the Be/Haiku Interface Kit ā€¦ what I see working with Rust is (as usual) leveraging traits to fill in for OOP. e.g.,

struct ExampleApp {
    app: BApplication,
    win: ExampleWin
}
impl ExampleApp {
     fn new() -> ExampleApp {
         let example = ExampleApp { app: BApplication::new("application/x-test"), ... };
         ... // setup macro installs BApplication callbacks on example from example.app
         ... // setup macro installs BHandler callbacks on example from example.app
         example
     }
}
#[be_dispatch(BApplication)]
impl BApplication_dispatch for ExampleApp {
     fn ReadyToRun(&mut self) {
         ... // create and Show window
     }
}
#[be_dispatch(BApplication)]  // following impl creates hooks from a C++ BApplication
impl BHandler_dispatch for ExampleApp {
      fn MessageReceived(&self, msg: &BMessage) {
          ...
      }
}

Iā€™ve omitted some boxing and so forth (Iā€™m weak on whatā€™s good practice in this incredibly baroque language.) The point is that app::BApplication 1) constructs a C++ BApplication, and 2) provides the API functions, and 3) can be coupled to your struct, in lieu of a subclass, that can implement its callback traits.

Unfortunately, modelling the Haiku API into Rust Traits is not really where the challenge is. Instead you have to look at how the inheritance model couples various layers of functionality, and how that goes against the strict ownership and (to a lesser extend) lifetime rules in Rust.

To look at your example, you implement the BHandler trait for your ExampleApp, but then how will this be added to a Looper? In your example the ExampleApp owns the BApplication, but in order for BApplication to call back the handler, it needs to know about the ExampleApp.

And thereā€™s a whole range of issues like this, especially once you get to Views, and how a lot of how it works really depends on inheritance.

I am not saying it cannot be solved, but it is a puzzle if one of your goals is to make it fit well within the Rust idiom.

Right. The C++ BApplication subclass stores ExampleAppā€™s address, and the Rust callback functions.

Itā€™s just manual. impl BView_inherit for BBox {...}, impl BHandler_inherit for BBox {...}. (ā€œManualā€ in the sense of ā€œno magicā€ - usable library would have to be generated from templates.) But at a proof of concept level, it seems to work fine ā€“

let (pt, _) = self.example_box.box.GetMouse(None);  // BView:GetMouse()
...
let name = self.example_box.box.Name(); // BHandler::Name()

Whether it fits well enough, I guess depends on how desperate you are. The ā€œhas aā€ form is more cumbersome than C++ ā€œis aā€. Thereā€™s no way to get C++ automatic type casts on parameters, so you have to do it yourself, e.g. box.AddChild(&Bview::from(&example.button), None).

So far I havenā€™t tangled with lifetimes particularly. The API manages some of that, so the Rust layer must not for example delete child views. Lifetimes for things like BMessage are kind of historic sore point with the Be API, but hopefully wonā€™t be unbearable once figured out.

And there you have to make the design choices, because pointers (i.e. just storing an address) is something you cannot ā€˜justā€™ do in Rust. I.e. you will get borrowing errors if you have to pass a mutable reference of the ExampleApp object back into the BApplication.

See this StackOverflow that illustrates the problem in a different scenario.

Itā€™s done, so I guess the question would be something like ā€œwhat could go wrong?ā€ So far, itā€™s just a couple of windows, each with a box and a button on the box, but it all seems to dispatch OK.

Something like let p: *mut c_void = (self as *mut Self) as *mut c_void;
(ā€¦ I hope that didnā€™t ruin anyoneā€™s lunch!)

I think dev.haiku-os.org contains a database full of crashes, deadlocks and data race issues where improper locking and stray pointers were the cause, so that could go wrong.

In any case, you donā€™t have to convince me that it can be done.

Itā€™s certainly intriguing to think about alternatives.