Uninitialized Memory: Unsafe Rust Is Too Hard

This page summarizes the projects mentioned and recommended in the original post on news.ycombinator.com

Our great sponsors
  • InfluxDB - Power Real-Time Data Analytics at Scale
  • WorkOS - The modern identity platform for B2B SaaS
  • SaaSHub - Software Alternatives and Reviews
  • rust

    Empowering everyone to build reliable and efficient software.

  • Rust already supports partially deinitialized structs - that is, moving out of a struct field by field - and there’s no fundamental reason it can’t support partial initialization too.

    Indeed, there’s an open issue for it:

    https://github.com/rust-lang/rust/issues/54987

    But there are a lot of desired features with open issues, so don’t expect this to be implemented anytime soon unless someone takes an interest in it.

  • firecracker

    Secure and fast microVMs for serverless computing.

  • - The Rust compiler is actually very helpful. It's the least cryptic compiler I've met. But its OK if you feel that now as you're just beginning your journey with Rust

    Avoid the temptation to "read" Rust from a book. Try to _do_ Rust. Otherwise it might overwhelm you. Simply keep adding Rust techniques to your arsenal as you mature in your usage of Rust.

    Learning Rust changed the way I look at programing. Rust is a beautiful language. As a random example, just look at the the Firecracker VMM written in Rust -- https://github.com/firecracker-microvm/firecracker . It would have been able to very difficult to understand the codebase if it were written in C/C++!

    Rust is one of those rare languages I've encountered that if the code compiles, there is a high probably it will work. The type system is that good!

    TL;DR Persist and you will reap the rewards with Rust.

  • InfluxDB

    Power Real-Time Data Analytics at Scale. Get real-time insights from all types of time series data with InfluxDB. Ingest, query, and analyze billions of data points in real-time with unbounded cardinality.

    InfluxDB logo
  • v

    Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation. https://vlang.io

  • I believe that different programming languages resonate better with some more than others. Often it's a matter of preferences, comfort, perceived usability, and previous experiences.

    So, it's quite possible for various people to feel more comfortable using Golang than Rust, and the opposite can be true as well, where using Rust is preferred over Golang. At the end of the day, it will usually come down to individual or corporate priorities.

    Another of the newer languages that fall into a similar usage space would be Vlang (https://github.com/vlang/v). It being debatably easier to learn and deal with it, in the context of languages that more easily interact with C.

  • perspective

    A data visualization and analytics component, especially well-suited for large and/or streaming datasets.

  • >> I'm not sure if I'd pick Rust for WASM

    What would be an alternative if I plan to develop a high-performant data analysis tool for WASM(similar to Perspective[0])? I looked at the list of supported languages[1] and Rust seems to be a good choice.

    [0] - https://github.com/finos/perspective

    [1] - https://github.com/appcypher/awesome-wasm-langs

  • miri

    An interpreter for Rust's mid-level intermediate representation

  • ```

    So it's only going to do a raw offset and then assign to it, which is identical to `ptr::addr_of_mut!((role).field) = value`.

    Sadly there's no way to tell miri to consider `&mut T` valid only if `T` is valid (that choice is not settled yet, AFAIK, at the language design level), in order to demonstrate the difference (https://github.com/rust-lang/miri/issues/1638).

    The other claim, "dereferencing is illegal", is more likely, but unlike popular misconception, "dereference" is a syntactic concept, that turns a (pointer/reference) "value" into a "place".

    There's no "operation" of "dereference" to attach dynamic semantics to. After all, `ptr::addr_of_mut!(p).write(x)` has to remain as valid as `p.write(x)`, and it does literally contain a "dereference" operation (and so do your field projections).

    So it's still inaccurate. I believe* what you want is to say that in `place = value` the destination `place` has to hold a valid value, as if we were doing `mem::replace(&mut place, value)`. This is indeed true for types that have destructors in them, since those would need to run (which in itself is why `write` on pointers exists - it long existed before any of the newer ideas about "indirect validity" in recent years).

    However, you have `Copy` types there, and those are definitely not different from `<mut T>::write` to assign to, today. I don't see us having to change that, but I'm also not seeing any references to where these ideas are coming from.

    > I'm pretty sure we can depend on things being aligned

    What do you mean "pretty sure"? Of course you can, otherwise it would be UB to allow safe references to those fields! Anything else would be unsound. In fact, this goes hand in hand with the main significant omission of this post: this is not* how you're supposed to use `MaybeUninit`.

    All of this raw pointer stuff is a distraction from the fact that what you want is `&mut MaybeUninit`. Then all of the things about reference validity are necessarily true, and you can safely initialize the value. The only `unsafe` operation in this entire blog post, that isn't unnecessarily added in, is `assume_init`.

    What the author doesn't mention is that Rust fails to let you convert between `&mut MaybeUninit` and some hypothetical `&mut StructBut>` because the language isn't powerful enough to do it automatically. This was one of the saddest things about `MaybeUninit` (and we tried to rectify it for at least arrays).

    This is where I was going to link to a custom derive that someone has written to generate that kind of transform manually (with the necessary check for safe field access wrt alignment). To my shock, I can't find one. Did I see one and did it have a funny name? (the one thing I did find was a macro crate but unlike a derive those have a harder time checking everything so I had to report https://github.com/youngspe/project-uninit/issues/1)

  • project-uninit

  • ```

    So it's only going to do a raw offset and then assign to it, which is identical to `ptr::addr_of_mut!((role).field) = value`.

    Sadly there's no way to tell miri to consider `&mut T` valid only if `T` is valid (that choice is not settled yet, AFAIK, at the language design level), in order to demonstrate the difference (https://github.com/rust-lang/miri/issues/1638).

    The other claim, "dereferencing is illegal", is more likely, but unlike popular misconception, "dereference" is a syntactic concept, that turns a (pointer/reference) "value" into a "place".

    There's no "operation" of "dereference" to attach dynamic semantics to. After all, `ptr::addr_of_mut!(p).write(x)` has to remain as valid as `p.write(x)`, and it does literally contain a "dereference" operation (and so do your field projections).

    So it's still inaccurate. I believe* what you want is to say that in `place = value` the destination `place` has to hold a valid value, as if we were doing `mem::replace(&mut place, value)`. This is indeed true for types that have destructors in them, since those would need to run (which in itself is why `write` on pointers exists - it long existed before any of the newer ideas about "indirect validity" in recent years).

    However, you have `Copy` types there, and those are definitely not different from `<mut T>::write` to assign to, today. I don't see us having to change that, but I'm also not seeing any references to where these ideas are coming from.

    > I'm pretty sure we can depend on things being aligned

    What do you mean "pretty sure"? Of course you can, otherwise it would be UB to allow safe references to those fields! Anything else would be unsound. In fact, this goes hand in hand with the main significant omission of this post: this is not* how you're supposed to use `MaybeUninit`.

    All of this raw pointer stuff is a distraction from the fact that what you want is `&mut MaybeUninit`. Then all of the things about reference validity are necessarily true, and you can safely initialize the value. The only `unsafe` operation in this entire blog post, that isn't unnecessarily added in, is `assume_init`.

    What the author doesn't mention is that Rust fails to let you convert between `&mut MaybeUninit` and some hypothetical `&mut StructBut>` because the language isn't powerful enough to do it automatically. This was one of the saddest things about `MaybeUninit` (and we tried to rectify it for at least arrays).

    This is where I was going to link to a custom derive that someone has written to generate that kind of transform manually (with the necessary check for safe field access wrt alignment). To my shock, I can't find one. Did I see one and did it have a funny name? (the one thing I did find was a macro crate but unlike a derive those have a harder time checking everything so I had to report https://github.com/youngspe/project-uninit/issues/1)

  • hubris

    A lightweight, memory-protected, message-passing kernel for deeply embedded systems.

  • Thanks Steve!

    It turns out I had misremembered; the cast I was thinking of is

    https://github.com/oxidecomputer/hubris/blob/master/sys/kern...

    which is from &mut MaybeUninit<[T]> to &mut [MaybeUninit], which doesn't construct a reference to something uninitialized.

  • WorkOS

    The modern identity platform for B2B SaaS. The APIs are flexible and easy-to-use, supporting authentication, user identity, and complex enterprise features like SSO and SCIM provisioning.

    WorkOS logo
  • unsafe-code-guidelines

    Forum for discussion about what unsafe code can and can't do

  • I feel like you're ignoring the actual history here, these are a few excerpts from right before the 1.0 release of Rust[0]:

    > Rust has remained true to its goal of providing the safety and convenience of modern programming languages, while still offering the efficiency and low-level control that C and C++ offer.

    > When used in its own most minimal configuration, it is even possible to write an operating systems kernel in Rust.

    > The resulting design is not only simpler to learn, but it is also much "closer to the metal" than we ever thought possible before. All Rust language constructs have a very direct mapping to machine operations, and Rust has no required runtime or external dependencies.

    They do actually have a group working on making `unsafe` better, so they do somewhat recognize the problem (I'm not sure about the results, but the last time I looked at it was probably over a year ago): https://github.com/rust-lang/unsafe-code-guidelines

    Perhaps another catch is that, at least in my experience, the most common situation where `unsafe` can into play is interop with other languages/libraries/etc.. Some of it is avoidable, but a certain amount of it is necessary just be able to interface with existing non-Rust code, so it's not really optional in those situations.

    Also, just to be clear, I do actually like Rust, and I've written a few different things in it. I just think the state of `unsafe` is poor to the point that it's very hard to determine if you're doing something correctly, which in my opinion makes it unusable for its purpose.

    [0]: https://blog.rust-lang.org/2014/09/15/Rust-1.0.html

  • rs_wad

    A Rust library for loading .WAD files (Doom, Quake, etc)

  • Thank you.

    I see. I'm looking now at how to load DOOM WAD files in Rust (https://github.com/bjt0/rs_wad/blob/master/src/wad.rs#L105) as an example, and I see it uses your method (even if it seems to store each piece in individual variables to then copy them into the struct).

    It's not as straightforward as one would expect (as you say, it requires an extra buffer and parsing, but no extra IO). I surely won't get to the solution by myself.

NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts