Live data from Hacker News

Unsafe Rust: An Intro and Open Questions

cglab.ca

31–34 of 34 posts

Re: Unsafe Rust: An Intro and Open Questions

#31

Earlier quoted context omitted.

Right, but if you write a library using only Rust "safe" code, the guarantee is back on the Rust team. To me, it would be better if libraries using unsafe code were marked.

Here's[0] an interesting idea for forcing crates using unsafe code to be handled specially, while allowing some "blessed" crates through without the special handling. [0]: https://github.com/rust-lang/cargo/issues/934#issuecomment-6...

I wonder if this shouldn't be on the "rust" side, rather than on the "crates.io"-side. Considering[1], I think I'd prefer either/or:

    extern unsafe crate phrases; // It's all UNSAFE!
Or: extern crate phrases; // It's mostly safe

    use unsafe phrases::english; // But not English
The idea being, that either phrases wouldn't be imported/give an error -- or everything in phrases except "english" would be imported -- and english would only be imported if qualified with "unsafe".

Either way... I can see this going the way of try/catch/throws in java -- where the usefulness diminishes as lazy programmers (we're all lazy) end up polluting everything with unsafe (just like "throws Exception e..").

[1] https://doc.rust-lang.org/book/crates-and-modules.html

Re: Unsafe Rust: An Intro and Open Questions

#32
post #29
post #27

Earlier quoted context omitted.

> The first one is mostly a problem with expressive power in the foreign function interface. Can you express what "int read(int fd, char buf[], size_t len)" means in the foreign function definition syntax? Rust's foreign function syntax isn't expressive enough to do that.[1] You can't tell Rust that "len" is the length of "buf". Being able to do that would help reduce the need for unsafe code. Most of the POSIX/Linux…

"Only a very small subset of types have the property that any bit-pattern is safe, essentially only primitives." Structs which contain only primitives have that property. Consider a TCP header.

No, only structs with no invariants. As soon as you have invariants, there are illegal bit patterns. Of course, these illegal bit pattern may not necessarily result in memory unsafety, but there's no way for the compiler to know this automatically^.

This functionality could be implemented something like

  fn from_bytes(bytes: &[u8]) -> Option {
      if bytes.len() >= std::mem::size_of::() {
          unsafe {
              Some(&*(bytes.as_ptr() as *const T))
          }
      } else {
          None
      }
  }
  /// Values for which any bit pattern is valid.
  pub unsafe trait JustBits {}
  
  unsafe impl JustBits for u8 {}
  unsafe impl JustBits for i8 {}
  unsafe impl JustBits for u16 {}
  unsafe impl JustBits for i16 {}
  // ...
Some custom struct that can be any bit pattern can then do:

  struct CustomStruct { ... }

  unsafe impl JustBits for CustomStruct {}
Of course, there's `unsafe` there, but there has to be: it's asserting that "yes, I'm sure that anything works".

^Notably, there's been proposals for `unsafe` fields which will make expressing "invariants exist" more focused, and adjust the trade-offs here.

(I'll note that a TCP header has 3 reserved bits (100, 101, 102) which, I believe, should be set to zero, making some bit patterns theoretically illegal.)

Re: Unsafe Rust: An Intro and Open Questions

#33
post #30

Earlier quoted context omitted.

> It's a good candidate for formal proof of correctness - not too big, and critical to system operation. Unfortunately, having actually looked for production strength proofs of correctness for production allocators (as opposed to toys), I wasn't able to find any. Given how high-value of a target they are for formal verification, we may be underestimating the complexity of doing so.

Successes have been claimed.[1] It's discouraging to me how little progress there's been in proof of correctness in the last 35 years. I used to work on that stuff. C set the field back by decades. [1] https://books.google.com/books?id=yvJqCQAAQBAJ&pg=PA364&lpg=...

That was one of the "toy" implementations I mentioned. The algorithm they described is going to be nowhere near the performance of a modern multithreaded allocator like jemalloc (which is correspondingly far more complex). Which is not to say things like that are not encouraging, only that I was hoping someone would have proven something people actually use correct.

Re: Unsafe Rust: An Intro and Open Questions

#34
post #11

Earlier quoted context omitted.

Just to add to this point, the difference is that Rust can only guarantee this for the standard library. I can similarly write a library with safe interfaces that can be (ab)used to cause UB and there's little that the Rust team can do. This is different from other "safe" languages. This is why it's so important to establish what the responsibility and expectation is of library developers to uphold the safety guarant…

This is not all that different from Java (or Python, etc.), where it is quite easy to hide a call to a native function behind a seemingly-safe interface. The real difference is that native methods in Java must be written in a different language (C), while Rust supports both modes in the same language. (Edit: Or, if you prefer, two different but very closely related languages.) I would argue, at any rate, that this so…

Agreed. We had a segfault in Servo due to upgrading the compiler (and some internal representations changing). I wasn't able to track it myself (unfamiliarity with the code), but someone else was able to find its origin and fix it without much trouble because of `unsafe`. (That aside, we very rarely have segfaults in Servo, and Servo's huge)
Post reply on HN