Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upTracking issue for specialization (RFC 1210) #31844
Comments
nikomatsakis
assigned
aturon
Feb 23, 2016
nikomatsakis
added
A-traits
B-RFC-approved
T-lang
B-unstable
labels
Feb 23, 2016
This comment has been minimized.
This comment has been minimized.
|
Some additional open questions:
|
This comment has been minimized.
This comment has been minimized.
|
I am not sure that specialization changes the orphan rules:
Worse than that, the "future compatibility" orphan rules keep cross-crate specialization under pretty heavy control. Without them, default-impls leaving their methods open becomes much worse. I never liked explicit negative reasoning. I think the total negative reasoning specialization provides is a nice compromise. |
This comment has been minimized.
This comment has been minimized.
|
Should this impl be allowed with specialization as implemented? Or am I missing something? |
This comment has been minimized.
This comment has been minimized.
|
Same with this one, would have expected it to compile: http://is.gd/RyFIEl |
This comment has been minimized.
This comment has been minimized.
|
Looks like there's some quirks in determining overlap when associated types are involved. This compiles: http://is.gd/JBPzIX, while this effectively identical code doesn't: http://is.gd/0ksLPX |
This comment has been minimized.
This comment has been minimized.
|
Here's a piece of code I expected to compile with specialization: #![feature(specialization)]
use std::str::FromStr;
struct Error;
trait Simple<'a> {
fn do_something(s: &'a str) -> Result<Self, Error>;
}
impl<'a> Simple<'a> for &'a str {
fn do_something(s: &'a str) -> Result<Self, Error> {
Ok(s)
}
}
impl<'a, T: FromStr> Simple<'a> for T {
fn do_something(s: &'a str) -> Result<Self, Error> {
T::from_str(s).map_err(|_| Error)
}
}
fn main() {
// Do nothing. Just type check.
}Compilation fails with the compiler citing implementation conflicts. Note that |
added a commit
to SergioBenitez/Rocket
that referenced
this issue
Mar 23, 2016
This comment has been minimized.
This comment has been minimized.
|
I had time to look at the first two examples. Here are my notes. Example 1First case, you have:
The problem is that these impls overlap but neither is more specific than the other:
This is the kind of situation that lattice impls would allow -- you'd have to write a third impl for the overlapping case, and say what it should do. Alternatively, negative trait impls might give you a way to rule out overlap or otherwise tweak which matches are possible. Example 2You have:
These overlap because you can have
But neither impl is more specific:
|
This comment has been minimized.
This comment has been minimized.
The problem is that the compiler is conservatively assuming that This is a conservative choice, and is something we might want to relax over time. You can get the background here: |
This comment has been minimized.
This comment has been minimized.
|
Thank you for clarifying those two cases. It makes complete sense now On Tue, Mar 22, 2016, 6:34 PM Aaron Turon notifications@github.com wrote:
|
This comment has been minimized.
This comment has been minimized.
Isn't this exactly what specialization is trying to address? With specialization, I would expect that even if an implementation of |
This comment has been minimized.
This comment has been minimized.
|
@SergioBenitez you need to put On Tue, Mar 22, 2016, 6:54 PM Sergio Benitez notifications@github.com
|
This comment has been minimized.
This comment has been minimized.
burdges
commented
Apr 1, 2016
|
I think "default" trait items being automatically considered |
This comment has been minimized.
This comment has been minimized.
|
Note from #32999 (comment): if we do go with the lattice rule (or allow negative constraints), the "use an intermediate trait" trick to prevent further specialization of something will no longer work. |
This comment has been minimized.
This comment has been minimized.
|
Why won't it work? The trick limits the specialization to a private trait. You can't specialize the private trait if you can't access it. |
This comment has been minimized.
This comment has been minimized.
|
@arielb1 Ah. Good point. In my case, the trait isn't private. |
This comment has been minimized.
This comment has been minimized.
|
I don't think the "externals can't specialize because orphan forward-compatibility + coherence rulea" reasoning is particularly interesting or useful. Especially when we don't commit to our specific coherence rules. |
LukasKalbertodt
referenced this issue
Apr 25, 2016
Open
PartialEq between reference and non-reference type? #1332
This comment has been minimized.
This comment has been minimized.
burdges
commented
May 7, 2016
|
Is there a way to access an overridden |
This comment has been minimized.
This comment has been minimized.
|
Allowing projection of default associated types during type-checking will allow enforcing type inequality at compile-time: https://gist.github.com/7c081574958d22f89d434a97b626b1e4 #![feature(specialization)]
pub trait NotSame {}
pub struct True;
pub struct False;
pub trait Sameness {
type Same;
}
mod internal {
pub trait PrivSameness {
type Same;
}
}
use internal::PrivSameness;
impl<A, B> Sameness for (A, B) {
type Same = <Self as PrivSameness>::Same;
}
impl<A, B> PrivSameness for (A, B) {
default type Same = False;
}
impl<A> PrivSameness for (A, A) {
type Same = True;
}
impl<A, B> NotSame for (A, B) where (A, B): Sameness<Same=False> {}
fn not_same<A, B>() where (A, B): NotSame {}
fn main() {
// would compile
not_same::<i32, f32>();
// would not compile
// not_same::<i32, i32>();
}edited per @burdges' comment |
This comment has been minimized.
This comment has been minimized.
burdges
commented
May 7, 2016
|
Just fyi @rphmeier one should probably avoid is.gd because it does not resolve for Tor users due to using CloudFlare. GitHub works fine with full URLs. And play.rust-lang.org works fine over Tor. |
This comment has been minimized.
This comment has been minimized.
|
@burdges FWIW play.rust-lang.org itself uses is.gd for its "Shorten" button. It can probably be changed, though: https://github.com/rust-lang/rust-playpen/blob/9777ef59b/static/web.js#L333 |
This comment has been minimized.
This comment has been minimized.
|
@Boiethios Your usecase is working if you default on the fn and not on the impl: #![feature(specialization)]
trait Count {
fn count(self) -> usize;
}
impl<T> Count for T {
default fn count(self) -> usize {
1
}
}
impl<T> Count for T
where
T: IntoIterator,
T::Item: Count,
{
fn count(self) -> usize {
let i = self.into_iter();
i.map(|x| x.count()).sum()
}
}
fn main() {
let v = vec![1, 2, 3];
assert_eq!(v.count(), 3);
let v = vec![vec![1, 2, 3], vec![4, 5, 6]];
assert_eq!(v.count(), 6);
} |
This comment has been minimized.
This comment has been minimized.
|
Has the soundness hole still not been fixed yet? |
This comment has been minimized.
This comment has been minimized.
|
@alexreg I don't think so. See http://smallcultfollowing.com/babysteps/blog/2018/02/09/maximally-minimal-specialization-always-applicable-impls/ My guess is that everyone's focused on the edition right now... |
This comment has been minimized.
This comment has been minimized.
|
Okay thanks... seems like this issue is dragging on forever, but fair enough. It's tough, I know. And attention is directed elsewhere right now unfortunately. |
LukeMathWalker
referenced this issue
Sep 29, 2018
Open
Remove 'static bound from type `A` in `CorrelationExt.cov` #3
This comment has been minimized.
This comment has been minimized.
|
Can someone more concretely explain the rationale behind not allowing projections for default associated types in fully-monomorphic cases? I have a use case where I would like that functionality (in particular, it would be semantically incorrect for the trait to ever be invoked with types that weren't fully monomorphic), and if there's no soundness issue I don't completely understand why it's disallowed. |
This comment has been minimized.
This comment has been minimized.
|
@pythonesque There's some discussion at #42411 |
This comment has been minimized.
This comment has been minimized.
|
Ah, I understand if it turns out that projection interacts badly with specialization in general. . And it is indeed true that what I want is of a "negative reasoning" flavor (though closed traits would not really be sufficient). Unfortunately, I'm not sure if there's really any way to do what I want without such a feature: I'd like to have an associated type that outputs "True" when two passed-in types implementing a particular trait are syntactically equal, and "False" when they aren't (with the "False" case triggering a more expensive trait search which can decide whether they are "semantically" equal). The only real alternative seems (to me) to be to just always do the expensive search; which is fine in theory, but it can be a lot more expensive. (I could work around this if the trait were intended to be closed, by just enumerating every possible pair of constructors in the head position and having them output True or False; but it's intended to be open to extension outside the repository, so that can't possibly work, especially since implementations in two different user repositories wouldn't necessarily know about each other). Anyway, maybe this is just an indication that what I want to do is a bad fit for the trait system and I should switch to some other mechanism, like macros :P |
This comment has been minimized.
This comment has been minimized.
An alternative to negative reasoning is requiring that a type implements only one trait of a closed set of traits, such that implementations with other other traits in the set cannot overlap (e.g. |
This comment has been minimized.
This comment has been minimized.
|
Even if there were a way to enforce that in Rust (which there isn't, AFAIK?), I do not think that would solve my problem. I would like users in different crates to be able to implement an arbitrary number of new constants, which should compare equal only to themselves and unequal to every other defined constant, including ones unknown at crate definition time. I don't see how any closed set of traits (or even set of families of traits) can accomplish that goal by itself: this is a problem that fundamentally can't be solved without looking directly at the types. The reason it would be workable with default projections is that you could default everything to "don't compare equal" and then implement equality of your new constant to itself in whatever crate you defined the constant in, which wouldn't run afoul of orphan rules because all the types in the trait implementation were in the same crate. If I wanted almost any such rule but equality, even this wouldn't work, but equality is good enough for me :) |
dtolnay
referenced this issue
Oct 17, 2018
Closed
Improve performance of `Vec<u8>` deserialization in `serde_rosmsg` #1414
sunchao
referenced this issue
Oct 19, 2018
Closed
ARROW-3541: [Rust] Update BufferBuilder to allow for new bit-packed BooleanArray #2799
This comment has been minimized.
This comment has been minimized.
rmanoka
commented
Oct 26, 2018
|
On present nightly, this works: trait Foo {}
trait Bar {}
impl<T: Bar> Foo for T {}
impl Foo for () {}but even with specialization, and using nightly, this does not: #![feature(specialization)]
trait Foo<F> {}
trait Bar<F> {}
default impl<F, T: Bar<F>> Foo<F> for T {}
impl<F> Foo<F> for () {}Does this have a rationale or is it a bug? |
This comment has been minimized.
This comment has been minimized.
Boscop
commented
Oct 27, 2018
|
@rmanoka Isn't this just the normal orphan rules? In the first case, no downstream crate could |
This comment has been minimized.
This comment has been minimized.
rmanoka
commented
Oct 27, 2018
|
@Boscop In that scenario, the default impl should anyway be overridden by the non-default one below. For instance, if I had: Digging deeper along the lines of the counter-example you've mentioned, I realise (or believe) that the example satisfies the "always-applicable" test, and may be being worked on. |
botika
referenced this issue
Nov 10, 2018
Open
recognize a template instance and don't bother escaping it #108
This comment has been minimized.
This comment has been minimized.
zserik
commented
Dec 31, 2018
|
This issue probably depends on #45814. |
nikomatsakis commentedFeb 23, 2016
•
edited
This is a tracking issue for specialization (rust-lang/rfcs#1210).
Major implementation steps:
default impl(#37653)defaultmembers? #48444Unresolved questions from the RFC:
default type? Never during typeck? Or when monomorphic?default(i.e. specializable)?default impl(where all items aredefault) orpartial impl(wheredefaultis opt-in)