-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Tweak object safety rules to allow static dispatch #2027
Tweak object safety rules to allow static dispatch #2027
Conversation
# Detailed design | ||
[design]: #detailed-design | ||
|
||
Today, the rules for object safey work like this: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/safey/safety
The documentation should mention fixing up https://doc.rust-lang.org/error-index.html#method-has-no-receiver |
What about that error message should change? You still can't have methods from the trait on the trait object type, just static methods from other traits (or inherent impls) |
Oh, nvm, I see now. |
This raises the question, if Foo is not object safe, can you impl Foo for Foo? |
Is there ever a context in this would be sensible? If not, it would be nice if we could modify the proposed design to continue to make this inexpressible, even in |
I'm overall not very fond of "let's make this inexpressible even in unsafe code", it invariably ends up coming back to bite us. It's very hard to know a priori what legitimate use cases there are for an API. I'm all for making things hard to do, but when we make things impossible it's often presumptuous. In fact, I'd argue for there to be a defined API with a stabilization path for constructing trait objects ( |
You can write that function today, just add an |
That makes sense to me! In this case, it seems like it might be worth thinking about a bit, since it is a question of preserving the status quo or not (vs. simply introducing new restrictions). Suppose This is either possibly sensible or it isn't, wrt what it is supposed to mean to be "object safe". If it is possibly sensible, and the rules for object safety are too strict right now, then this RFC adds a feature beyond just support for static dispatch: now you can (with some contortion) obtain a trait object that was previously unobtainable (at least after RFC 255). That's great! It also makes me ask: should there be a safe API for those cases? If this is provably never sensible— e.g. it permits a new, previously-inexpressible violation of a contract that is always supposed to hold— then to me, it seems there's a small amount of value in maintaining the status quo. This is definitely open to questioning, of course, since prior to RFC 255, any trait could be used to make a trait object. Maybe we lost something there, that we'd regain with this RFC. If so, it would be cool (but not mandatory) to have examples, if they exist. Of course, since we only recover the ability to create trait objects from non-object safe traits in I definitely don't know, hence my interest in concrete examples. |
@ranweiler I think it depends on what you mean by "valid use cases." There are two different ways to interpret that:
I still don't know why you would want to do that, but it would be a valid program AFAIK. However, I tend to want to take a "we are all adults here" attitude toward unsafe code; you can do all sorts of inadvisible things. And I don't think restricting this would be very easy to implement, since we'd have to check object safety every time we learn the type of a value, rather than only when seeing these specific casts. |
Actually, I have a (contrived) example of something you could do. Implementing methods on object-unsafe traits seems fine to me. You can never actually dereference their vtable or data pointers in those methods, because the object type doesn't implement the trait type, so it would be badly typed to call any methods from the vtable. But that means you could totally define a method on an object unsafe trait, instantiate the object, and then call it: trait ObjectSafe {
fn safe(&self) -> u32;
}
trait NotObjectSafe {
fn not_safe<T: Default>(&self) -> T;
}
trait FooBar {
fn foobar(&self) -> u32;
}
impl FooBar for ObjectSafe {
// performs dynamic dispatch through `self.safe()`
fn foobar(&self) -> u32 { self.safe() }
}
impl FooBar for NotObjectSafe {
fn foobar(&self) -> u32 {
// NotObjectSafe does not implement NotObjectSafe, so `self.not_safe()` cannot be called
0
}
}
fn main() {
unsafe {
let obj: &'static NotObjectSafe = mem::transmute(TraitObject { data: ptr::null(), vtable: ptr::null() });
obj.foobar()
}
} |
This RFC would basically let you use object unsafe traits analogously to opaque types; whatever data they carry you can't get to, but you can still operate on them. |
Some thoughts: A. I think that the rules proposed in this RFC are sensible and a reasonable "baby step" towards better object safety rules. B. I would very much like to preserve the invariant that This contributes to keeping MIR very simple. I might be overvaluing this, but I feel like there is value in having a simple "core language", and I'd like to see if we can hold the line there. C. At present, we have this compromise, where a trait D. I think it's orthogonal from this RFC, to some extent, but I do like the idea of people being able to provide their own Because impl<T> Iterator for Iterator<Item = T> {
type Item = T;
// No body means: delegate to a virtual call. Only possible
// if the method is object-safe.
fn next(&mut self) -> Option<T>;
fn map(self, ...) -> Map<Self, ...>{
// this would presumably e more-or-less the same as the default impl;
// but here you see the problem, that we can't do that because we need
// to introduce some kind of pointer type for the trait object.
//
// if this were an `&mut self` method, it could be made to work.
}
} |
These statements seem to be at odds, but I'm not certain that we mean the same thing. Under this RFC, |
You're correct. What I meant really was that |
@nikomatsakis Yes, I agree with that restriction |
@rfcbot fcp merge As @nikomatsakis elucidated, there's a lot more we could do to make object safety more ergonomic (e.g. |
Team member @withoutboats has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
|
||
Today, the rules for object safey work like this: | ||
|
||
* If the trait (e.g. `Foo`) **is** object safe: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean "If it is safe then...", or "It is safe if ..."?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the first
The final comment period is now complete. |
I've merged this PR as RFC 2027, tracking issue is at rust-lang/rust#43561 |
Rendered