-
Notifications
You must be signed in to change notification settings - Fork 49
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
Making Id::retain
safer
#399
Comments
Making |
This also relates to #401, #265 and #316. I'm starting to cook up a scheme like this: trait ClassKind: Sealed {}
// Fallback for everything that hasn't been specified
struct Unknown;
impl ClassKind for Unknown {}
/// `Id::retain` is safe
struct Immutable;
impl ClassKind for Immutable {}
/// Immutable, but disallows `Id::retain`
struct ImmutableWithMutableSubclass;
impl ClassKind for ImmutableWithMutableSubclass {}
/// Mutable, mutable methods use `&mut self`. `Id::retain` is not safe
struct Mutable;
impl ClassKind for Mutable {}
/// Mutable, mutable methods use `&self`. Not Send + Sync. `Id::retain` is safe
struct InteriorMutable;
impl ClassKind for InteriorMutable {}
/// Same as InteriorMutable, but only on the main thread. `Id::retain` is safe
struct MainThreadOnly;
impl ClassKind for MainThreadOnly {}
trait ClassType {
type Kind: ClassKind;
// ... Super + NAME
fn alloc()
where Self::Kind != MainThreadOnly || Unknown;
fn alloc(mtm: MainThreadMarker)
where Self::Kind == MainThreadOnly;
}
// Maybe even:
unsafe impl Send + Sync for Immutable {}
unsafe impl Send + Sync for ImmutableWithMutableSubclass {}
unsafe impl Send + Sync for Mutable {} And with a bit of extra trait bounds, we could even ensure that e.g. an Still very unsure about this! |
With that, we could perhaps even get rid of the let mutable_string: Id<NSMutableString> = NSMutableString::new();
// These are safe, since their lifetime is still tied to the mutable string
let _: &mut NSString = &mut mutable_string;
let string: &NSString = &mutable_string;
// string.retain() would not be possible, since `NSString` is `ImmutableWithMutableSubclass`
// Implicitly relinquish "owned" ownership of the variable here
let string: Id<NSString> = Id::into_super(mutable_string);
// So retaining the `NSMutableString` here is still safe, we statically know that nothing can modify the string any more
let string2: Id<NSString> = string.clone(); And for custom subclasses on declare_class!(
struct MyClass {
ivar: IvarEncode<u32, "_ivar">,
}
mod ivars;
unsafe impl ClassType for MyClass {
type Kind = Mutable;
type Super = NSObject;
}
unsafe impl MyClass {
#[method_id(init)]
fn init(this: Allocated<Self>) -> Option<Id<Self>> {
let this = unsafe { msg_send_id![super(this), init] }?;
Ivar::write(&mut this.ivar, 5);
Some(this)
}
}
);
extern_methods!(
unsafe impl MyClass {
#[method_id(new)]
fn new() -> Option<Id<Self>>;
}
);
let obj = MyClass::new()?;
*obj.ivar = 2; // Allowed, the object is mutable
// let obj2 = obj.clone(); // Disallowed And custom subclasses on declare_class!(
struct MyView {
ivar: IvarEncode<Cell<u32>, "_ivar">,
}
mod ivars;
unsafe impl ClassType for MyView {
type Kind = MainThreadOnly;
type Super = NSView;
}
unsafe impl MyView {
#[method_id(init)]
fn init(this: Allocated<Self>) -> Option<Id<Self>> {
let this = unsafe { msg_send_id![super(this), init] }?;
Ivar::write(&mut this.ivar, Cell::new(5)); // Ideally, mutability would be allowed in `init`
Some(this)
}
}
);
let obj = MyView::new()?;
// *obj.ivar = 2; // Disallowed, the object itself is immutable
obj.ivar.set(2); // But we can still use `Cell`'s powers
let obj2 = obj.clone(); // Also allowed
// But `obj` would also be !Send + !Sync |
Moved to #439. |
For some things like
NSMutableString
(and by extension,NSString
), callingId::retain
on&T
cannot be safe, since there could be a mutable reference to it somewhere else in the program (and the lifetime is erased by callingId::retain
, that's kinda the whole point).The solution there is to use
NSCopying::copy
to instead store a copy of your string (which is cheap when given&NSString
, and correct when given&NSMutableString
).But for types like
NSView
andNSWindow
, this is overly restrictive, since we know there can never be a mutable reference to these (they always use interior mutability); so perhaps we can make a trait for expressing this? (This comes up especially often in user-definedinit
methods).The text was updated successfully, but these errors were encountered: