TL;DR: I sometimes want to make Foo
to be unsafe where pub struct Foo(Inner);
, or want to be warned for use of Foo(_)
in safe context.
Consider the case you want to define newtypes for slice.
/// A string slice type that consists of one or more ASCII digits.
#[repr(transparent)]
pub struct AsciiDigitStr(str);
/// An owned string type that consists of one or more ASCII digits.
pub struct AsciiDigitString(String);
(Another examples would be Utf7Str(str)
, RelativePath(std::path::Path)
, AsciiStr(str)
, ... this pattern would be useful and be used practically in many situations.)
Then, you'll want to define constructors. Consider you don't want to expose unchecked version for some reason.
impl AsciiDigitStr {
/// Creates an `&AsciiDigitStr` from the given string slice, without validation.
///
/// # Safety
/// The given string should not be empty, and should consist of only ASCII digits.
unsafe fn new_unchecked(s: &str) -> &Self {
unsafe { &*(s as *const str as *const Self) }
}
fn new(s: &str) -> Result<&Self, AsciiDigitError> {
validate_ascii_digits(s)?;
unsafe { Self::new_unchecked(s) }
}
}
impl AsciiDigitString {
/// Creates an `AsciiDigitString` from the given string, without validation.
///
/// # Safety
/// The given string should not be empty, and should consist of only ASCII digits.
unsafe fn new_unchecked(s: String) -> Self {
// `Self` (`AsciiDigitString`) is safe `fn(String) -> Self` function, though it shouldn't.
Self(s)
}
fn new(s: String) -> Result<Self, FromStringError> {
if let Err(e) = validate_ascii_digits(&s) {
return Err(FromStringError::new(e, s);
}
Ok(unsafe { Self::new_unchecked(s) })
// It is possible to use `Ok(Self(s))`, but I think it is not desirable.
}
}
In this example, I made AsciiDigitStr::new_unchecked
and AsciiDigitString::new_unchecked
unsafe functions, even when they are private.
This is because they have unchecked preconditions, and they'll result in UB when the conditions are violated.
Making private methods unsafe is still useful to prevent writing UB by accident.
However, I cannot make AsciiDigitString
function (whose type is fn(String) -> Self
) unsafe.
If I write Self(some_unchecked_string)
in safe context by mistake, it compiles without any warnings or errors even if it causes UB.
So what I wanted is:
- a way to make tuple struct typename unsafe function, or
- a way to forbid or warn the use of tuple struct typename as a function in safe context.
By this, developers can prevent silent UB by being notified about AsciiDigitString(some_unchecked_string)
in safe context.
What do you think?