Live data from Hacker News

Giving up on wlroots-rs

way-cooler.org

61–70 of 126 posts

Re: Giving up on wlroots-rs

#61
This code

    fn some_wlroots_callback(output_handle: OutputHandle,
                             surface_handle: SurfaceHandle) {
        output_handle.run(|output| {
            surface_handle.run(|surface| {
                // maybe some more nested layers...
            }).unwrap()
        }).unwrap()
    }
is just screaming continuation monad. You see the same code pattern in early Node.js code (sometimes leading to callback hell). In the JavaScript world, the problem was solved using Promises, and then async/await syntax. But more fundamentally, this is an instance of the continuation monad at work.

The continuation monad transformer is defined as

    newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }
and is nothing more than just a function that takes a callback. If this were Haskell, one could just write

    stuff = runContT $ do
      output 
Granted, continuation code can easily be misused to produce an incomprehensible mess in Haskell (its full generality can be compared with goto), but with Rust's FnOnce trait, the scope for misuse is considerably reduced.

Re: Giving up on wlroots-rs

#62
post #20

Earlier quoted context omitted.

Quick answer, without understanding all the details: a weak-pointer-like structure that performed all the necessary locking and checking before giving out access to the underlying SHM. Rust's borrow checking is more or less a formalization of C++'s RAII style; in my experience, solutions for that translate relatively simply.

Rust's borrow checking does not correspond to RAII. A simplified explanation is that borrow checking gives you the guarantee that mutable references are unique and immutable references do not change. Rust's lifetime system is vaguely like RAII, but the C++ type system gives you no way to create an object with lifetimes that don't correspond to some scope. In Rust this is done by moving values, but in C++ this is not…

> C++ lets you have as many iterators as you like into the same container

While it's true that Rust doesn't let you have multiple mutable iterators, it's worth pointing out that you can certainly have multiple immutable iterators.

    let items = &[42, 101];

    // All products (a*a, a*b, b*a, b*b)
    for x in items {
      for y in items {
        println!("{}", x * y);
      }
    }

    println!("");

    // Pairwise products (a*a, b*b)
    let pairs = Iterator::zip(items.iter(), items.iter());
    let pointwise_products = pairs.map(|(x, y)| x * y);
    for product in pointwise_products {
      println!("{}", product);
    }

Re: Giving up on wlroots-rs

#63

A good post, thanks for sharing. > I want to make one (mildly controversial) thing clear: rewriting a library for the sake of only using Rust is not good engineering. Strong agree. > A literal rewrite of a project to Rust is not interesting, it’s not useful, it just causes churn and splits ecosystems. Time would be better spent either working with existing solutions that already have the effort put in to make them co…

I'm surprised nobody mentioned this yet- a rust rewrite means you are more likely to invite contributions from a larger community.

Re: Giving up on wlroots-rs

#64
post #61

This code fn some_wlroots_callback(output_handle: OutputHandle, surface_handle: SurfaceHandle) { output_handle.run(|output| { surface_handle.run(|surface| { // maybe some more nested layers... }).unwrap() }).unwrap() } is just screaming continuation monad. You see the same code pattern in early Node.js code (sometimes leading to callback hell). In the JavaScript world, the problem was solved using Promises, and then…

JavaScript and Haskell get to enjoy the productivity of using a tracing GC though.

Re: Giving up on wlroots-rs

#65
Personally I think Rust is a great language. That said it may not be great for everyone nor a great fit for every problem.

Sometimes trying and admitting failure is a perfectly rational and valid option.

Re: Giving up on wlroots-rs

#66
post #47

A pity wlroots itself isn't written in Rust.

C is the lingra franca of programming. By writing wlroots in C, we make it easy for a half dozen projects to make bindings to other programming languages. Rust is the only one that seems to have failed, and it's not surprising given the constraints of the language. wlroots brings together over a dozen different libraries and interfaces which are implemented in C. You'd have to repeat this process a dozen times over,…

C is the lingua franca of UNIX based platforms.

Thankfully not all OSes bow to it.

Re: Giving up on wlroots-rs

#67
post #66

Earlier quoted context omitted.

C is the lingra franca of programming. By writing wlroots in C, we make it easy for a half dozen projects to make bindings to other programming languages. Rust is the only one that seems to have failed, and it's not surprising given the constraints of the language. wlroots brings together over a dozen different libraries and interfaces which are implemented in C. You'd have to repeat this process a dozen times over,…

C is the lingua franca of UNIX based platforms. Thankfully not all OSes bow to it.

>Thankfully not all OSes bow to it.

Right, just the successful ones.

Re: Giving up on wlroots-rs

#68
post #62

Earlier quoted context omitted.

Rust's borrow checking does not correspond to RAII. A simplified explanation is that borrow checking gives you the guarantee that mutable references are unique and immutable references do not change. Rust's lifetime system is vaguely like RAII, but the C++ type system gives you no way to create an object with lifetimes that don't correspond to some scope. In Rust this is done by moving values, but in C++ this is not…

> C++ lets you have as many iterators as you like into the same container While it's true that Rust doesn't let you have multiple mutable iterators, it's worth pointing out that you can certainly have multiple immutable iterators. let items = &[42, 101]; // All products (a*a, a*b, b*a, b*b) for x in items { for y in items { println!("{}", x * y); } } println!(""); // Pairwise products (a*a, b*b) let pairs = Iterator:…

That's really not what I'm talking about. In your example you are using multiple iterators to iterate over the same structure multiple times, but in C++ you can use pairs of iterators to represent ranges. For example, I can have three iterators i, j, and k, which represent two ranges: i..j and j..k.

Even if i, j, k are const iterators, it's not pleasant to translate this to Rust. So it's not an issue of whether Rust lets you have multiple iterators or not, the issue is that Rust iterators are strictly less expressive than C++ iterators.

And that's okay. It's a tradeoff.

For more information about how iterators are used in C++, I would refer to the portion of the standard library. https://en.cppreference.com/w/cpp/algorithm

Re: Giving up on wlroots-rs

#69
post #63

A good post, thanks for sharing. > I want to make one (mildly controversial) thing clear: rewriting a library for the sake of only using Rust is not good engineering. Strong agree. > A literal rewrite of a project to Rust is not interesting, it’s not useful, it just causes churn and splits ecosystems. Time would be better spent either working with existing solutions that already have the effort put in to make them co…

I'm surprised nobody mentioned this yet- a rust rewrite means you are more likely to invite contributions from a larger community.

> rust rewrite means you are more likely to invite contributions from a larger community

I'd be really interested to know why you think that's the case. Somewhere around 3% of programmers know Rust, while over 20% know C. https://insights.stackoverflow.com/survey/2019#technology

Naively, I would have thought that a C project would invite contributions from a much larger community. Sure (as I've personally experienced!) the Rust community is friendly and helpful, but we're still small, so I'm curious why you think a rewrite would get more contributions.

Re: Giving up on wlroots-rs

#70
post #49

Earlier quoted context omitted.

Author here. The problem with that design (which is a great design given what I presented in the article by the way!) is that it doesn't allow you to share handles across callbacks, which is mandatory to do anything interesting. I'm assuming here that you can't use the handles except for that callback context. If you can, then that presents a different problem. https://play.rust-lang.org/?version=stable&mode=debug&ed…

The intent is that handles can be used across contexts (as long as both contexts are from the same instance of wlroots) - the context checks that the handle is valid before unwrapping it. Could you clarify what you mean by it presenting a different problem? With regards to the leaking issue - that's not actually correct. `Box::leak` does not give you access to the context with the `'static` lifetime, assuming the lif…

Your edit to your original comment was important information I was missing - you are correct that that would probably work.

With how wlroots-rs is setup it does require the upgraded resources to not be leaked because their drop impl decrements the reference count.

Regardless, this was obviously only one part of the problem I was running into. As well, as can be seen, it's very complicated trying to ensure this is all sound and it's no longer worth the headache.

Post reply on HN