'Is it appropriate to mark functions unsafe if the user's inputs can break invariants? [closed]

It's common in Rust to enforce invariants about the state of types by exposing a limited API. However, invariants can be easily broken if the invariant is difficult or prohibitively computationally expense to check and return an error.

For example, imagine we have a function that takes an integer and, for whatever reason, fails if that integer is a prime number. It may be too expensive to check that the input is not prime. Is it appropriate to mark such a function unsafe as a way of saying, "This method could potentially operate in an unexpected way if the input is prime, but we trust you and don't waste effort checking"?

If such an input could cause obvious undefined behavior (UB), then I imagine the answer is yes. But if it's not immediately clear if it could UB, I am uncertain whether such an API should be complicated with an unsafe attribute.



Solution 1:[1]

If a broken invariant could cause Undefined Behaviour then the convention would usually be to check the invariant in the "obvious" usage of the API, but also provide a less convenient unsafe variant where the user can take responsibility for the checks themselves.

The unsafe variants of functions usually have _unchecked as a suffix. For example:

/// Will panic if `p` is prime
pub fn do_stuff(p: u64) {
    assert!(!is_prime(p), "The argument must not be prime");
    unsafe { do_stuff(p) }
}

/// # Safety
/// It is Undefined Behaviour if `p` is prime
pub unsafe fn do_stuff_unchecked(p: u64) {
    todo!()
}

In a lot of cases it's more appropriate to return a Result rather than panicking. I used panicking in the example to reinforce that unsafe is about memory safety only.

See Also

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1