-
Notifications
You must be signed in to change notification settings - Fork 36
Description
In error.rs we define a repr(u32) enum, rustls_result, and use it throughout the API as an ABI-stable, FFI-friendly way of representing what a Rustacean would probably write as Result<(), NonZero<u32>>. This is mechanically safe, but also makes the Rust code awkward, in particular precluding use of ?. It also reduces type-safety by using the same type for both success and error (rustls_result::OK & rustls_result::* vs Ok(()) & Err(_) variants).
It was pointed out to me a while ago by @complexspaces that Result is documented to have the same stable ABI/repr as Option<T>, subject to some caveats. The docs say:
In some cases, Result<T, E> will gain the same size, alignment, and ABI guarantees as Option has. One of either the T or E type must be a type that qualifies for the Option representation guarantees, and the other type must meet all of the following conditions:
- Is a zero-sized type with alignment 1 (a “1-ZST”).
- Has no fields.
- Does not have the #[non_exhaustive] attribute.
For example, NonZeroI32 qualifies for the Option representation guarantees, and () is a zero-sized type with alignment 1, no fields, and it isn’t non_exhaustive. This means that both Result<NonZeroI32, ()> and Result<(), NonZeroI32> have the same size, alignment, and ABI guarantees as Option. The only difference is the implied semantics:
- Option is “a non-zero i32 might be present”
- Result<NonZeroI32, ()> is “a non-zero i32 success result, if any”
- Result<(), NonZeroI32> is “a non-zero i32 error result, if any”
I think changing our API to use Result<(), NonZeroU32> would be an ergonomics/safety improvement for the Rust code assuming there aren't any additional "gotchas" lurking here. My thinking is we would rename rustls_result to rustls_error and remove the rustls_result::Ok value.
From an API compatibility standpoint my main concern is how invasive this will be due to rustls_result::Ok being defined as 7000 presently. AIUI a caller that got an Ok(()) from an extern "C" fn rustls_foo() -> Result<(), NonZeroU32> would now have 0 in hand, as opposed to 7000. This is mainly a concern if someone wasn't using the .h constants (e.g. if(rr == 7000) vs if(rr == RUSTLS_RESULT_OK)) - it seems to me it's fair to say those people (if they exist) are already begging for bugs?
We have a sem-ver incompatible 0.15.0 release coming up Soon ™️ so maybe we should do this?