-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Forget
marker trait
#3782
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
base: master
Are you sure you want to change the base?
Forget
marker trait
#3782
Conversation
This definitely seems to be a reasonably motivated RFC, although it's going to definitely be something I'll have to read through a lot more closely to fully comment on it. A few first impressions:
Again, I do want to go through this a bit more closely before fully commenting on it, but I think that you definitely need to go back and make the primary motivation for this crystal clear: because of Generally, the only way to deal with this is to make these calls unsafe and tell people to pinky-promise they run the future to completion, but it would be nice to be able to have a safe way to do this instead. |
@clarfonthey We do use verbs as trait name when the only purpose of the trait is to support that verb, think of |
This is definitely a much desired feature that would enable new safe design patterns in Rust. Having a gradual migration path with I appreciate all the links to the prior art. |
# Guide-level explanation | ||
[guide-level-explanation]: #guide-level-explanation | ||
|
||
The core goal of `Forget` trait, as proposed in that RFC, is to bring back the "Proxy Guard" idiom for non-static types, with `async` being the primary motivation. |
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 I understand correctly that all !Forget
types need to have a lifetime bound to have any effect? Does it ever make sense to have a !Forget
type that has no lifetime? (like an integer)
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.
That’s correct. Non-Forget
types must be dropped before their lifetime ends
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.
From the perspective of use cases and definitions, it really makes little sense to have !Forget
and 'static
together. But I am not opposed too, it may have some other benefits which I am not aware of / or implementational / migrational benefits.
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.
This makes me wonder whether PhantomNotForget
should have a generic lifetime argument. Although it's not technically necessary, it would make it clearer that it's about lifetimes. I could work as self-documenting code which lifetime is the tricky one, and PhantomNotForget<'static>
would be something to lint against.
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.
Yeah, I also think so, but I fear some decision makers may not like it
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.
If there were to be a blanket impl<T: 'static> Forget for T
, then the lifetime parameter would be necessary for the type to work at all. I think that alone justifies it. IIUC the lifetime parameter would have to be covariant
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.
I would mention it just in case, this is already in unresolved questions:
Maybe force impl Forget for T where T: 'static {} and add a generic to the PhantomNonForget? Use cases and unsafe guarantee are fine with it, and we already allow !Forget in static.
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.
This makes me wonder whether
PhantomNotForget
should have a generic lifetime argument. Although it's not technically necessary, it would make it clearer that it's about lifetimes. I could work as self-documenting code which lifetime is the tricky one, andPhantomNotForget<'static>
would be something to lint against.
In my (not new) reference implementation of the Forget
trait, I have added it on a wrapper type Unforget
as a contravariant lifetime:
https://zetanumbers.github.io/leak-playground/leak_playground_std/marker/struct.Unforget.html
As to the magic 'static
lifetime, seems like it made a lot of contention in its prior discussion:
#t-lang > The destruction guarantee and linear types formulation
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Co-authored-by: Orion "Ardi" Gonzalez <[email protected]>
Co-authored-by: Yosh <[email protected]>
This comment was marked as resolved.
This comment was marked as resolved.
Great. Then you can find an explanation from @kornelski or me under that comment you linked. |
It may be confusing + we may potentially teach `Pin` in terms of `Forget` then the other way around.
After considering this for a bit, I don’t think category 2 makes sense. If you have an owned value (on the stack or in a |
If the value has to be pinned in particular place, than using |
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.
This seems like a useful change, but it would be easier to understand with some tweaks to the ordering of paragraphs, and some wording clarifications.
Co-authored-by: teor <[email protected]>
Co-authored-by: teor <[email protected]>
Co-authored-by: teor <[email protected]>
Co-authored-by: teor <[email protected]>
Co-authored-by: teor <[email protected]>
e6a737b
to
e534b0c
Compare
3aef99f
to
073b9ec
Compare
073b9ec
to
555fbfa
Compare
…f borrow-checker to prevent cycles and leaks
AFAIR original reason why How does |
I'd assume things like |
AFAIK there was another solution, scope-bound lifetimes. You effectively forbid object from leaving scope it was created in. |
Design of the RFC should support any extensions, as you can just create your own abstractions and use unsafe constructors internally, publish as a crate etc. |
Does this RFC address this issue raised by @withoutboats? |
I don't think it has that specific issue, because it has a different backwards compatibility issue that prevents you from getting into that situation: If crate A has a trait like: trait A {
fn foo<T>(t: T);
} and crate B has an implementation of A, then if A changes the bound on T to be ?Forget, then that is a breaking change, because the impl in B would have the constraint of Forget, which is more specific. Unless there was some mechanism to allow an impl to have more specific bounda than the trait it impls. Would this impact any std traits? Edit: I think this issue has some similarity to const methods in traits, and perhaps it could be solved in a similar way, once that is finalized. |
Yes |
Add a
Forget
marker trait indicating whether it is safe to skip the destructor before the value of a type exits the scope and basic utilities to work with!Forget
types. Introduce a seamless migration route for the standard library and ecosystem.Rendered
Pre-RFC thread
Unresolved questions
!Forget
gives to theunsafe
code is already fulfilled for all'static
types. Because of that, it doesn't really make a lot of sense to have!Forget + 'static
, it is still sound tomem::forget
that type. Should we forceimpl<T: 'static> Forget for T
then? Won't that impl create some unexpected problems?