3 ms·
> If you at all break the rather subtle rules, then essentially anything can and will happen. If by subtle rules you mean your invariants, that is missing fund
by Ygg2 3y ago
> If you at all break the rather subtle rules, then essentially anything can and will happen.
If by subtle rules you mean your invariants, that is missing fundamental assumptions.
It's akin to making a building without foundation and load bearing structures.
> So for example, there was recently a thread where someone had code that checked if a value was in range to safely coerce it directly to an enum then did so. But because of eager evaluation of an argument the unsafe cast happened first
You mean this: https://notgull.net/cautionary-unsafe-tale/ https://notgull.net/cautionary-unsafe-tale/
However note the UB goes away if you never use any unsafe code. Or if you expand your unsafe to encompas some safe code.
Issue it had was safe code was invalidating the invariants unsafe code was relying on. Iirc alignment.
- lmm 3y ago> If by subtle rules you mean your invariants, that is missing fundamental assumptions. > It's akin to making a building without foundation and load bearing structures. That's exactly how C approaches UB too. > However note the UB goes away if you never use any unsafe code. Or if you expand your unsafe to encompas some safe code. Right. The problem is that's untenable; any nontrivial program will have unsafe somewhere, and unsafe can cause failures arbitrarily far away from the incorrect code. The whole point of the article is that you need to be able to draw an actual boundary between the unsafe parts and the safe parts of your program and review the unsafe parts in isolation. If Rust doesn't give you more and better support in doing that than C does, then it's not really making a difference.
- nullc 3y agoExactly. All-hope-lost behavior is possible in rust code unless there is no unsafe anywhere (and no compiler bugs, but I think its fair to ignore those when discussing the language in the abstract). Rust potentially benefits from fewer opportunities to footgun yourself, but rust also comes with other costs (including a more complex syntax, a bad dependency culture, a lot more front-loaded cognitive load around lifetime management, etc.) which might offset those benefits. Some of those extra complexity costs seem hard (or impossible) to avoid when trying to keep memory safety from having a disproportional runtime cost, so I'm not necessarily faulting rust. But some of the sources of defects in rust code may also be entirely avoidable, which is why I think it's important to actually study it rather than axiomatically assume its behavior makes programs correct. It doesn't, even absent unsafe.
- torstenvl 3y ago> compiler bugs... its fair to ignore those when discussing the language in the abstract When the language is defined as whatever the compiler does, as it is in the case of Rust, I'm not so sure. If there were a Rust standard with multiple compliant compilers, I'd be more convinced, but Rust isn't there yet. (And TRF's trademark policy may well prevent it from ever getting there.)
- saagarjha 3y agoHaving more implementations doesn’t really help you avoid bugs in the one you’re using.
- deleted 3y ago[deleted]
- nullc 3y agoNot entirely true. When there are multiple implementations disagreements between them in normative behavior prove one or the other (or the language itself) has a bug. This means you can run randomized tests in your software, hash the results, and the hash should be the same between compilers (and across platforms). That's an option that doesn't exist when there aren't multiple, and even if you won't use the potential personally other people using it will eliminate bugs in the compiler. The csmith program was used to find an incredible amount of bugs in gcc and clang. This approach can also be applied to many pieces of ordinary code too. Prior poster's point is also that when there is only one the language is the compiler and it's not useful to talk about them in isolation. I don't fully agree with that in theory since the language is supposed to be stable-ish while bugs in the compiler will get fixed. So to the extent that something in rust is a footgun due to the language we might be stuck with it, but if its due to a compiler bug it will probably be fixed.
- couchand 3y ago> If Rust doesn't give you more and better support in doing that than C does, then it's not really making a difference. It does and if you've been missing that you've misunderstood this whole discussion. Rust allows you to wrap up some unsafe code in a safe abstraction. You use the type checker to enforce your invariants, such that the unsafe code can be reviewed in isolation. Rust gives you exactly what you're saying you want. Steve is describing Rust as it is.
- lmm 3y ago> It does and if you've been missing that you've misunderstood this whole discussion. Don't tell it to me, tell it to the person I was replying to.
- couchand 3y agoI believe you said > any nontrivial program will have unsafe somewhere, and unsafe can cause failures arbitrarily far away from the incorrect code But the point I was trying to make is that this claim is facially true but misses the critical context: in idiomatic Rust, the unsafe keyword is not the barrier that enforces integrity of the system. The integrity of the system is enforced by the type checker, the same way it always is. The unsafe annotation alerts the reader that there exists an invariant the compiler can't check. Idiomatic code will have a SAFETY comment above describing the invariant. It should be locally possible to reason about how the type abstraction used to encapaulate truly enforces that invariant. This is what Rust people are talking about when we say wrap unsafe code in a safe abstraction. If we do it right, it's no longer possible for spooky action at a distance. Now you might go and point out the fallibility of humans and all that, and you'd be right, but that's in fact what makes it so valuable to try.
- o11c 3y agoParticularly notable: due to the restrictions that Rust chooses to enforce on safe code, `unsafe` is forced in a lot of situations where other languages maintain full runtime safety. I'm not saying going full-Java is the only answer, but a language that is safer than Rust is certainly possible.
- tines 3y agoLike what exactly?
- Animats 3y agoThe problem is that's untenable; any nontrivial program will have unsafe somewhere I'm up to 47,000 lines of Rust with no "unsafe". The main program starts with #![forbid(unsafe_code)] and that applies to the entire program, although not external crates. If you're not using foreign functions, you don't need "unsafe". Some published crates I use do have "unsafe", more than they should. This is annoying.
- Ygg2 3y ago> That's exactly how C approaches UB too. In theory yes. In practice it's been proven that's not what happens. > The problem is that's untenable; any nontrivial program will have unsafe somewhere, and unsafe can cause failures arbitrarily far away from the incorrect code. Not really. Ignoring any and all non-compiler tooling, UB in code can be traced to: A) unsafe region in code where its invariants are invalidated B) if no unsafe code exists in project, then an unsoundness bug in the Rust standard lib. Safe code needs to be safe for any possible value of arguments, fields, variables. Doing otherwise is unsoundness bug. In C, the code you need to look is essentially ALL of your code + C std lib. In Rust, you can focus on places where safe and unsafe code mingle. And IFF (if and only iff) your code contains 0 unsafe, can you be sure there is a Rust bug. This however assumes any code. Idiomatic code in Rust will treat unsafe with big large friendly letters `SAFETY: this works while X and Y hold`. How do you do this is C? Since most lines are possible source of UB do you annotate every line of code? I assume not. How do you ensure something will not be mutated? How do you ensure something is thread-safe? Do you use type bounds? And if not how? > If Rust doesn't give you more and better support in doing that than C does, then it's not really making a difference. Also that's the Nirvana fallacy, Rust doesn't have to be perfect and prevent EVERY undefined behavior forever. If it's improvement over the niche it's targeting (C/C++) then it's an improvement. I.e. is it harder to find UB in Rust or in C? Like you don't go around and say, "Well seatbelts don't prevent head injuries when you hit a wall head-on. So remove seatbelts!", you add airbags to cushion the blow. And spoiler alert: It's giving you the airbags as well. The normal thing when encountering a UB isn't to try to track it down it's to run `miri`, it found UB as soon as it ran.
- bigstrat2003 3y ago> Issue it had was safe code was invalidating the invariants unsafe code was relying on. Iirc alignment. You do recall correctly. The safe code was producing a pointer with alignment that was off, and the unsafe code dereferenced it without checking. I felt that it was kind of a bad take when several people said that it was UB caused by safe code, because really the issue was that the unsafe code wasn't doing its job. The Rust unsafe model is that you can't trust safe code when inside unsafe code. It's on the unsafe code to uphold invariants which it requires, not the safe code which calls it