-
Notifications
You must be signed in to change notification settings - Fork 40
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
Add downcasting capability based on isKindOfClass:
#474
Conversation
NSObjectProtocol::is_member_of
and some related "downcasting" helpers
Another danger is generic types! E.g. there are no safeguards to prevent you from getting Also wanted to say that I agree that this feature is useful, and would like to get it somehow, it just needs a bit more thinking to make sure it's sound! |
😅 I have to admit I've started working with this crate since yesterday and didn't even realize that generics were a thing here. There is one option where one could encode the "genericity" in pub trait impl(self) Genericity {}
pub struct Generic(!);
impl Genericity for Generic {}
pub struct NotGeneric(!);
impl Genericity for NotGeneric {}
pub unsafe trait ClassType: Message {
...
type Genericity: Genericity;
}
impl AnyObject {
fn 🚲<T>(&self) -> Option<&T>
where
Self: 'static,
T: ClassType<Genericity = NotGeneric> + 'static,
{
if self.class() == T::class() { // Or isKindOfClass
// SAFETY: self is a direct member of the specified (`T`) class.
// Therefore casting should be safe.
Some(unsafe { &*(self as *const Self).cast::<T>() })
} else {
None
}
}
} |
Hmm... Yeah, that would go a long way in ensuring things are sound! Although this does exclude us from doing Also, I'm considering whether it would be possible to not restrict this to |
Something that would help my confidence in the soundness of whatever implementation we end up with, maybe we can assemble a list of allowed and disallowed cases (that could be added as (UI) tests)? |
I think it might be possible. The objc runtime exposes an internal api called
Digging further it seems like NSObject's isKindOfClass implementation is also very similar to However, I am not quite sure about "how internal" that api is since it doesn't have an underscore like many other internal but yet exposed objc runtime apis. But it seems like llvm does an optimization where it transforms Also if we were to use this, we'd also have to bump our min. |
I think The problem with calling I tried checking what Swift does, it seems like an expression like |
NSObjectProtocol::is_member_of
and some related "downcasting" helpersisKindOfClass:
Converted to a draft because it's probably still missing something (among others, ui-tests might be worthwhile, renaming downcasting -> cast?). |
I tried the following Swift code: import Foundation
let testConversionArray: AnyObject = NSArray.init(objects: NSString(), NSObject())
if let inner = testConversionArray as? [NSString] {
print("is NSString array", inner[0].length)
} else if let inner = testConversionArray as? [NSObject] {
print("is NSObject array", inner[0])
} else {
print("not array")
} And it is able to detect that the array is actually a The way that works is by checking every element of the array though, which is probably unacceptable performance-wise in Rust (I'd expect a downcast to be very cheap). So in Rust users will probably have to do the cast more explicitly: let array: &AnyObject = ...;
let array: &NSArray<AnyObject> = array.downcast()?;
for item in array {
let item: &NSString = item.downcast()?;
// Use item here.
} Tl;dr: I still think we should disallow casts on generic items, except for when the generics are set to |
Returning back to this: Decision: We shouldn't really care about Decision: I think the name "downcast" makes sense, that is also Swift's terminology, and matches the equivalent Rust concept of Decision: We also want some sort of Unsure how downcasting from Regarding mutability: An explicit design decision in Also unsure if we could add some kind of I also still have to consider generics some more. Likely we'll be implementing the trait for I'll try to make a collection of examples and UI tests in one of the coming days to move forwards here. |
0a552f4
to
fe53072
Compare
I've finally been able to come back to this properly, I've waited so long because I wanted to remove mutability first, I felt it was the only real way I could feasibly ensure that this was sound. I have pushed my commits to your branch, hope you don't mind ;) And thank you again for starting on this work wayy back then! |
It's not part of the protocol per-say, and it's better exposed as AnyObject::downcast.
To make it clear that it is unsafe, and to push users towards the new API.
fe53072
to
24a42e4
Compare
Add downcasting capability based on
isKindOfClass:
.