
Giving up on wlroots-rs - Bl4ckb0ne
http://way-cooler.org/blog/2019/04/29/rewriting-way-cooler-in-c.html
======
steveklabnik
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 correct or to come up with new green-field projects.

This... I'm not so sure about. It really depends on what your objective is.
For example, if your goal is to learn, you're not going to cause churn, and
you're not going to split ecosystems. Working on project you already know well
is a good way to learn, because you can focus on the language, not the
project.

This also isn't _exactly_ a re-write, in my mind. I mean it is, and it isn't.
This is because...

> The biggest problem when wrapping wlroots was defining the ownership model
> of the objects that wlroots exposes.

The pain here isn't a re-write, it's an integration with an existing system.
That's a good reason to not use something different! I also think this is
interesting because it demonstrates something that's often said in discussions
about Rust, but mostly in the abstract, and that's that Rust's rules influence
the design of your system. It will guide you away from designs where the
ownership of components is unclear. To many people, this is a benefit, but it
can often cause struggles when learning. And, it can often cause struggles in
situations like this: where you _can 't_ really re-write some external
component to fit in the rules.

That being said, there should be a way to do the ownership part that makes
sense, but I don't know wlroots well enough to comment.

 _That_ being said

> Currently there is 11 THOUSAND lines of Rust in wlroots-rs. All of this code
> is just wrapper code, it doesn’t do anything but memory management.

is also super legit. Managing this kind of thing is a pain. I can certainly
understand not wanting to do it.

~~~
ChuckMcM
I agree and was going to say the same. That there was a huge challenge in
writing code to manage memory ownership, is not surprising because memory
ownership is so fluid and adhoc in C and its derivatives. And when you are
asked to make that ownership explicit, if the original designers hadn't been
thinking about it, you get a lot of cases which they would not have
considered.

I would like to see a compositor written in Rust, I think it would make for a
robust window system. I also know that starting from the position of "this
will be written in Rust" would force the issue of memory ownership to the fore
and result in a different architecture from the start.

~~~
ajross
Not a wayland, wlroots or rust expert, but dollars to donuts says that it's
not "memory" management[1] at issue, but resource allocation on the other side
of the graphics driver. Vertex arrays, textures, framebuffers, shaders et. al.
all need some kind of allocation strategy, can in the modern world often be
shared between process contexts, and they really don't fit well into the
metaphors of either C or Rust.

But where in C you can just do it anyway, Rust is likely to flip out over your
wrapper object abstractions.

[1] I mean sure, technically it is memory management, but you know what I
mean.

~~~
klodolph
This is also something that causes some pain when using C++ with RAII.
Logically it makes some amount of sense that you have e.g. a C++ wrapper for a
texture in OpenGL, and it knows to call glDeleteTextures in its destructor.

 _HOWEVER,_ the destructor can now only be called if the OpenGL context is
active, and the texture may be deleted if the context is lost (which can
happen). At some point anybody who's tried to wrap an OpenGL texture with a
C++ class either decides to accept that RAII doesn't completely work here, or
refactors it so that you manage something like handles to textures, which is a
bit silly because you are really at that point managing handles _to handles_
to textures.

~~~
ska
re: the handles to handles stuff ... I don't think that's so silly really,
when you are talking about a resource that in some fundamental sense belongs
to a different system (in this case OpenGL). Sure, that system gave you a
handle, but if the semantics of managing that handle aren't 1:1 matched with
your languages model of things, another level of indirection is a clean way to
handle it.

~~~
anyfoo
And just think about what most file and I/O wrappers, that are part of the
standard library in most higher level languages, e.g. file objects in python,
streams in C++ etc., naturally do: They also just wrap (for example) POSIX
file descriptors, which are handles, with their own handles, i.e. the file
object/Stream/whatever.

~~~
klodolph
Maybe I didn't explain what's happening in OpenGL properly, because that
comparison doesn't work at all.

With e.g. std::ofstream, you are wrapping a handle, such as a POSIX file
descriptor. So that part of your description is accurate.

However, this is different from what happens with OpenGL. The problem is that
if you wrap an OpenGL texture (which is a handle), you end up with some
problems because you can only free it with the correct context active, and it
might become free for other reasons besides the wrapper being destroyed. So
you are no longer wrapping a handle (like std::ofstream), but you are actually
creating a new handle-to-handle, and wrapping that, and explicitly managing
the lifetime OpenGL resources with a separate object somewhere else.

Of course, you could just wrap an OpenGL texture, explicitly decide not to
handle context invalidation, and then be careful to ensure that your objects
get destroyed with the OpenGL context active. You lose some flexibility and
you have to babysit RAII to be sure it "does the right thing".

So what I'm saying here is that OpenGL textures are not like file handles.

~~~
ska
They don't behave like file handles - but at a certain distance if you squint
at it looks like the same problem. The real issue isn't that OpenGL textures
don't behave like file handles, but that neither of them behave quite like
objects in your language, which can lead to problems. The specifics of how the
behavior differs is I think less important that the fact that it differs. Of
course, file handles are not the best example because the language designers
often had to put some thought into it early on; less true of something like
OpenGL textures.

~~~
klodolph
> …but at a certain distance if you squint at it looks like the same problem…

The whole discussion here is about how these things differ in subtle ways. The
problem with “squinting” at the problem is you end up e.g. using RAII and then
having to redesign your system because RAII doesn’t match, and the problems
weren’t obvious when you started out.

Not to make too fine a point about it here, but file handles are perfect
matches for RAII / Rust lifetimes, unlike OpenGL textures. You just close the
handle at end of object lifetime. You can get errors when you close but
current recommendation is to ignore errors when cleaning up and have a
separate path for closing/committing data on close when writing, so on most
paths the destructor will nop. This works well.

An example of a mismatch with the language semantics and file handles is with
GC. If you close a file handle in a C++ destructor or Rust drop(), it’s fine,
those are run deterministically. If you use a JVM finalizer to close your file
handle that might not happen soon enough (cue EMFILE).

Again, the reason why textures don’t work this way is because you have to
release them in a certain context and they might be released outside your
control.

~~~
adrianratnapala
> Not to make too fine a point about it here, but file handles are perfect
> matches for RAII / Rust lifetimes, unlike OpenGL textures.

Agreed, something like an FD matches RAII very well, but you can open one file
multiple times, dup fds and multiple processes can do all this. So the FDs end
up mapping in some non-trivial, probably refcounted way to other objects in
the kernel.

So it seems with OpenGL, this kind of mapping / bookkeeping layer gets hoisted
up into the application framework -- which might be a reasonable choice.

~~~
klodolph
OpenGL textures point to some refcounted thing, but the handle table is not
per-process but per-context, and the contexts are not thread-safe, and the
contexts can become invalidated. So it is similar in many ways but there are
too many differences in general.

------
quietbritishjim
I know very little about Rust or Wayland (I suppose I'm not the target
audience) but I got lost very quickly here:

> A Wayland “output” is the resource that represents a display device.
> Commonly this means it handles a computer monitor. This resource could
> disappear at any time in the life cycle of the application. This is easy
> enough to imagine: all it takes is a yank of the display’s power cord and
> the monitor goes away.

Surely even if a physical monitor is connected from a computer, the object
representing it doesn't instantly go away? If it literally got freed as soon
as the user disconnected the monitor then any access to such an object would
be dangerous as you could be accessing an object after it's freed, or even
another valid display object that got allocated into that space in the mean
time.

Instead, I would expect an object in that situation to go into some error
state, and even that might only be picked up when you perform certain
operations. If that were the case, I don't really see how Rust lifetimes are a
problem.

Since this was the main summary of the problem for laypeople like me, it made
the rest of the article quite hard to follow.

~~~
scatters
I think you missed this part:

> [the resource handle] can only be dropped between event callbacks,
> wlroots/Wayland is callback based

So "at any time" means between any two user callbacks, but not during them.

Error states really aren't a solution IMO, as they tend to pollute the entire
interface in that methods that would be expected to work unconditionally can
now fail or panic. Either you have to check each method invocation (entirely
unnecessarily, since if a resource isn't in an error state at the start of
your callback it won't go into one during it) or you make the absence of error
state a precondition, in which case you're guaranteed to miss one or two cases
and panic when the user yanks out the cord.

~~~
quietbritishjim
> > [the resource handle] can only be dropped between event callbacks,
> wlroots/Wayland is callback based

Thanks, that certainly makes a hell of a lot more sense.

------
Diggsey
I can't speak to every issue which the author might have encountered, but
there is a better solution to the lifetime management problem than the two
mentioned in the article.

Instead of this:

    
    
        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()
        }
    

One can do this:

    
    
        fn some_wlroots_callback(
            ctx: CallbackContext,
            output_handle: OutputHandle,
            surface_handle: SurfaceHandle
        ) {
            let output = ctx.get(output_handle);
            let surface = ctx.get(surface_handle);
        }
    

This is safe, because the lifetime of "output" and "surface" can be bound to
the lifetime of the "ctx" (whose lifetime is controlled by the library: the
library simply has to make sure that "ctx" is not accessible outside of a
callback).

edit: Realised you can't tell that there's an implicit lifetime in
`CallbackContext` here:

    
    
        struct CallbackContext<'a> { ... };
    

OR

    
    
        type CallbackContext<'a> = &'a CallbackContextImpl;

~~~
timidger
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&editio...](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2018&gist=1a010f34fdb6886d41145eecc2963280)

If you can own a context, even with a lifetime parameter it's possible to leak
it using the Box api. That allows you to have access to a &'static
CallbackContext. This will break that assumption that it only lives as long as
the callback itself.

~~~
Diggsey
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 lifetime of the context is not 'static (that would fundamentally break
rust's safety guarantees).

