Skip to content

Implement more ergonomic rustls_result? #513

Closed as not planned
Closed as not planned
@cpu

Description

@cpu

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions