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

Better protocols #293

Merged
merged 27 commits into from
Jan 27, 2023
Merged

Better protocols #293

merged 27 commits into from
Jan 27, 2023

Conversation

madsmtm
Copy link
Owner

@madsmtm madsmtm commented Nov 23, 2022

Fixes #291.

TODO:

  • Finish basic implementation
  • Update documentation
  • Use new implementation for NSCopying, NSMutableCopying, ... Deferred to Fix NSCopying and NSMutableCopying #401
  • Test assembly output to ensure that using dyn like this doesn't incur extra overhead

@madsmtm madsmtm added enhancement New feature or request A-objc2 Affects the `objc2`, `objc2-exception-helper` and/or `objc2-encode` crates labels Nov 23, 2022
@madsmtm madsmtm added the A-framework Affects the framework crates and the translator for them label Dec 23, 2022
@madsmtm madsmtm added this to the objc2 v0.3 milestone Jan 18, 2023
@madsmtm
Copy link
Owner Author

madsmtm commented Jan 26, 2023

A somewhat end-goal is that we can use this in icrate for translating places that take/return something like id<NSMutableCopying + NSFastEnumeration> to:

ProtocolObject<dyn NSMutableCopying + NSFastEnumeration>

// where:
// trait NSMutableCopying: NSCopying { ... }
// trait NSFastEnumeration { ... }

To use such an object, we would like the following implementations to exist:

// Required to use the object at all
impl NSCopying for ProtocolObject<dyn NSMutableCopying + NSFastEnumeration> {}
impl NSMutableCopying for ProtocolObject<dyn NSMutableCopying + NSFastEnumeration> {}
impl NSFastEnumeration for ProtocolObject<dyn NSMutableCopying + NSFastEnumeration> {}

// Would be nice to have
impl ConformsTo<dyn NSCopying> for ProtocolObject<dyn NSMutableCopying + NSFastEnumeration> {}
impl ConformsTo<dyn NSMutableCopying> for ProtocolObject<dyn NSMutableCopying + NSFastEnumeration> {}
impl ConformsTo<dyn NSFastEnumeration> for ProtocolObject<dyn NSMutableCopying + NSFastEnumeration> {}

// Not really that important, but could be cool
impl ConformsTo<dyn NSCopying + NSFastEnumeration> for ProtocolObject<dyn NSMutableCopying + NSFastEnumeration> {}
impl ConformsTo<dyn NSMutableCopying + NSFastEnumeration> for ProtocolObject<dyn NSMutableCopying + NSFastEnumeration> {}

// For an object `MyClass` that implements the protocols, these are required
impl NSCopying for MyClass {}
impl NSMutableCopying for MyClass {}
impl NSFastEnumeration for MyClass {}

// And these would be expected
impl ConformsTo<dyn NSCopying> for MyClass {}
impl ConformsTo<dyn NSMutableCopying> for MyClass {}
impl ConformsTo<dyn NSFastEnumeration> for MyClass {}

// To properly handle the case above, these would be nice
impl ConformsTo<dyn NSCopying + NSFastEnumeration> for MyClass {}
impl ConformsTo<dyn NSMutableCopying + NSFastEnumeration> for MyClass {}

Note: dyn TraitA + TraitB + ... is not yet possible in Rust unless only one of them is a non-auto trait. But we still have to plan for the future!

@madsmtm madsmtm closed this Jan 26, 2023
@madsmtm madsmtm reopened this Jan 26, 2023
@madsmtm
Copy link
Owner Author

madsmtm commented Jan 26, 2023

I've been going about this a bit wrong; what we want is for users to specify this:

extern_protocol!(
    unsafe trait NSCopying {
        // ...
    }
);

extern_protocol!(
    unsafe trait NSMutableCopying: NSCopying {
        // ...
    }
);

extern_protocol!(
    unsafe trait NSFastEnumeration {
        // ...
    }
);

extern_class!(
    struct MyClass;
);

unsafe impl NSCopying for MyClass {}
unsafe impl NSMutableCopying for MyClass {}
unsafe impl NSFastEnumeration for MyClass {}

Notice how the protocol implementations for the class is not in the extern_class! macro? That's intentional, since the protocol may be specified in a "later"/"higher up" framework than the class is (which would work for Objectice-C, since they'd just define a category, but it won't work for us).

Additionally, the use of ConformsTo is entirely hidden, which is also really nice!

@madsmtm
Copy link
Owner Author

madsmtm commented Jan 26, 2023

Implementation-wise:

extern_protocol!(
    unsafe trait FooBar: Foo + Bar {}
);

// Generates

unsafe trait FooBar: ... {...}

unsafe impl<T: ?Sized + FooBar> FooBar for ProtocolObject<T> {}

// Note: Needs to be done a bit differently because of the orphan rule
impl<T: ?Sized + FooBar> ConformsTo<dyn FooBar> for T {}

// Maybe?
// impl<T: ?Sized + FooBar> ConformsTo<dyn Foo> for T {}
// impl<T: ?Sized + FooBar> ConformsTo<dyn Bar> for T {}

@madsmtm madsmtm linked an issue Jan 27, 2023 that may be closed by this pull request
@madsmtm madsmtm marked this pull request as ready for review January 27, 2023 08:15
@madsmtm madsmtm merged commit 1521cb0 into master Jan 27, 2023
@madsmtm madsmtm deleted the better-protocols branch January 27, 2023 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-framework Affects the framework crates and the translator for them A-objc2 Affects the `objc2`, `objc2-exception-helper` and/or `objc2-encode` crates enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improving how we map protocols
1 participant