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

define_class!: Allow safe overriding of methods / safe implementation of protocols #437

Open
madsmtm opened this issue Apr 13, 2023 · 5 comments
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

Comments

@madsmtm
Copy link
Owner

madsmtm commented Apr 13, 2023

It would be nice if icrate could mark certain protocols / methods as safe to override, such that define_class! could (after checking that all types are the same) allow just doing:

define_class!(
    // ...

    impl MyClass {
        #[method_id(init)]
        fn init(this: Allocated<Self>) -> Id<Self> {
            todo!()
        }
    }

    impl MyProtocol for MyClass {
        #[method(protocolMethod)]
        fn protocol_method(&self) {}
    }
);

(Notice the lack of unsafe impl)

@madsmtm madsmtm added enhancement New feature or request A-objc2 Affects the `objc2`, `objc2-exception-helper` and/or `objc2-encode` crates A-framework Affects the framework crates and the translator for them labels Apr 13, 2023
@madsmtm madsmtm added this to the icrate v0.2.0 milestone May 26, 2023
@madsmtm
Copy link
Owner Author

madsmtm commented Jun 11, 2023

Actually, creating news methods (i.e. not overriding) is safe (provided they are not special methods like retain, or private underscored methods).

So perhaps we need a #[override] attribute (that is checked at runtime), similar to Swift?

@madsmtm
Copy link
Owner Author

madsmtm commented Jun 27, 2023

Okay so we have two cases that I see a solution to:

  • Creating new methods:
    • Check that their selector is not an "unsafe" selector.
    • Verify that the selector is not implemented by a superclass.
  • Implementing protocols:
    • Use Rust's trait system for this, and actually implement the methods on the trait. This would ensure that the signatures are completely equivalent.

The hardest part here is overriding methods.
At the very least, it requires an #[override] attribute, to also allow calling the super method safely: #455

But checking that the signatures are equal is difficult! Possibly we could do the following check in the type system: Self::Super::$method_name == Self::$method_name? Though that won't work for overriding methods further up in the superclass hierarchy, and it would also fail for methods mentioning Self.

Note that having matching method encodings is not enough to make overriding safe.

@madsmtm
Copy link
Owner Author

madsmtm commented Jun 27, 2023

Another option is to have traits for each implementation, that the user can then select. So e.g.:

trait NSView_methods {
    fn drawRect(&self, rect: NSRect);
}

// Usage

define_class!(
    struct MyView;

    // unsafe impl ClassType ...

    impl NSView_methods for MyView {
        #[method(drawRect:)]
        fn drawRect(&self, rect: NSRect) {
            // Do drawing
        }
    }
);

Though this is just... Not very nice, and can be confusing to find the right trait for the methods you want (especially for NSView, which has a lot of categories).

@madsmtm
Copy link
Owner Author

madsmtm commented Jun 27, 2023

Though maybe the idea of specifying which class' method you're overriding is not a bad one? It would allow us to get the type from NSView::drawRect directly, and maybe with a bit (read: a lot) of type system and macro trickery, we can compare the two methods anyhow? Though I'm doubtful it's going to be particularly stable, since we also want the user to be able to write fn drawRect(self: &MyView, rect: NSRect) { ... }, which while a bit contrived, still needs to work.

@madsmtm
Copy link
Owner Author

madsmtm commented Jun 27, 2023

Note that we're assuming the user uses the same method names as the superclass/protocol, which they don't necessarily have to, so we also need a way to verify that.

A nice thing about that though is that we could maybe avoid the need to specify a selector in define_class!? That would make it very "Rusty"!

@madsmtm madsmtm changed the title declare_class!: Allow safe overriding of methods / safe implementation of protocols define_class!: Allow safe overriding of methods / safe implementation of protocols Nov 26, 2024
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

No branches or pull requests

1 participant