Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Result with stable size, alignment, and ABI guarantees ? #1035

Open
cpu opened this issue Dec 30, 2024 · 1 comment
Open

Use Result with stable size, alignment, and ABI guarantees ? #1035

cpu opened this issue Dec 30, 2024 · 1 comment

Comments

@cpu
Copy link

cpu commented Dec 30, 2024

👋 Hi folks,

Since RFC 3391 (rust-lang/rfcs#3391) the Result type's documentation has a Representation section that describes conditions where Result<T, E> can have the same size, alignment and ABI guarantees as Option<U>.

E.g.

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<NonZeroI32>. The only difference is the implied semantics:

Option<NonZeroI32> 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'm trying to lean on this guarantee to implement FFI for a Rust function that wants to use the implied semantics of returning Result<(), NonZeroU32> to mean "a non-zero u32 error result, if any":

pub const MAY_FAIL_ARG_ZERO_ERR: u32 = 99;

#[no_mangle]
extern "C" fn may_fail(arg: u32) -> Result<(), NonZeroU32> {
    match arg == 0 {
        true => Err(NonZeroU32::new(MAY_FAIL_ARG_ZERO_ERR).unwrap()),
        false => Ok(()),
    }
}

This code compiles, and generates no warnings about unsafe FFI type usage with rust stable.

However, the cbindgen (0.27.0) result is not what I expect:

#define MAY_FAIL_ARG_ZERO_ERR 99

typedef struct Result_u32 Result_u32;

struct Result_u32 may_fail(uint32_t arg);

If I change may_fail to return Option<NonZeroU32>, I get the results I expected, but I've lost the implied semantics I want to maintain on the Rust-side:

#define MAY_FAIL_ARG_ZERO_ERR 99

uint32_t may_fail(uint32_t arg);

Is there a workaround I could use to get cbindgen to play nice with "stable representation" Result instances? Is my understanding flawed in some other way?

Thanks!

@emilio
Copy link
Collaborator

emilio commented Jan 1, 2025

No, I think what you want to do should ideally just work, but it isn't implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants