Live data from Hacker News

Rust and Nix = easier Unix systems programming

kamalmarhubi.com

21–30 of 85 posts

Re: Rust and Nix = easier Unix systems programming

#21
post #18
post #15

Earlier quoted context omitted.

In this case it seems like a very thin wrapper that leverages the type system to allow catching a whole class of errors at compile time, like using exhaustiveness checks to make sure a function call handles all possible return values. I think the small overhead is well worth it. The original API is not "broken" per se, it's just limited by the language features ("magical" return values vs. tagged unions or whatever t…

It's not even clear to me that there's any overhead to the Rust version. Checking error codes that should be checked isn't overhead. Checking them inefficiently would be overhead, but the Rust version looks like it should compile down to something pretty similar to what the equivalent C switch blocks would produce.

I'm not talking about the efficency of the resulting binary, but the "distance" from what the programmer is thinking, to what the machine will really do.

Compiler optimizations aside, C does a pretty good job at this. It's way more efficient than writing assembly, but your still basically just moving memory around, while doing some arithmethic. Easy to understand in "machine" terms.

Of course, this is only relevant when you're doing low-level stuff, like kernel or drivers programming. For the userland, Rust really looks like a nice language (I've played with it just a bit), and I'd be really happy if it pushes C++ away ;-)

Re: Rust and Nix = easier Unix systems programming

#22
post #19

I always find it a bit unfair when I see sloppy C programs used for shock value. What if the Rust developer uses fork().unwrap_or(default_value) in a hurry, or writes if let Some(child) = fork() { do_only_child_stuff(); } else { do_only_parent_stuff(); } or if let Some(ForkResult::Child) = fork() { do_only_child_stuff(); } else { do_only_parent_stuff(); } Now, if you're about to tell me that the examples above are to…

> What if the Rust developer uses fork().unwrap_or(default_value) in a hurry

The point here is that the language's tools and APIs can significantly better drive the developer towards the safe/right solution, that's a large point of type theory and static type systems after all. In this case rust's type system is used to split out the various "result cases" and notify the developer upfront of the various cases to handle. The return type pretty much tells you how the function will behave and what you need to take care of as the caller.

That aside, why would you unwrap_or(default_value) if you're in a hurry when unwrap() is shorter (and you can later grep for "unwrap()" to find dodgy/hurried code, whereas unwrap_or is a perfectly legitimate recovery strategy).

> Now, if you're about to tell me that the examples above are totally stupid and no developer would do such a thing, then you know how I feel about the sloppy C versions. Doing a system call and not checking for error is totally stupid as well.

The issue being that even though you have a compiled statically typed language it's of absolutely no help in "checking for error", and interactions between syscalls can be hard to predict, not checking for fork(2)'s error isn't the end of the world... until you pass its result to kill(2) for instance (it might also give strange results if you pass specific pids to waidpid)

Re: Rust and Nix = easier Unix systems programming

#23

I can't help but think they're trying to fix something that isn't broken at all. Adding new abstraction layers rarely helps when doing systems programming. You (as in "the developer") want to be as near to the machine as possible. C does this pretty well. Perhaps I'm just getting old :-(

I don't think you want to be as near as the machine as possible, otherwise all system programmers would write machine code. You want to have powerful abstractions that the compiler can see through to produce optimal code.

Re: Rust and Nix = easier Unix systems programming

#24
post #18
post #15

Earlier quoted context omitted.

In this case it seems like a very thin wrapper that leverages the type system to allow catching a whole class of errors at compile time, like using exhaustiveness checks to make sure a function call handles all possible return values. I think the small overhead is well worth it. The original API is not "broken" per se, it's just limited by the language features ("magical" return values vs. tagged unions or whatever t…

It's not even clear to me that there's any overhead to the Rust version. Checking error codes that should be checked isn't overhead. Checking them inefficiently would be overhead, but the Rust version looks like it should compile down to something pretty similar to what the equivalent C switch blocks would produce.

> It's not even clear to me that there's any overhead to the Rust version.

There is a slight bit of stack overhead: Option is at least {tag:u8, {tag:u8, pid:i32}}, and due to alignment constraints it's actually {tag: u32, {tag: u32, pid: i32 }}). A nonzero wrapper[0] would allow folding either ForkResult or Option into a 0-valued pid_t and remove one level of tagging: http://is.gd/yxStW1

Beyond that you'd need generalised enum folding in order to fold two tags into the underlying value (you'd denote that pid_t is nonzero and nonnegative for instance)

[0] which is unstable, so not really an option

Re: Rust and Nix = easier Unix systems programming

#25
post #12

I was very confused. I thought this had something to do with Rust and nix[1]. [1]: https://nixos.org/nix/

Same here. To be fair the Nix package manager has a name that is actively harming its growth since most people think *nix as in unix when you say nix.

Re: Rust and Nix = easier Unix systems programming

#27
post #18

Earlier quoted context omitted.

It's not even clear to me that there's any overhead to the Rust version. Checking error codes that should be checked isn't overhead. Checking them inefficiently would be overhead, but the Rust version looks like it should compile down to something pretty similar to what the equivalent C switch blocks would produce.

> It's not even clear to me that there's any overhead to the Rust version. There is a slight bit of stack overhead: Option is at least {tag:u8, {tag:u8, pid:i32}}, and due to alignment constraints it's actually {tag: u32, {tag: u32, pid: i32 }}). A nonzero wrapper[0] would allow folding either ForkResult or Option into a 0-valued pid_t and remove one level of tagging: http://is.gd/yxStW1 Beyond that you'd need genera…

We do have a planned optimization that would fold the tags for cases like `Option` to give a word pair, which should be returned in %eax:%edx (or %rax:%rdx).

Re: Rust and Nix = easier Unix systems programming

#28
post #18

Earlier quoted context omitted.

It's not even clear to me that there's any overhead to the Rust version. Checking error codes that should be checked isn't overhead. Checking them inefficiently would be overhead, but the Rust version looks like it should compile down to something pretty similar to what the equivalent C switch blocks would produce.

I'm not talking about the efficency of the resulting binary, but the "distance" from what the programmer is thinking , to what the machine will really do. Compiler optimizations aside, C does a pretty good job at this. It's way more efficient than writing assembly, but your still basically just moving memory around, while doing some arithmethic. Easy to understand in "machine" terms. Of course, this is only relevant…

Rust is just as close to the hardware as C, it just checks your code more.

Re: Rust and Nix = easier Unix systems programming

#29
post #18

Earlier quoted context omitted.

It's not even clear to me that there's any overhead to the Rust version. Checking error codes that should be checked isn't overhead. Checking them inefficiently would be overhead, but the Rust version looks like it should compile down to something pretty similar to what the equivalent C switch blocks would produce.

I'm not talking about the efficency of the resulting binary, but the "distance" from what the programmer is thinking , to what the machine will really do. Compiler optimizations aside, C does a pretty good job at this. It's way more efficient than writing assembly, but your still basically just moving memory around, while doing some arithmethic. Easy to understand in "machine" terms. Of course, this is only relevant…

Compiler optimizations included, C does a terrible job at this. It puts forward a seductive but terrible mirage of simple mappings and understandings which are just plain broken. And then you add multithreading into the mix and it gets even worse, even without optimizations.

We live in a world of many cores, and multiple CPUs all over the place - in your GPUs, your hard drives, motherboard controllers - and the intrinsic language support for multithreading literally does not exist as part of the C99 standard? One has to reach out to a mixture of POSIX, and the compiler extensions the POSIX implementation uses to annotate memory barriers so the optimizer won't break things, and intrinsics that introduce atomic operations, and... gah!

C and C++ do such a terrible job of this I have to resort to disassembly to debug program behavior far too frequently. These are the only languages I'm forced to do this with. If C or C++ were really "close to what the machine will really do", I'd expect the opposite result.

Even simple things like class and structure layouts and type sizes are controlled by a mess of compiler and architecture specific rules and extensions to control the application of those rules with regards to padding, alignment, etc. which I get to debug. Ever had to debug differences in class layout between MSVC and Clang due to differently handling EBCO in a multiple inheritance environment? What about handling alignment of 8-byte types on 32-bit architectures differently? At least you've replaced all uses of "long" because of the mixture of LP64 and LLP64 compilers out there...? And what about when two incompatible versions of the standard library with different type layouts get linked in by a coworker? These are the symptoms of a language that doesn't control what the machine is really doing very well at all.

When I really need tight control over what the machine will do at a low level, my tools are actual (dis)assembly, intrinsics, an understanding of the underlying hardware itself, and simple code that eschews features requiring significant runtime support or underpinnings. None of those are C or C++ specific. The last one requires some knowledge of how a language's features are implemented - C and C++ might be broken enough that you're forced to wrestle with that topic, when it's more optional in other languages, but... that still doesn't make it C or C++ specific.

Re: Rust and Nix = easier Unix systems programming

#30
post #13
post #5

In reliability theory "X failed" is a poor error message. What we want to know is which failure mode has been triggered. The function of kill is to kill a given pid, so there are two failure modes : "the pid didn't exist" or "the pid didn't die"

Is there a logology/discipline where error messages are treated to medical-legal terminology?

I got exposed to reliability theory in Logistics Engineering.

Mostly: Reliability Centered Maintenance

https://en.wikipedia.org/wiki/Reliability-centered_maintenan...

Post reply on HN