rust-lang / rust-lang/rust-bindgen

Add Option return types to Objective-c for nullability attributes

Open
#1,876 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
5.3k
Forks
829
Avg merge
1d 1h
Merged PRs (30d)
15

Description

This is a subtask for #109 and I've been trying just implement it for a while but it's become clear that it's annoyingly nontrivial. Part of me writing this issue is so that it's at least documented.

Input Objc Header
// bindgen-flags: --objc-extern-crate -- -x objective-c
// bindgen-osx-only

@interface Bar
@end

@interface Foo
- (Bar* _Nullable)methodReturningBar;
- (Bar* _Nonnull)methodReturningNonnullBar;
@property (readonly, nonnull) Bar *nonnullBar;
@property (nullable, readonly) Bar *nullableBar;
@end
Bindgen Invocation
$ bindgen input.h --objc-extern-crate -- -x objective-c
Actual Results
/* automatically generated by rust-bindgen 0.54.1 */

#[macro_use]
extern crate objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Bar(pub id);
impl std::ops::Deref for Bar {
    type Target = objc::runtime::Object;
    fn deref(&self) -> &Self::Target {
        unsafe { &*self.0 }
    }
}
unsafe impl objc::Message for Bar {}
impl Bar {
    pub fn alloc() -> Self {
        Self(unsafe { msg_send!(objc::class!(Bar), alloc) })
    }
}
impl IBar for Bar {}
pub trait IBar: Sized + std::ops::Deref {}
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Foo(pub id);
impl std::ops::Deref for Foo {
    type Target = objc::runtime::Object;
    fn deref(&self) -> &Self::Target {
        unsafe { &*self.0 }
    }
}
unsafe impl objc::Message for Foo {}
impl Foo {
    pub fn alloc() -> Self {
        Self(unsafe { msg_send!(objc::class!(Foo), alloc) })
    }
}
impl IFoo for Foo {}
pub trait IFoo: Sized + std::ops::Deref {
    unsafe fn methodReturningBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodReturningBar)
    }
    unsafe fn methodReturningNonnullBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodReturningNonnullBar)
    }
    unsafe fn nonnullBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, nonnullBar)
    }
    unsafe fn nullableBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, nullableBar)
    }
}

The importart parts are the functions:

pub trait IFoo: Sized + std::ops::Deref {
    unsafe fn methodReturningBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodReturningBar)
    }
    unsafe fn methodReturningNonnullBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodReturningNonnullBar)
    }
    unsafe fn nonnullBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, nonnullBar)
    }
    unsafe fn nullableBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, nullableBar)
    }
}
Expected Results

Ideally, this should return Options around things that can be null

pub trait IFoo: Sized + std::ops::Deref {
    unsafe fn methodReturningBar(self) -> Option<Bar>
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodReturningBar)
    }
    unsafe fn methodReturningNonnullBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, methodReturningNonnullBar)
    }
    unsafe fn nonnullBar(self) -> Bar
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, nonnullBar)
    }
    unsafe fn nullableBar(self) -> Option<Bar>
    where
        <Self as std::ops::Deref>::Target: objc::Message + Sized,
    {
        msg_send!(self, nullableBar)
    }
}

This should also apply to parameters in functions. I've kept the nonnull functions because that's a very important use case that we need to preserve.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with the supplied input.h and bindgen invocation, then trace how Objective-C method and property nullability reaches the generated Rust signatures. Done means nullable object returns and parameters use Option while nonnull declarations remain unchanged, including the shown methods and properties.

Written by the indexing model from the issue text.

Assessment

Tech stack
objective-c, rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.