The only potential problem is that the destructor for the context may not run
(cf. leakpocalypse). If the implementation requires the destructor to run,
then you are right, you should never give ownership of the context object, it
should be passed into the callback by reference. If you need access to a
context outside of a callback, then you should use the callback-style:

    
    
        with_context(|ctx| {
            ... do stuff ...
        }
    

The nice thing is that `with_context` can enforce any threading constraints
you might have (like contexts being accessed from a single thread), can ensure
that the context is safely dropped at the end, and it's only required once -
once you have a context you can use it as many times as you need.

~~~
timidger
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.

------
ChrisRR
Interesting to see someone's reasons for moving back from rust to C. Normally
it's the other way round.

Speaking as a C developer who has dabbled in Rust, It's always good to see the
thoughts of someone who has spent a fair amount of time with the language who
can give it a fair assessment.

------
herodotus
> Way Cooler is a Wayland compositor that was written in Rust using wlc

I know it is not easy, but I wish the author could have started with a
paragraph that could help someone like me know whether or not the rest of the
article would be something I would like to read.

How about something like this (and of course I may some of the facts wrong,
but I want to be as constructive as I can):

"Wayland is a Windows manager developed as a better alternative to X-Windows.
Way Cooler is a tiling Wayland manager, written in Rust, and designed to be
easily extendible. In this article I describe my experience in trying to
refactor it, for reasons that will be described below. I will also explain
why, in certain instances, C was a better choice for this project than Rust."

~~~
guelo
You weren't the audience of this article. Since he wrote it as a blog post on
way-cooler.com he surely was expecting his audience to be people familiar with
Way Cooler, Wayland and Rust. He doesn't have a responsibility to dumb it down
for you. And anyway it only takes a few minutes for you to get the context.
Which you did, good job.

~~~
dmix
So it's way-cooler.com's fault for not explaining what their library/website
is about.

Which unfortunately is par for the course on 99% of company blogs, which never
explain what the company does without going onto the homepage (which even then
is often confusing).

~~~
OhSoHumble
It's not a company blog. It's a some guy's open source side project. Ya'
know... free... open source... side project... that's he's voluntarily sharing
technical information on.

Really, it's not the guy's responsibility to over-inflate the article because
some people need their hand held. It's a blog about about Rust, Wayland, and
window managers. If you don't know what those things are then, like, Wikipedia
is there for you, man.

It's like going to a programming languages website and going "well, gee, these
guys suck for not explaining what a programming language is on their homepage
what is with everyone being so discourteous!?"

And it's not like a paragraph of context would have mattered anyways; if you
don't know what Rust is, what Wayland is, or what tiling window managers are
then you'll need a lot more background knowledge before the article's content
begins to be assailable.

------
pcwalton
I may be missing something, but what's wrong with the first example? The fact
that you can leak an object shouldn't really matter for memory safety if it's
just a handle to an object that the server manages. Yeah, it could go _wrong_
, but it won't be _unsafe_. Object handles are essentially file descriptors,
right?

In general I think there's a tendency to overcomplicate safety features in
Rust. The solution to an overly-complicated system isn't to throw the whole
notion of safety out the window: it's to look at exactly what the complexity
is buying you. If intricate combinations of Rust features are one extreme of
the safety spectrum and C is the other extreme, there's frequently a happy
design medium somewhere in the middle.

Edit: Looks like oconnor663 over on Reddit had a similar but more specific
proposal, which probably works:
[https://www.reddit.com/r/rust/comments/biq864/comment/em2kip...](https://www.reddit.com/r/rust/comments/biq864/comment/em2kipe)

~~~
Arnavion
Yes, the same concept is discussed in
[https://news.ycombinator.com/item?id=19779243](https://news.ycombinator.com/item?id=19779243)

------
mcguire
" _A Wayland “output” is the resource that represents a display device.
Commonly this means it handles a computer monitor. This resource could
disappear at any time in the life cycle of the application. This is easy
enough to imagine: all it takes is a yank of the display’s power cord and the
monitor goes away._ [Except it can't; it can only disappear between
callbacks?] _This is basically the exact opposite of the Rust memory model.
Rust likes to own things and give compile-time defined borrows of that memory.
This is runtime lifetime management that must be managed in some way._ "

Something leads me to believe that there is something very wrong with the
architecture of wlroots-rs. The output should be attached to a callback
parameter or something, maybe? I don't know enough about Wayland to say, but
something ain't right.

~~~
timidger
Author here.

This output is attached to a callback parameter, but you can takes this
resource from the callback in C (because it's just a pointer you copy around)
and use it in other callbacks. Eventually a "special" callback that will
trigger to indicate that the data the pointer refer to will be cleaned up and
you need to remove all of your references to that resource because otherwise
they will be dangling.

------
kccqzy
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 <- ContT (run outputHandle)
          surface <- ContT (run surfaceHandle)
          -- and then maybe some more nested layers
          -- etc
    

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.

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

~~~
kccqzy
This doesn't really have much to do with the runtime. Certainly with a GC,
programming would be easier, but I'm really talking about abstractions within
the language. Rust already has language-integrated support for the Result
monad. Rust doesn't have a general `do` syntax, but it has `?` which has made
programming with the Result monad much easier. Can we think about the
continuation monad and arrive at a new syntax that can ease this style of
programming?

~~~
uryga
i don't really know Rust apart from some curious onlooking, but afaik
expressing monads (i.e. the Monad typeclass) in Rust is tricky – something to
do with the lifetimes of closures and the objects they close over, i think.
([Idiomatic Monads in Rust] probably touches on that). it might be possible to
just build it into the language a la Result or async/await though.

[Idiomatic Monads in Rust]
[https://varkor.github.io/blog/2019/03/28/idiomatic-monads-
in...](https://varkor.github.io/blog/2019/03/28/idiomatic-monads-in-rust.html)

------
lucideer
Question (for any more knowledgable readers here) from someone with a somewhat
shallow understanding of the topics discussed:

Does this end up being primarily a negative reflection on the general
structure of:

(a) Rust

(b) wl-roots

(c) Wayland

(d) all three

(e) none of the above, it's merely the incidental reality of trying to write
code that's compatible/usable across multiple language ecosystems and none of
the 3 projects can do much to improve this situation.

~~~
blattimwind
> Does this end up being primarily a negative reflection on the general
> structure of

A core problem in this case is wlroots using a memory management paradigm that
isn't easily modeled in Rust. This isn't unexpected per se, since C leaves MM
entirely to the developer, while Rust is opinionated.

~~~
steveklabnik
I would say it's a bit more subtle than that. Rust _can_ express this, but not
safely. This is what the end bit is about.

The question then becomes, is it worth it if it's largely unsafe? That's a
complex question. Unsafe Rust still does give you a lot of advantages, namely
that the checked constructs are still safe, even in an unsafe block. Unsafe
Rust is slightly more annoying to write than safe Rust, and so that's a
downside.

It's also possible, and again, this is more in theory since I know nothing
about wayland internals, that the safe abstraction was chosen to be a bit too
low-level. That is, rather than trying to make the primitive operations safer,
designing an external API you'd want users to use, rather than one defined in
terms of some of the primitives, may make sense. This has a lot of pros and
cons, as you'd imagine. And that's also more work to do.

~~~
timidger
Author here.

Ignoring the social impetus in the Rust community to not use unsafe, I also
don't feel like unsafe Rust is something I want to program in all the time.

When I program in safe Rust I can be happy once it compiles because I can
ignore all of the safety problems that come from C and C++.

However in unsafe Rust not only is it much more difficult to express what I
want syntatically (the lack of auto deref is very annoying, having to write
(*base).value all the time gets very old) and semantically (there is no
standard for the unsafe parts of the language - not so much a problem if only
smallish parts of this usage is used (because once a standard comes out just
that can be updated) but a problem if a whole program is written in it).

Unsafe Rust is "good enough" to try to encode these abstractions but I would
not use it over C or C++.

------
simag
Interesting post that show some current limitations of Rust, especially when
not being able to control the design entirely.

Rust's ownership system makes it harder to deal with the kind of dynamics
wlroots exposes... It seems to me that the issue here is that there is no
consensus (e.g library) on how Rust should deal with those issues, so if you
want to just to provide bindings to a dynamic system you end up writing
middle-ware that deals with that kind of dynamics in a Rust idiomatic fashion
(taking advantage of ownership) instead of focusing on the task at hand. It
somehow destroys Rust's promise of productivity.

A Rust project of mine[1], in an early stage and currently paused, deals with
those issues (probably in a way similar to the Handles described in the post)
by providing Service smart pointers (i.e, Svc<T>) and a component-framework
that helps deal with the dynamics of a service being unregistered (e.g, a
monitor unplugged, or a dynamic library unloaded) ; that component framework
helps you be sure that if your struct contains a Svc<Foo>, that service is
still available when you're called, or the component reaches an invalid state.

In general, it seems to be an area where Rust's ecosystem is still very early
and would benefit from more input (such as this post) and consensus.

Regarding the callback-hell issue, that "dehandle" macro looks very much like
async/await, and it looks like it could be implemented either in terms of
async/await (still unstable) or generators (even more unstable).

Hopefully similar projects will be more likely to succeed as Rust and its
ecosystem mature.

[1] [https://github.com/magnet/socrates-
rs](https://github.com/magnet/socrates-rs)

------
4bpp
How does Rust handle these problems in the context of file I/O? I'm sure there
is an axiomatically idiomatic (official) implementation of it as part of the
language distribution, and at the same time file handles seem conceptually
very similar to display handles and should have similar failure patterns
(space can run out, a disk can fail, a plug-and-play disk can be yanked out at
any time...).

~~~
steveklabnik
That is all here: [https://doc.rust-
lang.org/stable/std/fs/struct.File.html](https://doc.rust-
lang.org/stable/std/fs/struct.File.html)

These failure patterns are handled by each method, for example,

    
    
      pub fn open<P: AsRef<Path>>(path: P) -> Result<File>
    

that Result will return an error if the file can't be opened, etc. Let's say
you want to write some bytes, that's

    
    
      fn write(&mut self, buf: &[u8]) -> Result<usize>
    

This also returns Result, so if you've opened the file, but the disk is now
out of space, this will return an error, etc.

------
newnewpdro
I only skimmed the article, but the description of outputs and their
asynchronous lifecycle making things complicated just made me think of what's
become known as the ECS pattern in game development.

Your outputs sound just like entities in a game. They come and go as they
please, and you often have multiple references to them from myriad places
since entities may interact with many parts of the game.

I suspect if you investigated the established techniques for implementing ECS-
style games in rust, you might find some simpler and established solutions for
your troubles.

------
josteink
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.

------
Lowkeyloki
Everything the author and the commentors here have said is completely legit.
I'm not looking to start a war. And I completely acknowledge that what I'm
about to say might be wrong as I haven't seen the author's code or the code of
wlroots.

But I have to wonder if the design decisions of wlroots itself may be dubious.
If it's this hard to manage memory safely when you're trying to wrap the API
in a language that demands you're kept accountable for memory safety....

------
ac130kz
Does wlroots itself present good code quality or it is basically a horrible
wrapper to support every Xorg use case?

~~~
Sir_Cmpwn
wlroots is generally highly regarded in the Wayland community, both in terms
of technical design and code quality. There's a reason that every Wayland
project which started _since_ wlroots has used wlroots, and those who didn't
at first eventually rewrote their code to use wlroots.

~~~
AsyncAwait
I get the sense that most adopted wlroots because Wayland is a complex
protocol. There's no alternative to wlroots so part of the reason everybody
uses it is because there's no choice if you don't want to start from nothing.

~~~
Sir_Cmpwn
Wayland is a simple protocol. The complicated part is everything else, like
graphics and input and X11 compatibility.

~~~
AsyncAwait
Right, but that still means that getting to a point most end-users would
consider 'usable' is quite complex. Especially because the documentation
around these things can be lacking.

P.S. I like Wayland and use it everywhere for the record.

------
mempko
I have had the view that safety is typically not the most important goal of a
project (user satisfaction is). I have often said that safety can get in the
way of writing software that is useful. It's great to see a real-life case
with Rust from someone who obviously tried very hard but ultimately had safety
get in the way of writing the software they wanted. Hopefully, Rust will
evolve to interface with the "unsafe" world in a more ergonomic way.

------
vectorEQ
good ol C :). Thanks for sharing these experiences. Happy to see some example
of the places where rust still falls a little short / is a bit too restrictive
for system programming. a lot of people deny this, but there's plenty of
situations where you just want plain old C for it's straightforwardness to
implement your own design. if you want to do it the 'rust' way, you find
yourself restricted in all kinds of ways. if you can live with those
restrictions it's great, but sometimes you just get stuck. Good luck on the
rebuild in C!

------
shmerl
A pity wlroots itself isn't written in Rust.

~~~
Sir_Cmpwn
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,
running into the same problems which caused Timidger to abandon wlroots-rs,
only more so. And for what? wlroots works great and didn't require shaving a
thosuand yaks.

~~~
pjmlp
C is the lingua franca of UNIX based platforms.

Thankfully not all OSes bow to it.

~~~
Sir_Cmpwn
>Thankfully not all OSes bow to it.

Right, just the successful ones.

~~~
tomjakubowski
Not Windows.

~~~
Sir_Cmpwn
Right, because Win32 is a shining beacon of good design. Thank god it's not C,
right?

~~~
pjmlp
Meanwhwile Linux is still trying to catchup with the security level of
mainframes.

68% of out of bound kernel expoloits in 2018, nice.

------
scoutt
> When the benefit at the end of the day is just so I don’t have to write C,
> that doesn’t really make it worth it.

This line not only summarizes the content of the article, but I'm afraid it
may also describe the actual situation of porting C/C++ code to Rust.

