[HN Gopher] Safety and Soundness in Rust
       ___________________________________________________________________
        
       Safety and Soundness in Rust
        
       Author : oconnor663
       Score  : 64 points
       Date   : 2023-03-05 19:18 UTC (3 hours ago)
        
 (HTM) web link (jacko.io)
 (TXT) w3m dump (jacko.io)
        
       | pornel wrote:
       | I really like that in Rust the API safety is defined by the
       | language, not by each library individually.
       | 
       | There are no gotchas to be found in small print in the library
       | documentation, no disclaimers that the code is meant only for
       | single-threaded programs, or haggling over how rare the edge
       | cases are. If it doesn't say `unsafe`, it has to meet Rust's
       | universal requirements for safety.
        
       | satvikpendem wrote:
       | You could just use cargo-geiger and #![forbid(unsafe_code)] in
       | order to only have safe code. We use this in our CI/CD in order
       | to ensure that no unsafe code is used.
        
         | oconnor663 wrote:
         | I replied to a related comment here:
         | https://news.ycombinator.com/item?id=35034184
        
         | theobeers wrote:
         | I have a library in which I set #![forbid(unsafe_code)]. It
         | relies on a small number of carefully chosen dependencies,
         | almost all of which contain unsafe code. I mean first-rate
         | libraries like bincode, bstr, and once_cell. It feels almost
         | like a sham for me to emphasize the safety of the top layer
         | that I've written.
        
         | lordnacho wrote:
         | What happens if you find unsafe deeply nested in some
         | dependency that you need? Like a web framework or maybe a
         | graphics engine that's not so easy to find a replacement for?
        
           | satvikpendem wrote:
           | Well, the simple answer is you don't use it. The more complex
           | answer is that you do use it but have safe guards in place,
           | which `unsafe` does to some extent even more than C or C++
           | code. You could also ask the creators of said dependencies
           | why there is `unsafe` in their code and perhaps submit PRs to
           | mitigate or remove that, as in the case of actix-web a few
           | years ago.
           | 
           | But as Rust crates become more prevalent, I do expect that
           | we'll see fewer and fewer crates with `unsafe`, or have
           | enough safe alternatives to `unsafe` crates.
        
       | LegionMammal978 wrote:
       | > Rust is designed around safety and soundness. Roughly speaking,
       | safe code is code that doesn't use the unsafe keyword, and sound
       | code is code that can't cause memory corruption or other
       | undefined behavior.
       | 
       | I'd be a bit more precise here. Safety is indeed a property of
       | some particular lexical part of the code, but soundness is really
       | a property of an entire _interface_. A sound interface never
       | causes UB when you invoke it according to its documented
       | preconditions; an unsound interface may possibly cause UB in some
       | situations even when invoked according to its preconditions.
       | 
       | Some take a narrower definition of soundness, treating it
       | primarily as a property of a safe interface: a sound interface
       | that can be invoked from safe code can never cause UB under any
       | circumstances. But personally, I prefer the broader definition,
       | since it also includes unsafe interfaces which don't document all
       | the preconditions they rely on.
       | 
       | Knowing your interface boundary is an important part of writing
       | unsafe code in Rust. For instance, suppose you have a data type
       | with a method that unsafely makes a certain assumption about its
       | fields. Then you have to be very careful to uphold this invariant
       | in every other safe method that creates or modifies those fields.
       | Furthermore, you have to ensure that no language-level operations
       | on the object as a whole (e.g., swapping, dropping, forgetting,
       | or covariant transformations) can break this invariant. But once
       | you have locally checked all of those things, then you don't need
       | to check any other part of the code base to know that your data
       | type is sound.
       | 
       | As another example, contrary to what some people say, it's not
       | necessarily unsound to have a safe function that can cause UB on
       | some inputs. You just have to ensure that external code cannot
       | call that function (without having previously unsafely promised
       | not to call it incorrectly), and no internal code calls it
       | incorrectly. Here, the interface boundary is the entire section
       | of your code that has access to the function.
       | 
       | In particular, you sometimes see this with data types that have
       | to implement safe traits using unsafe operations (for performance
       | or for other reasons). Often, the function to create an object is
       | unsafe, since you have to promise that the rest of your safe code
       | does not invoke those trait implementations in such a way as to
       | cause UB. Practically, you can do this by limiting how much of
       | your code has access to the created object.
        
         | mlindner wrote:
         | > Knowing your interface boundary is an important part of
         | writing unsafe code in Rust. For instance, suppose you have a
         | data type with a method that unsafely makes a certain
         | assumption about its fields. Then you have to be very careful
         | to uphold this invariant in every other safe method that
         | creates or modifies those fields. Furthermore, you have to
         | ensure that no language-level operations on the object as a
         | whole (e.g., swapping, dropping, forgetting, or covariant
         | transformations) can break this invariant. But once you have
         | locally checked all of those things, then you don't need to
         | check any other part of the code base to know that your data
         | type is sound.
         | 
         | This is simply incorrect. If it is possible to, in any safe
         | code, invoke the invariants that can cause the undefined
         | behavior then your code is unsound. UnSafe code _cannot_ rely
         | on safe code invoking it correctly. ALL invariants must be
         | covered at the boundary between safe and unsafe code, anything
         | else is a soundness bug in the code.
         | 
         | > As another example, contrary to what some people say, it's
         | not necessarily unsound to have a safe function that can cause
         | UB on some inputs. You just have to ensure that external code
         | cannot call that function (without having previously unsafely
         | promised not to call it incorrectly), and no internal code
         | calls it incorrectly. Here, the interface boundary is the
         | entire section of your code that has access to the function.
         | 
         | This is again completely incorrect. If it is possible to invoke
         | unsound behavior from safe code then the code is fundamentally
         | unsound. This is the problem that the Actix developer kept
         | trying to justify that got him in a lot of hot water with the
         | community. Please never write code this way. If you have any
         | public repositories please mention them so bugs can be filed
         | against them.
         | 
         | Put another way, a bug in any piece of safe code must never
         | cause any piece of unsafe code to commit undefined behavior.
         | That's the fundamental guarantee that the entire language is
         | based on. Please don't violate that by lying to developers that
         | code is safe when it'a not.
        
           | LegionMammal978 wrote:
           | > This is simply incorrect. If it is possible to, in any safe
           | code, invoke the invariants that can cause the undefined
           | behavior then your code is unsound. UnSafe code _cannot_ rely
           | on safe code invoking it correctly. ALL invariants must be
           | covered at the boundary between safe and unsafe code,
           | anything else is a soundness bug in the code.
           | 
           | That's roughly what I'm saying? The point is that you have to
           | carefully design your interface so that safe code from the
           | outside is unable to break your invariants, no matter how it
           | invokes your interface.
           | 
           | > This is again completely incorrect. If it is possible to
           | invoke unsound behavior from safe code then the code is
           | fundamentally unsound. This is the problem that the Actix
           | developer kept trying to justify that got him in a lot of hot
           | water with the community. Please never write code this way.
           | If you have any public repositories please mention them so
           | bugs can be filed against them.
           | 
           | If you have a public safe function that can cause UB, then it
           | is indeed unsound. But it is possible to have a private safe
           | function that can only possibly be accessed from a small part
           | of your code base. If you are careful in preserving this
           | invariant within the parts of the code that can directly
           | access the function, then creating a sound interface without
           | direct access to the function is entirely possible. (But this
           | obviously gets more risky, the more code that has access to
           | the function. I'll grant that it's not an uncommon pitfall to
           | assume a crate-wide invariant somewhere that you later forget
           | about.)
        
             | codeflo wrote:
             | You even have this phenomenon inside single functions.
             | Let's say you write a function that's supposed to be a
             | safe/sound abstraction. You might be setting up a pointer,
             | maybe determined by a dozen lines of codes, that is then
             | finally dereferenced. You'd typically only put the unsafe
             | block around this final pointer access, but its safety
             | depends on the whole calculation being correct.
        
               | zozbot234 wrote:
               | Unsafe _blocks_ are very different from unsafe-marked
               | _functions_. A function that can indirectly cause UB when
               | called in safe code _must_ be marked as unsafe, so that
               | the invariants it relies on can be properly documented in
               | a Safety: comment, and manually checked by callers.
        
               | LegionMammal978 wrote:
               | Only if it's public, and you don't know who the callers
               | are. If you do know exactly who the callers are (since
               | it's a private function), and you have internal
               | documentation that lets contributors know of the proper
               | invariants, then it's entirely possible to prevent UB
               | from ever occurring even if the function is marked as
               | safe.
               | 
               | By itself, this is usually not a good idea. But in some
               | cases, it's necessary, e.g., an unsafe implementation of
               | an external safe trait on an internal type that only your
               | internal code will ever see.
        
               | zozbot234 wrote:
               | Why? If you have a non-public function and all its
               | potential callers are known, what's the harm in marking
               | it as unsafe and documenting its invariants in the
               | idiomatic way (a Safety comment)? Your case of
               | implementing a safe trait via unsafe code seems like it
               | should be quite rare, and even then the trait
               | implementation should document the assumptions it's
               | relying on as part of an unsafe block. If you neglect to
               | mark other possibly-UB functions as unsafe, this becomes
               | harder to ensure.
        
               | LegionMammal978 wrote:
               | I agree that it's rarely _a good idea_ to do this; it 's
               | a red flag at minimum when you see it. I'm just trying to
               | reject the notion that _it 's categorically unsound_ to
               | do this. I've personally seen the safe-trait example
               | while reviewing a crate, and there were indeed plenty of
               | comments documenting the assumptions. As long as there's
               | absolutely no way for anyone to cause these assumptions
               | to be broken from the outside, there's no way to cause
               | UB, and it's not really useful to call the interface
               | unsound.
        
         | humanrebar wrote:
         | > ...soundness is really a property of an entire interface.
         | 
         | Not really. I mean, maybe... if you limit the scope of your
         | consideration to language design itself.
         | 
         | If Rust call out to C code and they were using incompatible
         | understanding of an array length due to mismatching build rules
         | (like preprocessor definitions), you have a soundness bug it
         | won't show up obviously in any interfaces anywhere.
         | 
         | Soundness is a property of a single build of a program. You
         | cannot generalize it. That is why in safety critical software,
         | you cannot buy "safe" libraries or tools. You can buy libraries
         | and tools with certification packets but context is very
         | important when it comes to program correctness in all its
         | forms.
        
           | oconnor663 wrote:
           | > Soundness is a property of a single build of a program. You
           | cannot generalize it.
           | 
           | I think it's important to be able to talk about sound
           | functions, without needing to know anything about their
           | callers, other than what's expressed in the function
           | signature.
        
             | humanrebar wrote:
             | I'm saying even for that case, you have to know how the
             | implementation of that function was compiled, linked, and
             | possibly even deployed. You cannot promise an interface is
             | "sound". You can promise certain expectations were
             | communicated clearly within some parameters (like object
             | types and lifetimes assuming a coherent build process).
        
           | LegionMammal978 wrote:
           | > If Rust call out to C code and they were using incompatible
           | understanding of an array length due to mismatching build
           | rules (like preprocessor definitions), you have a soundness
           | bug it won't show up obviously in any interfaces anywhere.
           | 
           | In that scenario, I'd say that the build rules are a poorly-
           | documented part of the C code's interface, and by calling out
           | to it with mismatching build rules the Rust code is unsound.
           | 
           | In general, C interfaces tend to have a lot of poorly-
           | documented or undocumented preconditions, which is one of the
           | things that make Rust-to-C FFI so tricky. One of Rust's big
           | ideas is to make all these preconditions fully explicit,
           | either through typestate or through "Safety" sections on
           | unsafe functions and traits.
        
             | humanrebar wrote:
             | Bad docs and brittle build expectations make C to C "ffi"
             | tricky as well. But it's realistically how this kind of
             | programming, generally speaking, tends to happen.
             | 
             | I don't expect we'll have a great end to end solution until
             | there are language agnostic build and dependency management
             | systems available. As much as I like cargo, I don't expect
             | porting everything to cargo will cut it. At least not in
             | its current form.
        
         | Animats wrote:
         | > Then you have to be very careful
         | 
         | The problem.
        
           | LegionMammal978 wrote:
           | Well, in principle, you could place each field in its own
           | wrapper type that is unsafe to access. But you'd still have
           | to be nearly as careful: there's no way around that, short of
           | formal verification. Rust is ultimately a pragmatic language,
           | and it lets the user pick their preferred balance between
           | decreased performance, extra care, or extra line noise that
           | people will get tired of reading. At best, it can give
           | suggestions in the design of its standard library of where
           | that balance ought to be.
        
         | oconnor663 wrote:
         | I think what you're describing might be similar to
         | footnote/marginnote #6?
        
           | LegionMammal978 wrote:
           | Sorry, I was reading the article from my phone and didn't
           | notice that footnote. It definitely ties in to the point I'm
           | making here, but I still feel that the distinction is
           | important enough to elaborate on a bit.
        
       | dataflow wrote:
       | I feel like #![forbid(unsafe_code)] deserves a mention?
        
         | oconnor663 wrote:
         | This starts to open up a whole topic that I though about
         | including but ultimately decided not to: How do we deal with
         | unsafe code in our dependencies? This gets into all sorts of
         | tools like cargo-geiger and blessed.rs and things like that.
         | 
         | Luckily, I think the Python/Java metaphors work well here. Rust
         | apps have to worry about unsafe code in their dependencies,
         | like Python and Java apps have to worry about C code in their
         | dependencies. So hopefully not _too_ much is lost by leaving
         | those questions for another article. Maybe I should add another
         | footnote though.
        
       | Animats wrote:
       | Oh, if only that worked in practice.
       | 
       | There's far too much use of "unsafe" in Rust crates. Sometimes,
       | for really good reasons. Too often, because someone can't figure
       | out a safe way to do something.
       | 
       | Here's a thread I started on Reddit on this subject: "We're not
       | really game yet".[1]
       | 
       | Someone commented there: _" There are people here who would
       | volunteer to help you out, dig up the internals and address those
       | issues, given you provide them with sufficient information."_
       | 
       | My reply:
       | 
       | I've done that three times now:
       | 
       | * jpeg2000-decoder -- JPEG 2000 decoder test fixture. This
       | exercises jpeg2k->jpeg2000-sys->OpenJPEG. The first two are in
       | Rust, the last one is in C, and valgrind shows it referencing un-
       | initialized memory. It randomly segfaults. OpenJPEG has a long
       | history of doing this, and has been the subject of several CERT
       | security advisories. The author of jpeg2k has managed to contain
       | the the problem by running OpenJPEG in a WASM sandbox. This keeps
       | the program from crashing, but there is a 2.6x performance
       | penalty. A bug report has been submitted to the OpenJPEG
       | maintainers, who are funded by universities and companies but
       | over 200 issues behind.
       | 
       | * ui-mock -- game GUI test fixture This exercises
       | rfd->egui->rend3->wgpu. It's a game GUI with menus and dialogs,
       | but no game behind it, just a 3D drawing of a cube. It's useful
       | for making bugs in that stack repeatable. That's been helpful in
       | wringing out obscure bugs in egui.
       | 
       | * render-bench -- scene update performance test fixture. This
       | exercise rend3->wgpu->vulkan. It draws a city of identical
       | buildings, then, from a second thread, periodically deletes half
       | of them and re-creates them. If the stack is performing as
       | intended, the updates from the second thread should not impact
       | the frame rate from the main thread. But due to lock problems at
       | the WGPU level, the frame time goes from 16ms to 700ms when the
       | update happens.
       | 
       | (Those are all projects on my Github [2], by the way, if anyone
       | wants them.)
       | 
       | Each time I have to do one of those bug-reproduction test fixture
       | projects, it costs me substantial time not spent on the main
       | project.
       | 
       | Right now, I'm totally stopped by a race condition crash bug not
       | in the list above, one for which I don't have a standalone
       | project which can duplicate the bug. They're trying to fix it,
       | but for now I'm stuck. I may have to build a custom test project.
       | But it will be tough, because it's a timing-dependent bug that
       | only appears under load.
       | 
       | This is why no one has successfully done a major game on Rust.
       | The foundations are too weak to support it. The right stuff is
       | there, but much of it is stuck at "sort of works". It's a real
       | question whether game development in Rust will ever have tools
       | that Just Work, or whether parts of the ecosystem will go
       | directly from "sort of works" to "abandoned" without ever
       | reaching Just Works.
       | 
       | Safe Rust really works. My own code is 100% safe Rust, 36,000
       | lines of it. No obscure crashes in my own code. When I've needed
       | gdb or valgrind, it's always been due to a problem in someone's
       | unsafe Rust or C code.
       | 
       | There are people who think they're so good they can write unsafe
       | code.
       | 
       | They're not.
       | 
       | [1]
       | https://www.reddit.com/r/rust_gamedev/comments/11b0brr/were_...
       | 
       | [2] https://github.com/John-Nagle
        
         | nindalf wrote:
         | I might have misunderstood what you've written, but is there a
         | reason you can't use https://github.com/etemesi254/zune-image
         | as a JPEG decoder? It has minimal use of unsafe and is
         | performant.
        
           | erk__ wrote:
           | Jpeg 2000 is not the same codec as Jpeg, it was an attempt to
           | make a more modern image format, but it never really caught
           | on outside of science and medical imaging. For example
           | satalite images are often jpeg 2000
        
           | Animats wrote:
           | That's a classic JPEG decoder, not a JPEG 2000 decoder.
           | They're completely different compression systems. JPEG is
           | discrete cosine transform, while JPEG-2000 is wavelet. JPEG
           | 2000 never really caught on; it's a good compression
           | algorithm but has too many features. JPEG 2000 is used mostly
           | for medical imagery, because you can efficiently zoom in on
           | an area and the most detailed level can be compressed
           | losslessly. It's also used for some virtual world content,
           | because you can read the beginning of the file and get a
           | lower-rez version.
        
             | nindalf wrote:
             | Yeah fair enough. It might be worth requesting the
             | maintainer of zune-image to add a decoder for jpeg2000. The
             | repo already supports a whole bunch of formats already.
        
         | Ygg2 wrote:
         | > This is why no one has successfully done a major game on
         | Rust. The foundations are too weak to support it.
         | 
         | That's patently false. https://veloren.net/
         | 
         | People have made games in it. And in Zig, and C, and C++ and
         | so. And assembly. And machine code. Unsafety isn't a problem
         | it's lack of libs and integration.
         | 
         | If you refer to AAA the issue is lack of engines where Rust is
         | a must. I.e. network effect, you need engines in Rust to make
         | games in Rust. And to make engines in Rust, more games need to
         | require Rust.
        
           | Thaxll wrote:
           | Veloren is not a major game, it's not even considered a
           | successful indy game.
        
         | olah_1 wrote:
         | > This is why no one has successfully done a major game on
         | Rust. The foundations are too weak to support it.
         | 
         | The Finals open beta is happening on the 6th. Check it out
         | https://www.reachthefinals.com/
         | 
         | Here is a blog post about their usage of Rust:
         | https://medium.com/embarkstudios/inside-rust-at-embark-b82c0...
        
           | Thaxll wrote:
           | This game uses Unreal engine so it's most likely all in C++
           | or most of it.
           | 
           | We don't really know about the state or what game is built in
           | Rust a Embark, but 2 majors games they work on are in C++.
        
       | ridiculous_fish wrote:
       | > Instead, the only way I can think of to make foo1 commit UB is
       | to give it an uninitialized index
       | 
       | Nah a `fclose(stderr)` before calling it will do it!
       | 
       | > A safe caller can't be "at fault" for memory corruption or
       | other UB.
       | 
       | An example of where a safe caller can be at fault is after fork,
       | or in a signal handler. Functions like malloc must not be called,
       | but Rust does not model this and freely invokes malloc from safe
       | functions.
       | 
       | In these regimes safety is inverted: File::open is NOT safe, and
       | may invoke UB; while the "unsafe" libc::open IS safe and should
       | be used instead.
        
         | burntsushi wrote:
         | I wouldn't agree with this. In order to register a signal
         | handler in Rust, you have to utter unsafe somewhere. That's the
         | point at which you form the contract that your signal handler
         | is correct. The fault is not with the safe code that allocates.
         | The fault is with the creation of the signal handler.
         | 
         | It is true that Rust could model signal safety in some richer
         | way, but that's just a matter of shifting where the contract is
         | formed.
        
       | codeflo wrote:
       | This is a very good explanation.
       | 
       | It's sad that these terms are so counterintuitive. I still
       | regularly have to clarify the confusion between undefined and
       | unspecified behavior. And to be fair, "define" and "specify" are
       | basically synonyms in everyday language, it's evil that these
       | terms have so wildly different meanings. Quite a bit of confusion
       | is caused by people not fully understanding that "unsafe" in Rust
       | actually just means "unchecked", i.e. not verified by the
       | compiler, and not necessarily "insecure".
       | 
       | Now we add "unsound" to the mix, and I fear that all of this is
       | simply too hard to learn.
       | 
       | Maybe we should reverse all those terms to be positive, and also
       | use words in their intuitive meaning, and also avoid confusing
       | almost-synonyms. Here's a quick attempt:
       | 
       | 1. "Well-behaved" code would be code that only accesses memory
       | correctly. (That's not fully precise enough. What I actually mean
       | of course is "no UB", but that's hard to put into words if you
       | don't already know what UB is. Let's say well-behaved code only
       | does "allowed stuff".)
       | 
       | 2. "Checked" code is code verified by Rust's compiler to be well-
       | behaved if certain invariants are fulfilled. Code is always
       | checked by default unless you opt out with a special keyword
       | (confusingly called "unsafe").
       | 
       | 3. "Sound" code is well-behaved code (that may or may not be
       | checked by the compiler) with the additional restriction that
       | will remain well-behaved no matter how it's embedded into checked
       | code. This is what enables "safe abstractions". Checked code is
       | automatically sound by this definition, but alternatively,
       | unchecked code could also be sound, which would have to be
       | manually verified somehow. To do this, you obviously need to know
       | which assumptions the checker makes in its reasoning.
       | 
       | (Edit: Reworded a bit to address an unexpected source confusion.
       | What's now called "well-behaved" was called "safe", but that has
       | multiple meanings. Some of the responses in the thread apply to
       | the old version.)
        
         | hitekker wrote:
         | To follow your recommendation would require defining Rust as a
         | memory-safer language, not as the language of memory safety.
         | That will take time due to history.
         | 
         | Defining safety was stonewalled by the previous maintainers of
         | Rust who enshrined their nebulous idea of safety as the source
         | of the language's identity. These maintainers focused their
         | efforts on evangelizing and expanding Rust, at the expense of
         | governing it. They believed they could enforce their idea of
         | safety by arguing with people on Twitter or HN, instead of
         | writing a formal specification or a constitution. The latter is
         | hard work, the former is easy and also has the added benefit of
         | the motte-and-bailey game, i.e. talking out of both sides of
         | the mouth.
         | 
         | Following highly dramatic people problems, those maintainers
         | have since been ejected[1][2] and replaced with people who
         | seems to actually care about the governance of Rust[3][4].
         | 
         | [1]
         | https://www.theregister.com/2022/02/01/rust_core_team_depart...
         | 
         | [2] https://www.rust-lang.org/governance/teams/core
         | 
         | [3] https://github.com/rust-lang/rfcs/pull/3355,
         | https://blog.m-ou.se/rust-standard/
         | 
         | [4] https://thenewstack.io/rust-project-reveals-new-
         | constitution...
        
           | burntsushi wrote:
           | As a former member of the Rust mod team, and a continuing
           | member of the Rust project, this narrative is complete
           | bullshit.
        
           | codeflo wrote:
           | > To follow your recommendation would require defining Rust
           | as a memory-safer language, not as the language of memory
           | safety.
           | 
           | I'm actually suggesting changes in explanation only, hoping
           | to make these terms less confusing for beginners. Rust
           | veterans know that "safe" code just means "code not in an
           | unsafe block", beginners are often confused by the more
           | common meaning of the word.
        
         | LegionMammal978 wrote:
         | I don't think your attempt would be quite accurate: safe code,
         | even though it is checked, can cause UB. This can only occur if
         | some earlier unsafe code was unsound, by the current
         | definition.
         | 
         | For instance, suppose you have an ordinary Box<i32>. Then, you
         | dereference it to create a local &i32 reference, and use unsafe
         | code to turn it into a &'static i32 reference. Then, you drop
         | the Box<i32>, which deallocates the backing memory. Finally,
         | you attempt to read the value from the &'static i32.
         | 
         | The UB occurs only at the last step, when you read from
         | deallocated memory. But reading from a &'static i32 reference
         | is completely allowed in safe, checked code. That's why we use
         | the term "unsound" to cast fault on the earlier unsafe code
         | which allowed us to do this.
        
           | skitter wrote:
           | Just holding an invalid reference is UB, even if you don't do
           | anything with it (the example still works as the reference
           | gets invalidated by safe code).
        
             | oconnor663 wrote:
             | I think there are some aspects of this rule that are still
             | undecided. See for example:
             | 
             | - https://github.com/rust-lang/unsafe-code-
             | guidelines/issues/8...
             | 
             | - https://github.com/rust-lang/miri/issues/2732
        
             | LegionMammal978 wrote:
             | A reference is never really "held": from a language-
             | semantics standpoint, it only exists when it is actually
             | used. In this example, copying, reborrowing, or accessing
             | the reference would be UB, but simply letting it fall out
             | of scope would not be UB (modulo the UCG issue oconnor
             | mentioned; but I personally doubt that this status quo will
             | change). You can try this yourself with Tools > Miri on the
             | Playground (https://play.rust-
             | lang.org/?version=stable&mode=debug&editio...). The
             | distinction is far more relevant for unsafe code than for
             | safe code.
        
           | oconnor663 wrote:
           | Yeah this is a great example of why formally defining
           | "soundness" is so tricky. Not only do we have to worry about
           | unsafe code committing UB, we also have to worry about it
           | setting up a situation where safe code might commit UB later.
           | Soundness is a property of functions and modules, but that
           | property is a statement about the entire program that
           | contains them, or actually about _any_ program that _could_
           | contain them.
           | 
           | In retrospect, I kind of wish I had asked somebody like Ralf
           | Jung to review this post before I published it. On the other
           | hand, the fastest way to get an answer on the internet is to
           | post the wrong answer :-D
        
           | codeflo wrote:
           | That's a very good example. I think that's what I meant by:
           | 
           | > "Sound" code is safe code (that may or may not be checked
           | by the compiler), that will remain safe no matter how it's
           | called by checked code.
           | 
           | Maybe "called" was too specific, it could be embedded in
           | other ways. But it's clear in your example that there's a way
           | of integrating the unsafe cast into otherwise fully checked
           | code that leads to UB. Hence, it is not sound.
        
       ___________________________________________________________________
       (page generated 2023-03-05 23:01 UTC)