From: cross@spitfire.i.gajendra.net   
      
   In article <4afb8338-5540-4549-9440-26ec8b17b34an@googlegroups.com>,   
   Alexei A. Frounze wrote:   
   >On Saturday, May 6, 2023 at 6:01:23 PM UTC-7, Dan Cross wrote:   
   >>[snip]   
   >> It's a bit of a joke that all new Rust programmers go through   
   >> a phase of, "fighting the borrow-checker." (Followed shortly by   
   >> the "traits are AWESOME, let's use them EVERYWHERE!" phase).   
   >   
   >:) I'm not fighting it (yet). I think I get the basic idea (or one of the   
   >few) that the allocations/lifetimes are nested (entirely or largely,   
   >unless we're using the unsafe escapes), but I got confused with   
   >some details and put it aside. Should work out those details at   
   >some point.   
      
   Conceptually, it's fairly simple:   
      
   1. Every non-trivial[*] object has a single owner   
   2. Assignment is logically a "move" operation, not copy, and   
    transfers ownership of the object   
   3. Objects are immutable by default.   
   3. You can either borrow a single mutable reference to a mutable   
    object, or one or more immutable references to any object,   
    but these are mutually exclusive:   
    a. if a mutable refence to an object exists, you cannot   
    borrow another reference of any kind to the same object;   
    b. if any number of refenences to an object exists you cannot   
    borrow a mutable refence to that object.   
    Note that (a) and (b) together imply that you can create as   
    many immutable references to an object as you like.   
   4. A reference may not outlive the object it is borrowed from,   
    and lifetimes are a first-class property in the language,   
    with explicit language support.   
      
   The interesting thing is that, in safe Rust code, all of this is   
   statically enforced by the compiler, and the combination has   
   some interesting properties. Safe Rust code is, by definition,   
   free of data-races; further, the existence of a mutable   
   reference is a compiler-enforced proof of the right to mutually   
   exclusive access to a valid object (references are defined to   
   be non-nullable). Undefined behavior in safe Rust is a   
   compile-time error.   
      
   Unsafe Rust lets you do a handful of other things (like   
   dereference raw pointers), but the programmer must still obey   
   all of the above rules. Effectively, `unsafe` says that the   
   compiler cannot check the rules statically, so the programmer is   
   asserting that they have all been followed using external   
   knowledge that the compiler is not privy to.   
      
   >> Personally, I like just cracking it open and playing around with   
   >> a language to learn it, but that's just me.   
   >   
   >I was hoping, I'd just read the relevant chapters, but I might   
   >end up doing just the same, writing code as aid. I expect my code   
   >compiling and working being more of a proof of the soundness   
   >of the code with rust than with C/C++. Though I think rust   
   >still got some things wrong (e.g. different overflow handling in   
   >debug vs release), but overall it seems a step in the right direction.   
   >Perhaps, a few steps, not just one. :)   
      
   Yes; safe Rust code that successfully compiles is essentially   
   statically free of entire classes of memory safety problems, and   
   more or less cannot segfault.   
      
   An observation when we started programming in it at work was   
   that it took us longer to get our programs to compile, but once   
   we did, they tended not to have memory-safety issues. That   
   doesn't mean that they didn't have bugs, but the defects were   
   more often logic issues rather than bad pointers and so on.   
   Moreover, where we used `unsafe` was where we really needed to   
   go look and see if we were messing something up in our reviews.   
      
   I tend to agree wth you about e.g. overflow handling, though we   
   also found that because we were thinking about it for the debug   
   case, we used the explicit versions if we really wanted wrapping   
   semantics and so on, and where we wanted the checks even in   
   release mode we could use `checked_add` and so on. Still, there   
   is an argument that it would have been better if checked were   
   the default, and the unchecked kind had to be explicitly   
   requested. One nice thing in this area is that generally, Rust   
   is explicit in places where other languages are implicit:   
   conversion between types, for example.   
      
   I think they got a few things wrong in the memory model,   
   frankly, and the way that allocators work can be challenging if   
   you neither need more want infallible semantics for allocation.   
   It's not prefect, but right now, it's pretty good; I've enjoyed   
   programming bare-metal at a higher level of abstraction than one   
   gets out of C, for example.   
      
    - Dan C.   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|