3 ms·
What is described there, seems basic encapsulation to me. We do this too in C with structure types and API around it that enforces the invariants. So C is a X-
by uecker 2y ago
What is described there, seems basic encapsulation to me. We do this too in C with structure types and API around it that enforces the invariants. So C is a X-safe language too? Or what am I missing?
- sitharus 2y agoC is not X-safe because you can’t declare conformity in the type system. In Rust, if I understand the article, you can create a “trait” that marks a type as conforming to an invariant, so in the article they marked thread-safe structures as Send and the thread functions as requiring types that implement Send. Send isn’t an API to implement or type definition, it’s a sentinel saying “I declare that this type conforms to the documented expectations” even though the expectations can’t be checked by a compiler.
- dzaima 2y agoMore generally, with C you can't restrict what can (accidentally or not) interact with the internal unsafe bits (without the cost of forcing the data to always live in the heap at least; or perhaps annoying field names that are automatically searched-for by your build system, though then you're essentially making a DSL), or even force using the "safe" parts properly (not enforced at compile-time, at least) outside of, again, a rather limited subset of cases. As a very general example, you can repeat basically any statement in C twice and it'll still compile. If you get lucky, the compiler might tell you you've ended up with a double-free or something, but that's a very limited set of cases, and won't help if the second copy is invoked down a couple function calls. There may be some ways to still get additional true guarantees in C, but they'll be rather more restrictive than ones you can write in Rust, and you'll likely end up with overhead, which tempts skimping out on doing things properly in the name of performance.
- uecker 2y agoIndeed, the double consumption you can not express in C. But invariants of data structures are not a practical problem in C. Looking at the Rust code of this project though, I trust my C code a lot more though... ;-)
- dzaima 2y ago> invariants of data structures are not a practical problem in C. Is that not the cause of like all memory safety vulnerabilities, which are like 30%-or-whatever of linux ones? I've certainly written my fair share of mistakes around invariants in C code. Of course, if you're a perfect developer, indeed the choice of language won't end up mattering.
- uecker 2y agoStraw man fallacy. .
- 0xDEAFBEAD 2y ago>it’s a sentinel saying “I declare that this type conforms to the documented expectations” even though the expectations can’t be checked by a compiler. Interesting. So perhaps the next step is to sprinkle asserts in randomly at runtime to help with catching bugs.
- db48x 2y agoYea, it’s just encapsulation. Rust gives you some additional tools for achieving it though. Enums are very useful for this, as are the rules for handling shared and mutable references. For some examples, imagine an HTTP server that answers requests from clients. You might imagine having a Response object that lets you set headers and the body, with a send method that sends the response back to the client that made the request. It would be an obvious sort of error to send the thing twice, so in C you would assert that send was only called once. In Rust, on the other hand, the send method can _consume_ the Response object. This takes it away from the caller, so the compiler will ensure that they can’t even write code with two calls to the send method. You can’t enforce this at compile time in C because in C all methods take a simple pointer to the object to act on. Another invariant that you might want to enforce is that only one body gets attached to the Response, that the body is attached before the Response is sent, and that the user cannot forget to attach a body. You would start with a Response object that has methods for adding headers. It would also have a method that attaches a body. This body method would _consume_ the Response and return a ResponseWithBody object. The ResponseWithBody object doesn’t have any methods for adding headers, or for adding a body, so several of our requirements are now checked by the compiler. It does have the method for sending the response though, and the Response object does not. This satisfies the rest of the requirements. If you try to send a Response, it’ll fail to compile. If you try to add headers after the body, it’ll fail to compile. You literally just make a state machine out of types, with methods that consume one type and return another, and the compiler enforces that only valid state transitions are possible. This is usually called “typestate programming” if you want to search for more examples.
- uecker 2y agoI think an object ownership system is something we should have in C. Otherwise, i am relatively unimpressed TBH. And readability of this is questionable: https://cs.opensource.google/fuchsia/fuchsia/+/main:src/connectivity/network/netstack3/core/src/ip/device.rs https://cs.opensource.google/fuchsia/fuchsia/+/main:src/conn...
- dzaima 2y agoWith my ~2 weeks of Rust usage, that looks pretty readable. You can freely skip over reading some if not most of the boilerplate, reading just the bits actually doing the main stuff (and whatever context you desire), without fear of having skipped out on some safety-critical part.
- jandrewrogers 2y agoI think the main thing is that it is all done in the type system at compile-time. This is the kind of thing C++ is good at but I’m not sure that C can do it with the same guarantees.
- pjmlp 2y agoIt can't, because C doesn't have the ability to create library types as if they were built-ins.
- uecker 2y agoWhy would this be required?
- pjmlp 2y agoBecause otherwise there is no mechanism to introduce types, that can be used as built-is, while having the connection points across the language to enforce variants.
- uecker 2y agoWhat I do not get is the "can be used as built-in" part. What exactly does this mean? You can enforce invariants by encapsulating structures behind an API. This works fine in C (even better than in C++ IMHO).
- hgomersall 2y agoThe other thing that is important is the statically enforced move and ownership semantics. They are required for types to encode state.
- vacuity 2y agoJava GC, Rust borrow checker, OCaml modules, linters, Valgrind...we more or less know the values that well-written code possesses, but we have yet to figure out how to make well-written code easy to write. Especially at the C or Rust "bare metal" level, it's not about whether a desirable programming practice is possible but whether programmers can reasonably be expected to pull it off.