Feature idea: Defining custom variants of unsafe

I agree with those other developers that using unsafe only for things that can lead to unsoundness is critical. We already have trouble with people thinking "how bad can it be?" for things that actually can lead to unsoundness (UTF-8 in str being a common one), so the more things use unsafe for things that can only cause logic errors the more of a problem that can become. Remember that plenty of bad things are perfectly "safe" in the soundness sense. It's safe to delete all your photographs. It's safe to upload your financial records to pastebin. It's safe to submit limit sell orders at $0.01 for all your stock. Etc.

So generally the way to do "tricky but not unsound" is with names much longer than the alternatives. You might have a less-tricky fn get_a(&self) -> &T; and a fn get_a_solemnly_swear_to_do_good(&mut self) -> &mut T. (Ok, maybe not quite that long of a name. And even the &T version isn't infallible if it has internal mutability, though of course usize doesn't. But I'm also unclear whether usize is your actual case, since for something small and copy like that the &mut is rarely critical.)

Now, all that said, are there any situation in which you could use DisjointPair such that you're relying on them being different for soundness of unsafe code? Because it's acceptable to require unsafe for things that can break those invariants if it's needed for soundness. For your fake example, imagine you wanted to have

impl DisjointPair {
    fn wrapping_distance(&self) -> NonZeroUsize {
        let d = self.a.wrapping_sub(self.b);
        // SAFETY: it's a soundness invariant of the type that they must be different, so the subtraction can never yield zero
        unsafe {
            NonZeroUsize::new_unchecked(d)
        }
    }
}

Then it would be necessary for a &mut Self -> &mut usize method to be unsafe, as otherwise safe code could cause UB.

So you have to decide which kind of invariant you're looking for.

4 Likes