Skip to content
New issue

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

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

Already on GitHub? # 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.

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

No branches or pull requests

2 participants