Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Encapsulating unsafe code behind a safe interface is not so easy (especially when cycles like these are involved) and there's always the danger of getting it wrong, so you generally want to avoid doing that.

Yes, it's true. Most of the time I had logic errors only in safe code and segfaults caused by unsafe code. But it's not a problem after C, where segfaults and memory leaks are norm.

> Did you mean that the safe interface should be built around the whole tree rather than just the parent reference?

Safe and sound (bulletproof) façade for the pointer and the tree. Safety is overloaded term in Rust. For example, when indices are used instead of references, it easy to make memory management mistakes invisible to rust compiler. Such code is "safe" in Rust terms, but unsound.



> Safe and sound (bulletproof) façade for the pointer and the tree

That doesn't really answer my question, the façade should be either:

- at the level of the pointer, meaning it offers the user all the functionality they need to code the tree data structure; - at the level of the tree, meaning the tree externally offers the user all the functionality they wanted from the tree, but the tree internally uses `unsafe` and maintains invariants that make its use sound.

> Such code is "safe" in Rust terms, but unsound.

You might not want to use unsound here, since that's overloaded too. The way I usually see and use those terms in a Rust context is:

- safe: code that does not use `unsafe` - sound: a safe interface (i.e. callable from safe code) that internally uses `unsafe`, but there is no way for the safe side to produce UB when calling it.

I would refer to the index code as "incorrect", "erroneous" or "buggy" instead to avoid the overload in this context.


UB and unsoundness are different things. "If it compiles, then it works (properly)" is true for sound code only. Direct indexing of arrays with `for` loop is unsound, because of typical errors with indexes, such as off by one. Iterators are sound.

I want to debug my code less, so I use sound interface to arrays: iterators. Other than that, both interfaces are equal.

If there is no ready to use method for my usage pattern, I can write unsound code directly, or I can write sound interface and then write sound code using it, so compiler will not allow me to write stupid mistakes. Unsound version will be faster to write, but may require more time to debug and support.


> UB and unsoundness are different things. "If it compiles, then it works (properly)" is true for sound code only.

In programming language theory soundness is a property of a type system for which the safety theorem hold. In other words, if some program is well-typed according to such type systems then its execution following the rules of the language abstract machine reduces to some terminal value. "Undefined behaviour" is the opposite situation, where some program reaches a non-terminal state where no abstract machine rules applies to make progress, meaning the abstract machine does not describe or define the behaviour of that program and state pair.

In Rust's case the claim is that this holds only as long as you use its safe subset, in other words "rust's type system is sound for `unsafe`-less programs". This can then be extended to some particular `unsafe` code, making the claim "rust's type system is sound for `unsafe`-less programs plus this particular function/code", which is what is generally meant with "this unsafe code is sound".

Most (all?) type systems don't try to define what it means for a program to "work (properly)", that is generally left to the programmer. For those that do, then yes, unsoundness would mean that an accepted program does not work properly. I find it hard to imagine such a programming languages, though surely I will expect this to be parameterized over some specification that the type system assumes to be the final desired property. In any case this is far from what Rust promises.


I agree completely, but I should point to a problem: it's easy to implement unsound virtual machine on top of sound language. When developers uses indices to simulate references, to circumvent borrow checker, they are doing exactly that. Then again, a sound façade can be built around unsound code, like `indextree` crate does:

> This arena tree structure is using just a single `Vec` and numerical identifiers (indices in the vector) instead of reference counted pointers. This means there is no `RefCell` and mutability is handled in a way much more idiomatic to Rust through unique (`&mut`) access to the arena. The tree can be sent or shared across threads like a `Vec`. This enables general multiprocessing support like parallel tree traversals.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: