rust-lang / rust-lang/rust-bindgen
The API generated for objective-c categories is inconvenient and requires implementation knowledge
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 5.3k
- Forks
- 829
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 15
Description
Bindgen code from objective-c categories results in traits like CLASS_CATEGORY to be generated. This is not intuitive for a few reasons:
- I didn't even realize that bindgen supported categories. I was allowlisting the classes I wanted and saw that methods were missing. I looked in the files and realized that those methods were in category blocks and just assumed that it was unsupported. Only later did I realize that I had to allowlist
CLASS_CATEGORY(or more broadlyCLASS_.*). - Category names (and contents!) are implementation details. They are used in the implementation to organize things but are not exposed in any way in normal objective-c APIs. Creating a separate rust API is an unfortunate mismatch for developers.
- Because the categories are traits, you need to import them by name. I'd prefer not to use
use my_crate_bindings::*to pull in all the category traits, so I need to know their names to import them.
This is a tricky problem to work around, but if there is some way to address some of these points it'd make the developer interaction with the generated code more similar to the interaction when using objective-c.
Input C/C++ Header
@interface Foo
-(void)method;
@end
@interface Foo (BarCategory)
-(void)categoryMethod;
@end
Bindgen Invocation
$ bindgen input.h --objc-extern-crate -- -x objective-c
Actual Results
/* automatically generated by rust-bindgen */
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
#![cfg(target_os = "macos")]
#[macro_use]
extern crate objc;
#[allow(non_camel_case_types)]
pub type id = *mut objc::runtime::Object;
#[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 method(self)
where
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
msg_send!(self, method)
}
}
impl Foo_BarCategory for Foo {}
pub trait Foo_BarCategory: Sized + std::ops::Deref {
unsafe fn categoryMethod(self)
where
<Self as std::ops::Deref>::Target: objc::Message + Sized,
{
msg_send!(self, categoryMethod)
}
}
And in code using the generated code:
use my_crate_bindings::{Foo, IFoo, Foo_BarCategory};
let f = Foo::alloc();
unsafe { f.categoryMethod() };
Expected Results
I'm not entirely sure the best approach, but some suggestions:
- Maybe
allowlist_item("Class")can include categories automatically? I figure this is somewhat unlikely given that the allowlist acts on the rust API. - Change all categories into
impl Fooinstead. This is how I expected bindgen to work, though it doesn't mesh well with inheritance (see #1779) and then thetrait IFoomight seem weird. I don't think there's a strict need to keep them separate given that categories are invisible in the objective-c API and exist purely in syntax. Alternatively you could merge all included categories into the onetrait IFoo(and then inheritance will work, at the cost of what I imagine would be a bit more juggling in the bindgen logic). - Put all category traits into a module (either as a whole for everything generated or for each class). For example, I would be much less hesitant to
use my_crate_bindings::{Foo, FooCategories::*}. This avoids my needing to know the category names, though I still need to be aware of how bindgen implements categories.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the bindgen invocation and Objective-C category example in this issue, then trace how allowlisting the class and its category traits affects the generated API. Done should be a decided API improvement that makes category methods discoverable and usable without requiring callers to know category trait names.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- objective-c, rust
- Domain
- developer-experience, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100