Email or username:

Password:

Forgot your password?
Asahi Linya (朝日りにゃ〜)

Almost every thread about Rust for Linux ends up with someone saying "why not Zig instead"? And usually the answer is just "it's less mature" or "nobody pushed it".

I didn't know anything about Zig, so I decided to take a look today... and I'm not very impressed ^^;;

I learned that Zig does not have:

- Destructors
- Macros
- Lifetimes
- Operator overloading

Those are major reasons why I chose Rust for the drm/asahi driver...

It sounds like Zig is trying to be "modern C"... but the whole point of R4L is to not get stuck with C!

All those things Rust has that Zig doesn't are important for the things I'm doing.

Destructors/RAII are fundamental to how the driver tracks and cleans up firmware structures safely and reliably when needed. If I had to write "defer" everywhere it would be a bug-prone mess...

Rust's amazing macros are how I deal with the firmware versioning differences while keeping the driver maintainable. In C you can sort of do that (poorly) with the preprocessor... but Zig doesn't have that either, so that's actually a step back from C, as far as I can tell...!

I don't think I really need to explain lifetimes/memory safety, that's the one thing everyone knows is different between the languages... I'll just say, I am certain I wouldn't have gone from "first render" to "stable accelerated Linux desktop" in 2 days without memory safety.

(continued)

8 comments
Asahi Linya (朝日りにゃ〜)

Operator overloading is the one I could live without... but it still would have been very annoying to have to use functions to do all the complex GPU power management calculations that I built on top of a simple soft float implementation (no real floats in the kernel!).

I have nothing against Zig, it looks like a neat programming language and it definitely has its place!

... but I don't think that place is writing complex operating systems and drivers today. At least I'm pretty sure it wouldn't have worked for my Apple GPU driver.

Edit: I'm getting a bunch of comments pointing out Zig's compile-time evaluation and metaprogramming features. I've looked into it a bit more, and so far my conclusion is "it's impressive and clearly very well designed and powerful... but for the very specific problem I'm personally trying to solve, Rust macros still work better"

Edit 2: People have come up with solutions to my versioning problem in Zig that are pretty similar to what I do in Rust with macros, so I'll retract that point. The other three still stand though, and the safety and destructors ones are pretty important ^^

Operator overloading is the one I could live without... but it still would have been very annoying to have to use functions to do all the complex GPU power management calculations that I built on top of a simple soft float implementation (no real floats in the kernel!).

I have nothing against Zig, it looks like a neat programming language and it definitely has its place!

Asahi Linya (朝日りにゃ〜)

Copying a thread I tweeted about `defer` vs. `Drop`:

> with defer:
> - whilst a bit error-prone, destruction is bound to the context and you can have non-intrusive destructors, you'll need this for APIs where you need a context to destroy a resource, so storing it in each object is infeasible

This is a valid argument and I actually have run into this myself! It's a real annoyance in Rust...

... but the thing is, this is the exception, not the rule. 99% of the time automatic Drop is what I want.

One of the neat demos I did of how Rust is better than C is that my Rust OF abstractions take care of refcounts for you, even across iterators and such. All the explicit refcounting (that nobody gets right) disappears.

In Zig, it'd come back, just as "defer" everywhere instead.

It *is* possible to forbid Drop in Rust for a type and require explicit destruction, and I'd rather do that for the 1% of cases where I need it, than have to defer the other 99%.

The Zig approach really doesn't scale for complex data structures. You can't have a generic vector or map, and just destroy it. You need to write explicit code to iterate through it and destroy every element. That's a real major pain...

Much of the time I don't even have Drop impls for my objects, or they just do one trivial thing. 99% of the cleanup code is generated by the compiler recursively calling Drop on all contained types. Having to type that out would be a lot of time wasted writing error-prone code...

Copying a thread I tweeted about `defer` vs. `Drop`:

> with defer:
> - whilst a bit error-prone, destruction is bound to the context and you can have non-intrusive destructors, you'll need this for APIs where you need a context to destroy a resource, so storing it in each object is infeasible

This is a valid argument and I actually have run into this myself! It's a real annoyance in Rust...

iliazeus

@lina

> It *is* possible to forbid Drop in Rust for a type and require explicit destruction

Do you mind giving an example of how it can be done? I've been looking for a way to do this some time ago, but haven't really found anything.

Josh Simmons

@iliazeus @lina personally, I approach this by having an explicit destroy function, which consumes the value, and then jamming an assert into the drop path which checks whether you're actually inside the explicit destroy function. You still end up with the unfortunate stuff around std::mem::forget, but in those cases I make sure it's merely a leak rather than a safety violation.

iliazeus

@dotstdy the problem with that approach, if I understand it correctly, is that it isn't compile-time. From Lina's post, I somehow thought that there's a compile-time way to do that.

@lina

Benji Smith

@iliazeus @lina Does `std::mem::ManuallyDrop` do this? Or do you mean forbid it at the type level?
doc.rust-lang.org/std/mem/stru
Edit: nvm, just saw your follow-up to that StackOverflow link

Go Up