Skip to content

Implement more ergonomic rustls_result? #513

@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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions