Private unsafe fields are a poorly motivated feature

I also have some thoughts on robust

When I'm writing safe code and see a function NonZero.get() that is not robust then how can I trust this function in safe code. The Author clearly doesn't trust the correctness of the function. Safe code can still leak the entire DB.

So robust should the only be added to properties that are not relevant to safe code (like the validity of as_mut_ptr()). But i really don't trust people to only use it for these things. (otherwisewe have the everythingis robust problem again)

If tooling requires robust for unsafe code then people won't add robust to functions because they trust the implementation. They will add it if it is necessary. So we'd recursively add robust to everything until it is meaningless.

Also i don't see a big difference in the finding and fixing of bugs between robust and non robust functions. In both cases the Author needs to be notified to fix the bug.

Yes, I agree. That's why I haven't been arguing for unsafe fields. I think they don't pull their weight in most cases, and public unsafe fields seem like an anti-pattern.

This is a common theme across all unsafe code. You will have to deal trust safe code at some point. This can be fine when it is some concrete safe code. Like a dependency using trusted types/functions/impl blocks. The issue only arises when using arbitrary safe code, such as from insufficiently constrained generic parameters.

For example, we have the entire API of core::alloc::Layout, where a user can use only the safe API of Layout and needs it to be correct for soundness (otherwise they aren't allocating the correct amount of memory, or are deallocating with a mismatched layout).

This is quite similar to your TwoDigit API. Ignoring that the actual Layout is implemented with a lot of unsafe code. Pretty much all of it can be removed, just at a significant runtime cost (with no change to the behavior of the API).

Across the ecosystem, bitflags is likely being used for soundness, and the only unsafe it has (including in macros) is to implement some traits from bytemuck. All the code to manipulate the bitfields is safe code.

I don't think we can ever get away from unsafe code relying on some concrete safe code.

I agree with you here. One thing to consider is that some people like to make abstraction boundaries within a single crate. So having more tools to help there can be useful.

3 Likes

Obviously there's some levels of community norms, and author's personal meanings of statements like this. But I would hope that making guarantees like this would normally be backed up by work to actually prove it, whether that's actual mathematical proofs, very high coverage fuzzing, or a consistent overly thorough manual review process. It's the sort of work that most crates should not be doing, because it is a lot of extra work compared to just normal testing for general correctness; and so the few that do the work and mark their APIs this way are the outliers and these sorts of claims can be checked.

1 Like

If you're equally confident about both guarantees, why bother stating a separate weaker guarantee at all? Why not call the regular behavior of NonZero "robust"? It's easier to deal with one specification than with two specifications.

I should have said "not only confident". Before all, it's about commitment, responsibility, maintenance, clarity, etc. My problem with confidence, is that it makes it look like it's about trust. This is a consequence of the API choice, not the API choice itself. You pay more attention to your safety properties than your correctness properties. That said, for trivial libraries, it might be simpler to have a single property: correctness. Because the cost of tracking 2 independent ones is not worth it. That's just part of the API choice. For libraries where correctness is highly non trivial, it makes a lot of sense to have simpler safety (possibly only what safe code gives you).

1 Like