-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Tracking issue for allowing overlapping implementations for marker trait #29864
Comments
With respect to the second question of whether we should make a more explicit notion of marker trait, I have been pondering this since the RFC discussion. On the one hand, there are a number of places in Rust today where going from nothing to something is a breaking change: adding a private field to a struct, for example. The difference here I think is that:
However, the second point depends a lot on the fate of specialization. If we wind up adopting specialization, and in particular a model that does not require strict subsets, then it would still be possible to adapt existing impls. This makes me a lot less worried. Of course, implementing that form of specialization is tricky, but not (I don't think...) that much trickier than implementing THIS suggestion. The hard part in both cases is the potential for overlap. |
There's a few limitations that I'm not happy with at the moment: - The type of the expression has to be repeated twice (https://github.com/rust-lang/rust/isues/30056) - It won't work with joins yet. I might be able to work around this in the short term, but it may require rust-lang/rust#29864 or specialization to express properly
This is useful for things like full text search, where you need to do an expensive calculation to a parameter going into the where clause, but doing it inline would repeat the calculation for each loop. I had to expose a lot of internals here, because this is yet another case where we can't properly express what we want without either specialization or rust-lang/rust#29864. I could not find a way to properly enforce the selectability of `Aliased` when `with` was called more than once (this is ultimately the same problem as joining multiple times). In the interim, we'll allow the alias to be used anywhere.
@nrc This has the RFC implemented label, but I don't think it has been. If it has, is there a feature gate that I'm missing? If it hasn't been implemented, is there anything I can do to help push this along? It's now been a year since the RFC was accepted. |
@sgrif swapped labels |
While this is useful for some cases where you don't want to load the rows, this won't fill every use case for the expression, as right now you wouldn't be able to build a query that references the outer table. For us to do that and have it be type safe we'd need overlapping impls for `SelectableExpression` (story of my life), which requires rust-lang/rust#29864 being implemented. Fixes #414.
While this is useful for some cases where you don't want to load the rows, this won't fill every use case for the expression, as right now you wouldn't be able to build a query that references the outer table. For us to do that and have it be type safe we'd need overlapping impls for `SelectableExpression` (story of my life), which requires rust-lang/rust#29864 being implemented. Fixes #414.
This patch allows overlap to occur between any two impls of a trait for traits which have no associated items. Several compile-fail tests around coherence had to be changed to add at least one item to the trait they test against. Ref rust-lang#29864
…matsakis Implement RFC 1268 This patch allows overlap to occur between any two impls of a trait for traits which have no associated items. Several compile-fail tests around coherence had to be changed to add at least one item to the trait they test against. Ref rust-lang#29864
@rylev basically we just need to create a stabilization PR with a write-up that includes
There is some documentation here about writing a report and also preparing a PR. |
I've tried to collect the known open questions and issues here. Questions
Bugs
I think most of these should be answered before we move forward with stabilization. |
Yes, opt-in is key. Likely none of the existing auto traits can be made |
@nikomatsakis do you have thoughts on whether an RFC is required for stabilizing the name |
@scottmcm did you have a specific reason to nominate this? :) |
cc @rust-lang/wg-traits -- I seem to recall some concerns about the implementation potentially being unsound? Does anyone remember what I'm talking about? |
We discussed this in our @rust-lang/lang meeting today. One thing we were wondering about are the active use cases and stakeholders -- seems like @rust-lang/wg-safe-transmute was one potential consumer, did the current design meet the needs there? |
Can you elaborate on this one, @RalfJung? I would have guessed that |
For I assume the same could happen, in principle, for any of the other existing marker traits, though I am not aware of concrete examples. |
I think the issue was #88139. The implementation now results in weird ambiguities, but at least it isn't unsound anymore afaik
Afaict the issue here is about impls for marker traits violating the orphan rules, which is generally unsound to my knowledge and not permitted by the current implementation. It might actually be possible to change all auto traits to marker traits again, only requiring opt-in for non-auto traits, as for those, adding an item to the trait definition would otherwise be a breaking change. edit: nm, the idea is for a macro to add |
The entire point of the feature in this tracking issue is to allow violating the orphan rules by permitting overlapping implementations -- or am I misunderstanding? |
it allows overlapping impls in the same crate, allowing it across crates is unsound without restricting the way marker traits can be used. consider a crate like this: #![feature(marker_trait_attr)]
#[marker]
pub trait Marker {}
pub struct B;
trait NotMarker {
type Assoc;
}
impl NotMarker for B {
type Assoc = usize;
}
impl<T: Marker> NotMarker for T {
type Assoc = Box<String>;
} if a different crate were to add an impl leading to |
Oh, because rustc actually uses negative reasoning inside a crate, so not all 'monotone' extensions of the impl set are actually allowed... that's nasty. |
I have an action item to leave a comment here but I can't find the notes. In any case, my vague recollection is that: Despite marker traits permitting one to stretch the coherence rules, they are specifically targeting the overlap rules and not the orphan rules (i.e., they don't let you write impls in crates that you wouldn't have been permitted to write an impl in). If we ensure that marker traits follow the same orphan rules as everyone else, I don't think they pose any particular problem for the negative reasoning that we do elsewhere. There was also a (related) interaction between marker traits and negative impls that was discussed here. In short, the problem was that if we lifted the orphan rules for marker traits, then one could add an explicit The case that @RalfJung is talking about seems to be the same, but the "negative reasoning" in question is implicit due to the fact that everything is within the same crate. |
One use for this which I'm particularly excited about but haven't seen explicitly mentioned is that it allows things like this: #[marker]
trait TupleHas<T> {}
impl<T1> TupleHas<T1> for (T1, ) {}
impl<T1, T2> TupleHas<T1> for (T1, T2) {}
impl<T1, T2> TupleHas<T2> for (T1, T2) {}
impl<T1, T2, T3> TupleHas<T1> for (T1, T2, T3) {}
impl<T1, T2, T3> TupleHas<T2> for (T1, T2, T3) {}
impl<T1, T2, T3> TupleHas<T3> for (T1, T2, T3) {}
// ... A use case for this would be Bevy's queries, which look like this. fn score_system(mut query: Query<(&Player, &mut Score)>) {
// Type safe access to components
for (player, mut score) in query.iter_mut() {
// Some score updating logic here
}
// However, we can also do this:
let player: &Player = query.get_component(some_specific_entity);
// We're only allowed to call get_component with one of the types in the Query.
// (Player or Score in this case).
// Currently, this is only enforced with a runtime error.
// A QueryHasReadAccess<T> marker trait could allow this to be a type error instead.
} I'd love to see this stabilized! |
@SamPruden I'd also like to see this stabilize. It looks like there's at least one open issue that probably blocks it, though:
F-marker_trait_attr
|
I'm currently writing a linear algebra library that would really benefit from this feature as well, so I'd love to see this move forward. My usecases, in case they're insightful: The first is #[marker]
trait VectorLike {}
impl<D> VectorLike for (Const<1>, D) {}
impl<D> VectorLike for (D, Const<1>) {} The second is #[marker]
trait DimEq {}
impl<const D: usize> DimEq for (Const<D>, Const<D>) {} // guaranteed to be equal
impl<const D: usize> DimEq for (Dyn, Const<D>) {} // *can* be equal (ensured by runtime assertion)
impl DimEq for (Dyn, Dyn) {} // *can* be equal (ensured by runtime assertion)
impl<T, U> DimEq for (T, U) where (U, T): DimEq {} // order shouldn't matter when using this
// (important in generic code calling other generic code, otherwise they have to require both or agree on order)
// Notably, this isn't implemented for `(Const<A>, Const<B>)` when A and B are different.
// catches mistakes at compiletime instead of runtime when possible! |
@SludgePhD I wonder if your logic for impl<T, U> DimEq for (T, U) where (U, T): DimEq {} is right. Notably, it overflows the current typechecker, and the next trait solver intends to resolves cycles with a "yes" answer by separating impl<T> DimEq for (T, T) where (T, T): DimEq {} // has impl, so is impl; in other words, always holds. |
Tracking issue for rust-lang/rfcs#1268.
Status
Known bugs
History
Prior to stabilization
#[marker]
annotation, see Support an explicit annotation for marker traits #53693Other notes
In #96766 we decided NOT to disable the orphan check for marker traits as part of this work (just the overlap check), which was a proposed extension.
The text was updated successfully, but these errors were encountered: