Tracking issue for specialization (RFC 1210)
This is a tracking issue for specialization (rust-lang/rfcs#1210).
Major implementation steps:
- [x] Land https://github.com/rust-lang/rust/pull/30652 =)
- [ ] Restrictions around lifetime dispatch (currently a soundness hole)
- [ ]
default impl(https://github.com/rust-lang/rust/issues/37653) - [ ] Integration with associated consts
- [ ] Bounds not always properly enforced (https://github.com/rust-lang/rust/issues/33017)
- [ ] Should we permit empty impls if parent has no
defaultmembers? https://github.com/rust-lang/rust/issues/48444 - [ ] implement "always applicable" impls https://github.com/rust-lang/rust/issues/48538
- [ ] describe and test the precise cycle conditions around creating the specialization graph (see e.g. this comment, which noted that we have some very careful logic here today)
Unresolved questions from the RFC:
- Should associated type be specializable at all?
- When should projection reveal a
default type? Never during typeck? Or when monomorphic? - Should default trait items be considered
default(i.e. specializable)? - Should we have
default impl(where all items aredefault) orpartial impl(wheredefaultis opt-in) - How should we deal with lifetime dispatchability?
This is a tracking issue for specialization (rust-lang/rfcs#1210).
Major implementation steps:
- [x] Land https://github.com/rust-lang/rust/pull/30652 =)
- [ ] Restrictions around lifetime dispatch (currently a soundness hole)
- [ ]
default impl(https://github.com/rust-lang/rust/issues/37653) - [ ] Integration with associated consts
- [ ] Bounds not always properly enforced (https://github.com/rust-lang/rust/issues/33017)
- [ ] Should we permit empty impls if parent has no
defaultmembers? https://github.com/rust-lang/rust/issues/48444 - [ ] implement "always applicable" impls https://github.com/rust-lang/rust/issues/48538
- [ ] describe and test the precise cycle conditions around creating the specialization graph (see e.g. this comment, which noted that we have some very careful logic here today)
Unresolved questions from the RFC:
- Should associated type be specializable at all?
- When should projection reveal a
default type? Never during typeck? Or when monomorphic? - Should default trait items be considered
default(i.e. specializable)? - Should we have
default impl(where all items aredefault) orpartial impl(wheredefaultis opt-in); see https://github.com/rust-lang/rust/issues/37653#issuecomment-616116577 for some relevant examples of wheredefault implis limiting. - How should we deal with lifetime dispatchability?
Note that the
specializationfeature as implemented currently is unsound, which means that it can cause Undefined Behavior withoutunsafecode.min_specializationavoids most of the pitfalls.
Tracking issues have a tendency to become unmanageable. Please open a dedicated new issue and label it with F-specialization for absolutely any topics you want to discuss or have questions about. See https://github.com/rust-lang/compiler-team/issues/739 for details and discussions on this prospective policy.
Ralf Jung at 2020-04-22 08:24:32
Some additional open questions:
- Should we revisit the orphan rules in the light of specialization? Are there ways to make things more flexible now?
- Should we extend the "chain rule" in the RFC to something more expressive, like the so-called "lattice rule"?
- Related to both of the above, how does negative reasoning fit into the story? Can we recover the negative reasoning we need by a clever enough use of specialization/orphan rules, or should we make it more first-class?
Aaron Turon at 2016-02-24 00:06:13
I am not sure that specialization changes the orphan rules:
- The "linking" orphan rules must stay the same, because otherwise you would not have safe linking.
- I don't think the "future compatibility" orphan rules should change. Adding a non-specializable impl under you would still be a breaking change.
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.
Ariel Ben-Yehuda at 2016-02-24 12:13:39
Should this impl be allowed with specialization as implemented? Or am I missing something? http://is.gd/3Ul0pe
Sage Griffin at 2016-03-20 05:25:14
Same with this one, would have expected it to compile: http://is.gd/RyFIEl
Sage Griffin at 2016-03-20 05:33:37
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
Sage Griffin at 2016-03-20 05:54:51
Here's a piece of code I expected to compile with specialization:
http://is.gd/3BNbfK
#![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
&strdoesn't implementFromStr, so there shouldn't be a conflict.Sergio Benitez at 2016-03-23 00:14:53
@sgrif
I had time to look at the first two examples. Here are my notes.
Example 1
First case, you have:
FromSqlRow<ST, DB> for T where T: FromSql<ST, DB>FromSqlRow<(ST, SU), DB> for (T, U) where T: FromSqlRow<ST, DB>, U: FromSqlRow<SU, DB>,
The problem is that these impls overlap but neither is more specific than the other:
- You can potentially have a
T: FromSql<ST, DB>whereTis not a pair (so it matches the first impl but not the second). - You can potentially have a
(T, U)where:T: FromSqlRow<ST, DB>,U: FromSqlRow<SU, DB>, but not(T, U): FromSql<(ST, SU), DB>- (so the second impl matches, but not the first)
- The two impls overlap because you can have a
(T, U)such that:T: FromSqlRow<ST, DB>U: FromSqlRow<SU, DB>(T, U): FromSql<(ST, SU), DB>
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 2
You have:
Queryable<ST, DB> for T where T: FromSqlRow<ST, DB>Queryable<Nullable<ST>, DB> for Option<T> where T: Queryable<ST, DB>
These overlap because you can have
Option<T>where:T: Queryable<ST, DB>Option<T>: FromSqlRow<Nullable<ST>, DB>
But neither impl is more specific:
- You can have a
Tsuch thatT: FromSqlRow<ST, DB>butTis not anOption<U>(matches first impl but not second) - You can have an
Option<T>such thatT: Queryable<ST, DB>but notOption<T>: FromSqlRow<Nullable<ST>, DB>
Aaron Turon at 2016-03-23 00:29:16
@SergioBenitez
Compilation fails with the compiler citing implementation conflicts. Note that
&strdoesn't implementFromStr, so there shouldn't be a conflict.The problem is that the compiler is conservatively assuming that
&strmight come to implementFromStrin the future. That may seem silly for this example, but in general, we add new impls all the time, and we want to protect downstream code from breaking when we add those impls.This is a conservative choice, and is something we might want to relax over time. You can get the background here:
- http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/
- https://github.com/rust-lang/rfcs/pull/1023
- https://github.com/rust-lang/rfcs/issues/1053
- https://github.com/rust-lang/rfcs/pull/1148
Aaron Turon at 2016-03-23 00:33:23
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:
@SergioBenitez https://github.com/SergioBenitez
Compilation fails with the compiler citing implementation conflicts. Note that &str doesn't implement FromStr, so there shouldn't be a conflict.
The problem is that the compiler is conservatively assuming that &str might come to implement FromStr in the future. That may seem silly for this example, but in general, we add new impls all the time, and we want to protect downstream code from breaking when we add those impls.
This is a conservative choice, and is something we might want to relax over time. You can get the background here:
http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/
- rust-lang/rfcs#1023 https://github.com/rust-lang/rfcs/pull/1023
- rust-lang/rfcs#1053 https://github.com/rust-lang/rfcs/issues/1053
- rust-lang/rfcs#1148 https://github.com/rust-lang/rfcs/pull/1148
— You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub https://github.com/rust-lang/rust/issues/31844#issuecomment-200093757
Sage Griffin at 2016-03-23 00:35:14
@aturon
The problem is that the compiler is conservatively assuming that &str might come to implement FromStr in the future. That may seem silly for this example, but in general, we add new impls all the time, and we want to protect downstream code from breaking when we add those impls.
Isn't this exactly what specialization is trying to address? With specialization, I would expect that even if an implementation of
FromStrfor&strwere added in the future, the direct implementation of theSimpletrait for&strwould take precedence.Sergio Benitez at 2016-03-23 00:53:30
@SergioBenitez you need to put
default fnin the more general impl. Your example isn't specializable.On Tue, Mar 22, 2016, 6:54 PM Sergio Benitez notifications@github.com wrote:
@aturon https://github.com/aturon
The problem is that the compiler is conservatively assuming that &str might come to implement FromStr in the future. That may seem silly for this example, but in general, we add new impls all the time, and we want to protect downstream code from breaking when we add those impls.
Isn't this exactly what specialization is trying to address? With specialization, I would expect that even if an implementation of FromStr for &str were added in the future, the direct implementation for the trait for &str would take precedence.
— You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub https://github.com/rust-lang/rust/issues/31844#issuecomment-200097995
Sage Griffin at 2016-03-23 00:56:11
I think "default" trait items being automatically considered
defaultsounds confusing. You might want both parametricity for a trait like in Haskell, etc. along side with easing theimpls. Also you cannot easilygrepfor them like you can fordefault. It's not hard to both type thedefaultkeyword and give a default implementation, but they cannot be separated as is. Also, if one wants to clarify the language, then these "default" trait items could be renamed to "trait proposed" items in documentation.Jeff Burdges at 2016-04-01 16:51:17
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.
Steven Allen at 2016-04-15 18:42:40
@Stebalien
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.
Ariel Ben-Yehuda at 2016-04-15 20:02:16
@arielb1 Ah. Good point. In my case, the trait isn't private.
Steven Allen at 2016-04-15 20:04:43
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.
Ariel Ben-Yehuda at 2016-04-15 20:09:20
Is there a way to access an overridden
default impl? If so, this could aid in constructing tests. See Design By Contract and libhoare.Jeff Burdges at 2016-05-07 05:13:06
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
robert at 2016-05-07 17:26:55
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.
Jeff Burdges at 2016-05-07 18:30:12
@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
Simon Sapin at 2016-05-07 18:35:03
use like this(https://is.gd/Ux6FNs):
#![feature(specialization)] pub trait Foo {} pub trait Bar: Foo {} pub trait Baz: Foo {} pub trait Trait { type Item; } struct Staff<T> { } impl<T: Foo> Trait for Staff<T> { default type Item = i32; } impl<T: Foo + Bar> Trait for Staff<T> { type Item = i64; } impl<T: Foo + Baz> Trait for Staff<T> { type Item = f64; } fn main() { let _ = Staff { }; }Error :
error: conflicting implementations of trait `Trait` for type `Staff<_>`: [--explain E0119] --> <anon>:20:1 20 |> impl<T: Foo + Baz> Trait for Staff<T> { |> ^ note: conflicting implementation is here: --> <anon>:16:1 16 |> impl<T: Foo + Bar> Trait for Staff<T> { |> ^ error: aborting due to previous errorDoes feture
specializationsupport this, and is there any other kind of implementations currently?Linhe Huo at 2016-05-17 09:31:34
@zitsen
These impls are not allowed by the current specialization design, because neither
T: Foo + BarnorT: Foo + Bazis more specialized than the other. That is, if you have someT: Foo + Bar + Baz, it's not clear which impl should "win".We have some thoughts on a more expressive system that would allow you to also give an impl for
T: Foo + Bar + Bazand thus disambiguate, but that hasn't been fully proposed yet.Aaron Turon at 2016-05-17 18:53:59
If negative trait bounds
trait Baz: !Barever land, that could also be used with specialization to prove that the sets of types that implement Bar and those that implement Baz are distinct and individually specializable.robert at 2016-05-17 21:07:54
~~Seems @rphmeier 's reply is what I exactly want, impls for
T: Foo + Bar + Bazwould also help.~~Just ignore this, I still have something to do with my case, and always exciting for the
specializationand other features landing.Thanks @aturon @rphmeier .
Linhe Huo at 2016-05-18 01:42:52
I've been playing around with specialization lately, and I came across this weird case:
#![feature(specialization)] trait Marker { type Mark; } trait Foo { fn foo(&self); } struct Fizz; impl Marker for Fizz { type Mark = (); } impl Foo for Fizz { fn foo(&self) { println!("Fizz!"); } } impl<T> Foo for T where T: Marker, T::Mark: Foo { default fn foo(&self) { println!("Has Foo marker!"); } } struct Buzz; impl Marker for Buzz { type Mark = Fizz; } fn main() { Fizz.foo(); Buzz.foo(); }Compiler output:
error: conflicting implementations of trait `Foo` for type `Fizz`: [--explain E0119] --> <anon>:19:1 19 |> impl<T> Foo for T |> ^ note: conflicting implementation is here: --> <anon>:15:1 15 |> impl Foo for Fizz { |> ^I believe that the above should compile, and there's two interesting variations that actually do work-as-intended:
- Removing the
where T::Mark: Fizzbound:
impl<T> Foo for T where T: Marker //, T::Mark: Fizz { // ... }- Adding a "trait bound alias":
trait FooMarker { } impl<T> FooMarker for T where T: Marker, T::Mark: Foo { } impl<T> Foo for T where T: FooMarker { // ... }(Which doesn't work if
Markeris defined in a separate crate (!), see this example repo)I also believe that this issue might be related to #20400 somehow
EDIT: I've opened an issue about this: #36587
Kyle Lacy at 2016-05-24 00:06:03
- Removing the
I'm encountering an issue with specialization. Not sure if it's an implementation problem or a problem in the way specialization is specified.
use std::vec::IntoIter as VecIntoIter; pub trait ClonableIterator: Iterator { type ClonableIter; fn clonable(self) -> Self::ClonableIter; } impl<T> ClonableIterator for T where T: Iterator { default type ClonableIter = VecIntoIter<T::Item>; default fn clonable(self) -> VecIntoIter<T::Item> { self.collect::<Vec<_>>().into_iter() } } impl<T> ClonableIterator for T where T: Iterator + Clone { type ClonableIter = T; #[inline] fn clonable(self) -> T { self } }(playpen) (by the way, it would be nice if this code eventually landed in the stdlib one day)
This code fails with:
error: method `clonable` has an incompatible type for trait: expected associated type, found struct `std::vec::IntoIter` [--explain E0053] --> <anon>:14:5 |> 14 |> default fn clonable(self) -> VecIntoIter<T::Item> { |> ^Changing the return value to
Self::ClonableItergives the following error:error: mismatched types [--explain E0308] --> <anon>:15:9 |> 15 |> self.collect::<Vec<_>>().into_iter() |> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found struct `std::vec::IntoIter` note: expected type `<T as ClonableIterator>::ClonableIter` note: found type `std::vec::IntoIter<<T as std::iter::Iterator>::Item>`Apparently you can't refer to the concrete type of a defaulted associated type, which I find quite limiting.
Pierre Krieger at 2016-07-02 09:48:53
@tomaka it should work, the RFC text has this:
impl<T> Example for T { default type Output = Box<T>; default fn generate(self) -> Box<T> { Box::new(self) } } impl Example for bool { type Output = bool; fn generate(self) -> bool { self } }(https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md#the-default-keyword)
Which seems similar enough to your case to be relevant.
James Miller at 2016-07-06 02:31:19
@aatch that example doesn't seem to compile with the intuitive definition for the example trait: https://play.rust-lang.org/?gist=97ff3c2f7f3e50bd3aef000dbfa2ca4e&version=nightly&backtrace=0
the specialization code explicitly disallows this -- see #33481, which I initially thought was an error but turned out to be a diagnostics issue. My PRs to improve the diagnostics here went unnoticed, and I haven't maintained them to the latest master for quite some time.
robert at 2016-07-06 10:10:20
@rphmeier the RFC text suggests that it should be allowed though, that example is copied from it.
James Miller at 2016-07-07 00:59:18
I had a play with some code that could benefit from specialization. I strongly think we should go for the lattice rule rather chaining - it feels natural and was the only way to get the flexibility I needed (afaict).
If we went for
defaulton theimplas well as individual items, could we enforce that if any item is overridden then they all must be? That would allow us to reason based on the precise type of a default assoc type (for example) in the other items, which seems like a useful boost in expressivity.Nick Cameron at 2016-09-17 23:45:01
Should the following be allowed? I want to specialize a type so that ArrayVec is
Copywhen its element type is Copy, and that it otherwise has a destructor. I'm trying to accomplish it by using an internal field that is replaced by specialization.I hoped this would compile, i.e that it deduces the copyability of
ArrayVec<A>'s fields from the field types that are selected by theA: Copy + Arraybound (compilable snippet on playground).impl<A: Copy + Array> Copy for ArrayVec<A> //where <A as Repr>::Data: Copy { }The commented-out where clause is not wanted because it exposes a private type
Reprin the public interface. (It also ICEs anyway).Edit: I had forgotten I reported issue #33162 about this already, I'm sorry.
bluss at 2016-09-18 08:11:44
Follow up on my comment, my actual use case:
// Ideal version trait Scannable {} impl<T: FromStr> Scannable for T {} impl<T: FromStr> Scannable for Result<T, ()> {} // But this doesn't follow from the specialisation rules because Result: !FromStr // Lattice rule would allow filling in that gap or negative reasoning would allow specifying it. // Second attempt trait FromResult { type Ok; fn from(r: Result<Self::Ok, ()>) -> Self; } impl<T> Scannable for T { default type Ok = T; default fn from(r: Result<T, ()>) -> Self {...} // error can't assume Ok == T, could do this if we had `default impl` } impl<T> Scannable for Result<T, ()> { type Ok = T; default fn from(r: Result<T, ()>) -> Self { r } } fn scan_from_str<T: FromResult>(x: &str) -> T where <T as FromResult>::Ok: FromStr // Doesn't hold for T: FromStr because of the default on T::Ok { ... } // Can also add the FromStr bound to FromResult::Ok, but doesn't help // Third attempt trait FromResult<Ok> { fn from(r: Result<Ok, ()>) -> Self; } impl<T> FromResult<T> for T { default fn from(r: Result<Self, ()>) -> Self { ... } } impl<T> FromResult<T> for Result<T, ()> { fn from(r: Result<T, ())>) -> Self { r } } fn scan_from_str<U: FromStr, T: FromResult<U>>(x: &str) -> T { ... } // Error because we can't infer that U == String let mut x: Result<String, ()> = scan_from_str("dsfsf");Nick Cameron at 2016-09-18 19:20:53
@tomaka @Aatch
The problem there is that you are not allowed to rely on the value of other default items. So when you have this impl:
impl<T> ClonableIterator for T where T: Iterator { default type ClonableIter = VecIntoIter<T::Item>; default fn clonable(self) -> VecIntoIter<T::Item> { // ^^^^^^^^^^^^^^^^^^^^ self.collect::<Vec<_>>().into_iter() } }At the spot where I highlighted,
clonableis relying onSelf::ClonableIter, but becauseCloneableIteris declared as default, you can't do that. The concern is that someone might specialize and overrideCloneableIterbut notclonable.We had talked about some possible answers here. One of them was to let you use
defaultto group together items where, if you override one, you must override all:impl<T> ClonableIterator for T where T: Iterator { default { type ClonableIter = VecIntoIter<T::Item>; fn clonable(self) -> VecIntoIter<T::Item> { ... } } }This is ok, but a bit "rightward-drift inducing". The
defaultalso looks like a naming scope, which it is not. There might be some simpler variant that just lets you toggle between "override-any" (as today) vs "override-all" (what you need).We had also hoped we could get by by leveraging
impl Trait. The idea would be that this most often comes up, as is the case here, when you want to customize the return type of methods. So perhaps if you could rewrite the trait to useimpl Trait:pub trait ClonableIterator: Iterator { fn clonable(self) -> impl Iterator; }This would effectively be a kind of shorthand when implemented for a default group containing the type and the fn. (I'm not sure if there'd be a way to do that purely in the impl though.)
PS, sorry for the long delay in answering your messages, which I see date from July.
Niko Matsakis at 2016-09-24 09:30:52
While impl Trait does help, there is no RFC that has been accepted or implemented which allows it to be used with trait bodies in any form, so looking to it for this RFC feels a bit odd.
Sage Griffin at 2016-09-27 23:14:05
I'm interested in implementing the
default implfeature (where all items aredefault). Would you accept a contribution on that?Gianni Ciccarelli at 2016-11-04 17:19:15
@giannicic Definitely! I'd be happy to help mentor the work as well.
Aaron Turon at 2016-11-04 23:07:32
Is there currently a conclusion on whether associated types should be specializable?
The following is a simplification of my use-case, demonstrating a need for specializable associated types. I have a generic data structure, say
Foo, which coordinates a collection of container trait objects (&trait::Property). The traittrait::Propertyis implemented by bothProperty<T>(backed byVec<T>) andPropertyBits(backed byBitVec, a bit vector). In generic methods onFoo, I would like to be able to determine the right underlying data structure forTvia associated types, but this requires specialization to have a blanket impl for non-special cases as follows.trait ContainerFor { type P: trait::Property; } impl<T> ContainerFor for T { default type P = Property<T>; // default to the `Vec`-based version } impl ContainerFor for bool { type P = PropertyBits; // specialize to optimize for space } impl Foo { fn add<T>(&mut self, name: &str) { self.add_trait_obj(name, Box::new(<T as ContainerFor>::P::new()))); } fn get<T>(&mut self, name: &str) -> Option<&<T as ContainerFor>::P> { self.get_trait_obj(name).and_then(|prop| prop.downcast::<_>()); } }Ashish Myles at 2016-11-07 00:52:34
Thanks @aturon ! Basically I'm doing the work by adding a new "defaultness" attribute to the
ast::ItemKind::Implstruct (and then use the new attribute together with the impl item "defaultness" attribute) but there is also a quick and easy possibility consisting on setting default to all the impl items of thedefault implduring parsing. To me this isn't a "complete" solution since we lost the information that the "defaultness" is related to the impl and not to each item of the impl, additionally if there is a plan to introduce apartial implthe first solution would already provide an attribute that can be used to storedefaultas well aspartial. But just to be sure and not wasting time, what do you think about?Gianni Ciccarelli at 2016-11-07 01:33:37
@giannicic @aturon may I propose we create a specific issue to discuss
default impl?Niko Matsakis at 2016-11-08 20:43:13
Never mind, I created one: https://github.com/rust-lang/rust/issues/37653
Niko Matsakis at 2016-11-08 20:46:11
Would the lattice rule allow me to, given:
trait Foo {} trait A {} trait B {} trait C {} // ...add implementations of
Foofor subset of types that implement some combination ofA,B,C, ...:impl Foo for T where T: A { ... } impl Foo for T where T: B { ... } impl Foo for T where T: A + B { ... } impl Foo for T where T: B + C { ... } // ...and allow me to "forbid" some combinations, e.g., that
A + Cshould never happen:impl Foo for T where T: A + C = delete;?
Context: I landed into wanting this when implementing an
ApproxEqual(Shape, Shape)trait for different kinds of shapes (points, cubes, polygons, ...) where these are all traits. I had to work around this by refactoring this into different traits, e.g.,ApproxEqualPoint(Point, Point), to avoid conflicting implementations.gnzlbg at 2016-11-14 14:32:13
@gnzlbg
and allow me to "forbid" some combinations, e.g., that A + C should never happen:
No, this is not something that the lattice rule would permit. That would be more the domain of "negative reasoning" in some shape or kind.
Context: I landed into wanting this when implementing an ApproxEqual(Shape, Shape) trait for different kinds of shapes (points, cubes, polygons, ...) where these are all traits. I had to work around this by refactoring this into different traits, e.g., ApproxEqualPoint(Point, Point), to avoid conflicting implementations.
So @withoutboats has been promoting the idea of "exclusion groups", where you can declare that a certain set of traits are mutually exclusive (i.e., you can implement at most one of them). I envision this as kind of being like an enum (i.e., the traits are all declared together). I like the idea of this, particularly as (I think!) it helps to avoid some of the more pernicious aspects of negative reasoning. But I feel like more thought is needed on this front -- and also a good writeup that tries to summarize all the "data" floating around about how to think about negative reasoning. Perhaps now that I've (mostly) wrapped up my HKT and specialization series I can think about that...
Niko Matsakis at 2016-11-14 15:51:23
@nikomatsakis :
So @withoutboats has been promoting the idea of "exclusion groups", where you can declare that a certain set of traits are mutually exclusive (i.e., you can implement at most one of them). I envision this as kind of being like an enum (i.e., the traits are all declared together). I like the idea of this, particularly as (I think!) it helps to avoid some of the more pernicious aspects of negative reasoning. But I feel like more thought is needed on this front -- and also a good writeup that tries to summarize all the "data" floating around about how to think about negative reasoning. Perhaps now that I've (mostly) wrapped up my HKT and specialization series I can think about that...
I thought about exclusions groups while writing this (you mentioned it in the forums the other day), but I don't think they can work since in this particular example not all traits implementations are exclusive. The most trivial example is the
PointandFloattraits: aFloatcan be a 1D point, soApproxEqualPoint(Point, Point)andApproxEqualFloat(Float, Float)cannot be exclusive. There are other examples likeSquareandPolygon, orBox|CubeandAABB(axis-aligned bounding box) where the "trait hierarchy" actually needs more complex constraints.No, this is not something that the lattice rule would permit. That would be more the domain of "negative reasoning" in some shape or kind.
I would at least be able to implement the particular case and put an
unimplemented!()in it. That would be enough, but obviously I would like it more if the compiler would statically catch those cases in which I call a function with anunimplemented!()in it (and at this point, we are again in negative reasoning land).gnzlbg at 2016-11-14 16:32:15
@gnzlbg lattice specialization would allow you to make that impl panic, but the idea of doing that makes me :cry:.
The idea of "exclusion groups" is really just negative supertrait bounds. One thing we haven't explored too thoroughly is the notion of reverse polarity specialization - allowing you to write a specialized impl that is of reversed polarity to its less specialized impl. For example, in this case you would just write:
impl<T> !Foo for T where T: A + C { }I'm not fully sure what the implications of allowing that are. I think it connects to the issues Niko's already highlighted about how specialization is sort of conflating code reuse with polymorphism right now.
srrrse at 2016-11-14 18:02:51
With all this discussion of negative reasoning and negative impls, I feel compelled to bring up the old Haskell idea of "instance chains" again (paper, paper, GHC issue tracker, Rust pre-RFC), as a potential source of inspiration if nothing else.
Essentially the idea is that anywhere you can write a
traitimpl, you can also write any number of "else if clauses" specifying a differentimplthat should apply in case the previous one(s) did not, with an optional final "else clause" specifying a negative impl (that is, if none of the clauses forTraitapply, then!Traitapplies).Gábor Lehel at 2016-11-15 08:13:56
@withoutboats
The idea of "exclusion groups" is really just negative supertrait bounds.
I think that would be enough for my use cases.
I think it connects to the issues Niko's already highlighted about how specialization is sort of conflating code reuse with polymorphism right now.
I don't know if these can be untangled. I want to have:
- polymorphism: a single trait that abstracts different implementations of an operation for lots of different types,
- code reuse: instead of implementing the operation for each type, I want to implement them for groups of types that implement some traits,
- performance: be able to override an already existing implementation for a particular type or a subset of types that has a more specific set of constraints that the already existing implementations,
- productivity: be able to write and test my program incrementally, instead of having to add a lots of
impls for it to compile.
Covering all cases is hard, but if the compiler forces me to cover all cases:
trait Foo {} trait A {} trait B {} impl<T> Foo for T where T: A { ... } impl<T> Foo for T where T: B { ... } // impl<T> Foo for T where T: A + B { ... } //< compiler: need to add this impl!and also gives me negative impls:
impl<T> !Foo for T where T: A + B { } impl<T> !Foo for T where T: _ { } // _ => all cases not explicitly covered yetI would be able to incrementally add impls as I need them and also get nice compiler errors when I try to use a trait with a type for which there is no impl.
I'm not fully sure what the implications of allowing that are.
Niko mentioned that there are problems with negative reasoning. FWIW the only thing negative reasoning is used for in the example above is to state that the user knows that an impl for a particular case is required, but has explicitly decided not to provide an implementation for it.
gnzlbg at 2016-11-15 10:17:17
I just hit #33017 and don't see it linked here yet. It is marked as a soundness hole so it would be good to track here.
David Tolnay at 2016-11-28 01:18:04
For https://github.com/dtolnay/quote/issues/7 I need something similar to this example from the RFC which doesn't work yet. cc @tomaka @Aatch @rphmeier who commented about this earlier.
trait Example { type Output; fn generate(self) -> Self::Output; } impl<T> Example for T { default type Output = Box<T>; default fn generate(self) -> Box<T> { Box::new(self) } } impl Example for bool { type Output = bool; fn generate(self) -> bool { self } }I stumbled upon the following workaround which gives a way to express the same thing.
#![feature(specialization)] use std::fmt::{self, Debug}; /////////////////////////////////////////////////////////////////////////////// trait Example: Output { fn generate(self) -> Self::Output; } /// In its own trait for reasons, presumably. trait Output { type Output: Debug + Valid<Self>; } fn main() { // true println!("{:?}", Example::generate(true)); // box("s") println!("{:?}", Example::generate("s")); } /////////////////////////////////////////////////////////////////////////////// /// Instead of `Box<T>` just so the "{:?}" in main() clearly shows the type. struct MyBox<T: ?Sized>(Box<T>); impl<T: ?Sized> Debug for MyBox<T> where T: Debug { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "box({:?})", self.0) } } /////////////////////////////////////////////////////////////////////////////// /// Return type of the impl containing `default fn`. type DefaultOutput<T> = MyBox<T>; impl Output for bool { type Output = bool; } impl<T> Example for T where T: Pass { default fn generate(self) -> Self::Output { T::pass({ // This is the impl you wish you could write MyBox(Box::new(self)) }) } } impl Example for bool { fn generate(self) -> Self::Output { self } } /////////////////////////////////////////////////////////////////////////////// // Magic? Soundness exploit? Who knows? impl<T: ?Sized> Output for T where T: Debug { default type Output = DefaultOutput<T>; } trait Valid<T: ?Sized> { fn valid(DefaultOutput<T>) -> Self; } impl<T: ?Sized> Valid<T> for DefaultOutput<T> { fn valid(ret: DefaultOutput<T>) -> Self { ret } } impl<T> Valid<T> for T { fn valid(_: DefaultOutput<T>) -> Self { unreachable!() } } trait Pass: Debug { fn pass(DefaultOutput<Self>) -> <Self as Output>::Output; } impl<T: ?Sized> Pass for T where T: Debug, <T as Output>::Output: Valid<T> { fn pass(ret: DefaultOutput<T>) -> <T as Output>::Output { <T as Output>::Output::valid(ret) } }David Tolnay at 2016-11-28 03:28:30
I am still working on https://github.com/dtolnay/quote/issues/7 and needed a diamond pattern. Here is my solution. cc @zitsen who asked about this earlier and @aturon and @rphmeier who responded.
#![feature(specialization)] /// Can't have these impls directly: /// /// - impl<T> Trait for T /// - impl<T> Trait for T where T: Clone /// - impl<T> Trait for T where T: Default /// - impl<T> Trait for T where T: Clone + Default trait Trait { fn print(&self); } fn main() { struct A; A.print(); // "neither" #[derive(Clone)] struct B; B.print(); // "clone" #[derive(Default)] struct C; C.print(); // "default" #[derive(Clone, Default)] struct D; D.print(); // "clone + default" } trait IfClone: Clone { fn if_clone(&self); } trait IfNotClone { fn if_not_clone(&self); } impl<T> Trait for T { default fn print(&self) { self.if_not_clone(); } } impl<T> Trait for T where T: Clone { fn print(&self) { self.if_clone(); } } impl<T> IfClone for T where T: Clone { default fn if_clone(&self) { self.clone(); println!("clone"); } } impl<T> IfClone for T where T: Clone + Default { fn if_clone(&self) { self.clone(); Self::default(); println!("clone + default"); } } impl<T> IfNotClone for T { default fn if_not_clone(&self) { println!("neither"); } } impl<T> IfNotClone for T where T: Default { fn if_not_clone(&self) { Self::default(); println!("default"); } }David Tolnay at 2016-11-29 16:40:17
Hit a bug (or at least unexpected behavior from my perspective) with specialization and type inference: #38167
Ivan Petkov at 2016-12-04 22:10:48
These two impls should be expected to be valid with specialization, right? It seems to not be successfully picking it up.
impl<T, ST, DB> ToSql<Nullable<ST>, DB> for T where T: ToSql<ST, DB>, DB: Backend + HasSqlType<ST>, ST: NotNull, { ... } impl<T, ST, DB> ToSql<Nullable<ST>, DB> for Option<T> where T: ToSql<ST, DB>, DB: Backend + HasSqlType<ST>, ST: NotNull, { ... }Sage Griffin at 2016-12-10 16:48:22
I filed https://github.com/rust-lang/rust/issues/38516 for some unexpected behavior I ran into while working on building specialization into Serde. Similar to https://github.com/rust-lang/rust/issues/38167, this is a case where the program compiles without the specialized impl and when it is added there is a type error. cc @bluss who was concerned about this situation earlier.
David Tolnay at 2016-12-21 18:31:57
What if we allowed specialization without the
defaultkeyword within a single crate, similar to how we allow negative reasoning within a single crate?My main justification is this: "the iterators and vectors pattern." Sometimes, users want to implement something for all iterators and for vectors:
impl<I> Foo for I where I: Iterator<Item = u32> { ... } impl Foo for Vec<u32> { ... }(This is relevant to other situations than iterators and vectors, of course, this is just one example.)
Today this doesn't compile, and there is tsuris and gnashing of teeth. Specialization solves this problem:
default impl<I> Foo for I where I: Iterator<Item = u32> { ... } impl Foo for Vec<u32> { ... }But in solving this problem, you have added a public contract to your crate: it is possible to overide the iterator impl of
Foo. Maybe we don't want to force you to do that - hence, local specialization withoutdefault.
The question I suppose is, what exactly is the role of
default. Requiringdefaultwas, I think, originally a gesture toward explicitness and self-documenting code. Just as Rust code is immutable by default, private by default, safe by default, it should also be final by default. However, because "non-finality" is a global property, I cannot specialize an item unless I let you specialize an item.srrrse at 2016-12-27 10:19:22
Requiring
defaultwas, I think, originally a gesture toward explicitness and self-documenting code. However [..] I cannot specialize an item unless I let you specialize an item.Is that really so bad though? If you want to specialize an impl then maybe other people want to aswell.
I worry because just thinking about this RFC is already giving me PTSD flashbacks of working in C++ codebases which use obscene amounts of overloading and inheritance and having no idea wtf is going on in any line of code which has a method call in it. I really appreciate the lengths that @aturon has gone to to make specialization explicit and self-documenting.
Andrew Cann at 2016-12-28 03:18:47
Is that really so bad though? If you want to specialize an impl then maybe other people want to aswell.
If other people only "maybe" want to specialize it too, and if there are good cases where we wouldn't want them to, we shouldn't make it impossible to specify this. (a bit similar to encapsulation: you want to access some data and maybe some other people want to as well -- so you explicitly mark this data public, instead of defaulting all data to be public.)
I worry because just thinking about this RFC is already giving me PTSD flashbacks ...
But how would disallowing this specification prevent these things from happening?
Xùdōng Yáng at 2016-12-31 06:02:57
if there are good cases where we wouldn't want them to, we shouldn't make it impossible to specify this.
It's not necessarily a good idea to give users a power whenever they might have a good usecase for it. Not if it also enables users to write confusing code.
But how would disallowing this specification prevent these things from happening?
Say you see
foo.bar()and you want to look at whatbar()does. Right now, if you find the method implemented on a matching type and it's not markeddefaultyou know that its the method definition you're looking for. With @withoutboats' proposal this will no longer be true - instead you'll never know for sure whether you're actually looking at the code which is getting executed.Andrew Cann at 2016-12-31 09:08:45
instead you'll never know for sure whether you're actually looking at the code which is getting executed.
This is quite an exaggeration of the effect of allowing specialization of non-default impls for local types. If you are looking at a concrete impl, you know you are looking at the correct impl. And you have access to the entire source of this crate; you can determine if this impl is specialized or not significantly sooner than "never."
Meanwhile, even with
default, the problem remains when an impl has not been finalized. If the correct impl is actually adefaultimpl, you are in the same situation of having difficulty being unsure if this is the correct impl. And of course if specialization is employed, this will quite commonly be the case (for example, this is the case today for nearly every impl ofToString).In fact I do think this is a rather serious problem, but I'm not convinced that
defaultsolves it. What we need are better code navigation tools. Currently rustdoc makes a very much 'best effort' approach when it comes to trait impls - it doesn't link to their source and it doesn't even list impls that are provided by blanket impls.I'm not saying this change is a slamdunk by any means, but I think its worth a more nuanced consideration.
srrrse at 2016-12-31 09:42:56
It's not necessarily a good idea to give users a power whenever they might have a good usecase for it. Not if it also enables users to write confusing code.
Exactly, I absolutely agree. I think I'm talking about a different "user" here, which is the user of crates you write. You don't want them to freely specialize traits in your crate (possibly affecting the behavior of your crate in a hacky way). On the other hand, we'd be giving more power the "user" you're talking about, namely the crate author, but even without @withoutboats' proposal, you'd have to use "default" and run into the same problem.
Xùdōng Yáng at 2016-12-31 10:53:11
I think
defaulthelps in the sense that if you want to simplify reading a code then you can ask that nobody usedefaultor establish rigorous documentation rules for using it. At that point, you need only worry about thedefaults fromstd, which presumably folks would better understand.I recall the idea that documentation rules could be imposed on usages of specialization contributed to getting the specialization RFC approved.
Jeff Burdges at 2016-12-31 15:58:51
@withoutboats am I correct in reading your motivation for loosening of
defaultas you want a restricted form ofdefaultwhich means "overridable, but only in this crate" (i.e.,pub(crate)but fordefault)? However, to keep things simple you are proposing changing the semantics of omittingdefault, rather than adding graduations ofdefault-ness?Nick Cameron at 2017-01-04 01:17:29
Correct. Doing something like
default(crate)seems like overkill.srrrse at 2017-01-04 01:23:39
A priori, I'd imagine one could simulate that through what the crate exports though, no? Are there any situations where you could not simply introduce a private helper trait with the
defaultmethods and call it from your own finalimpls? You want the user to use yourdefaults but not supply any of their own?Jeff Burdges at 2017-01-04 13:02:25
Correct. Doing something like default(crate) seems like overkill.
I disagree. I really want a restricted form of default. I have been meaning to propose it. My motivation is that sometimes intersection impls etc will force you to add default, but that doesn't mean you want to allow for arbitrary crates to change your behavior. Sorry, have a meeting, I can try to elaborate with an example in a bit.
Niko Matsakis at 2017-01-04 21:02:22
@nikomatsakis I have the same motivation, what I'm proposing is that we just remove the default requirement to specialize in the same crate, as opposed to adding more levers. :-)
srrrse at 2017-01-04 21:06:59
If by chance this non-exported default might be the more common usage, then a
#[default_export]feature would be easier to remember by analogy with#[macro_export]. An intermediate option might be allowing this export feature forpub useorpub modlines.Jeff Burdges at 2017-01-04 21:13:40
Using the
pubkeyword would be better, since Macros 2.0 will support macros as normal items and usepubinstead of#[macro_use]. Usingpubto indicate visibility across the board would be a big win for its consistency.jimmycuadra at 2017-01-04 21:48:19
@withoutboats regardless, I think sometimes you will want to specialize locally but not necessarily open the doors to all
Niko Matsakis at 2017-01-04 21:49:25
Using the pub keyword would be better
Having
pub default fnmean "publicly export the defaultness of the fn" as opposed to affecting the visibility of the function itself would be super confusing to newcomers.Sage Griffin at 2017-01-04 22:42:14
@jimmycuadra is that what you meant by using the
pubkeyword? I agree with @sgrif that it seems more confusing, and if we're going to allow you to scope defaultness explicitly, the same syntax we decide on for scoping visibility seems like the correct path.srrrse at 2017-01-04 22:44:35
Probably not
pub default fnexactly, because that is ambiguous, as you both mention. I was just saying there's value in havingpubuniversally mean "expose something otherwise private to the outside." There's probably some formulation of syntax involvingpubthat would be visually different so as not to be confused with making the function itself public.jimmycuadra at 2017-01-04 23:13:05
Although it is a bit syntaxey, I would not oppose
default(foo)working likepub(foo)- the symmetry between the two marginally outweights the fiddliness of the syntax for me.Nick Cameron at 2017-01-04 23:54:12
Bikeshed warning: have we considered calling it
overridableinstead ofdefault? It's more literally descriptive, andoverridable(foo)reads better to me thandefault(foo)- the latter suggests "this is the default within the scope offoo, but something else might be the default elsewhere", while the former says "this is overridable within the scope offoo", which is correct.Gábor Lehel at 2017-01-05 07:29:35
I think the first two questions are really : Is exporting or not exporting
defaultness significantly more common? Should <i>not</i> exportingdefaultness be the default behavior?Yes case: You could maximize the similarity with exports elsewhere dictates something like
pub mod mymodule default;andpub use mymodule::MyTrait default;, or maybe withoverridable. If needed, you could exportdefaultness for only some methods withpub use MyModule::MyTrait::{methoda,methodb} default;No case: You need to express privateness, not publicness, which differs considerably from anything else in Rust anyways, so now
default(crate)becomes the normal way to control these exports.Also, if exporting and not exporting
defaultness are comparably common, then you guys can probably choose arbitrarily to be in either the yes or no case, so again just pickingpub use MyModule::MyTrait::{methoda,methodb} default;works fine.All these notations look compatible anyways. Another option might be some special
implthat closed off thedefaults, but that sounds complex and strange.Jeff Burdges at 2017-01-05 12:54:53
@burdges Do you have the labels "yes case" and "no case" backwards there, or am I misunderstanding what you're saying?
jimmycuadra at 2017-01-05 21:42:57
Yup, oops! Fixed!
Jeff Burdges at 2017-01-05 21:59:29
We have
impl<T> Borrow<T> for T where T: ?Sizedso that aBorrow<T>bound can treat owned values as if they were borrowed.I suppose we could use specialization to optimize away calls to
clonefrom aBorrow<T>, yes?pub trait CloneOrTake<T> { fn clone_or_take(self) -> T; } impl<B,T> CloneOrTake<T> for B where B: Borrow<T>, T: Clone { #[inline] default fn clone_or_take(b: B) -> T { b.clone() } } impl<T> CloneOrTake<T> for T { #[inline] fn clone_or_take(b: T) -> T { b }; }I'd think this might make
Borrow<T>usable in more situations. I dropped theT: ?Sizedbound because one presumably needsSizedwhen returningT.Another approach might be
pub trait ToOwnedFinal : ToOwned { fn to_owned_final(self) -> Self::Owned; } impl<B> ToOwnedFinal for B where B: ToOwned { #[inline] default fn to_owned_final(b: B) -> Self::Owned { b.to_owned() } } impl<T> ToOwnedFinal for T { #[inline] fn to_owned_final(b: T) -> T { b }; }Jeff Burdges at 2017-01-07 16:42:12
We've made some possibly troubling discoveries today, you can read the IRC logs here: https://botbot.me/mozilla/rust-lang/
I'm not 100% confident about all of the conclusions we reached, especially since Niko's comments after the fact seem uplifting. For a little while it seemed a bit apocalyptic to me.
One thing I do feel fairly sure about is that requiring the
defaultcannot be made compatible with a guarantee that adding newdefaultimpls is always backward compatible. Here's the demonstration:crate
parentv1.0.0trait A { } trait B { } trait C { fn foo(&self); } impl<T> C for T where T: B { // No default, not specializable! fn foo(&self) { panic!() } }crate
client(depends onparent)extern crate parent; struct Local; impl parent::A for Local { } impl parent::C for Local { fn foo(&self) { } }Local implements
AandCbut notB. If local implementedB, its impl ofCwould conflict with the non-specializable blanket impl ofC for T where T: B.crate
parentv1.1.0// Same code as before, but add: default impl<T> B for T where T: A { }This impl has been added, and is a completely specializable impl, so we've said its a non-breaking change. However, it creates a transitive implication - we already had "all B impl C (not specializable)", by adding "all A impl B (specializable)," we've implicitly added the statement "all A impl C (not specializable)". Now the child crate cannot upgrade.
It might be the case that the idea of guaranteeing that adding specializable impls is not a breaking change is totally out the window, because Aaron showed (as you can see in the logs linked above) that you can write impls which make equivalent guarantees regarding defaultness. However, Niko's later comments suggest that such impls may be prohibited (or at least prohibitable) by the orphan rules.
So its uncertain to me if the 'impls are non-breaking' guarantee is salvageable, but it is certain that it is not compatible with explicit control over impl finality.
srrrse at 2017-01-28 05:12:37
Is there any plan on allowing this?
struct Foo; trait Bar { fn bar<T: Read>(stream: &T); } impl Bar for Foo { fn bar<T: Read>(stream: &T) { let stream = BufReader::new(stream); // Work with stream } fn bar<T: BufRead>(stream: &T) { // Work with stream } }So essentially a specialization for a template function which has a type parameter with a bound on
Awhere the specialized version has a bound onB(which requiresA).Thomas Schaller at 2017-01-28 13:21:54
@torkleyy not currently but you can secretly do it by creating a trait which is implemented for both
T: ReadandT: BufReadand containing the parts of your code you want to specialize in the impls of that trait. It doesn't even need to be visible in the public API.srrrse at 2017-01-28 22:35:34
Regarding the backwards compatibility issue, I think thanks to the orphan rules we can get away with these rules:
An impl is backwards compatible to add unless:
- The trait being impl'd is an auto trait.
- The receiver is a type parameter, and every trait in the impl previously existed.
That is, I think in all of the problematic examples the added impl is a blanket impl. We wanted to say that fully default blanket impls are also okay, but I think we just have to say that adding of existing blanket impls can be a breaking change.
The question is what guarantee do we want to make in the face of that - e.g. I think it would be a very nice property if at least a blanket impl can only be a breaking change based on the code in your crate, so you can review your crate and know with certainty whether or not you need to increment the major version.
srrrse at 2017-01-30 01:22:53
@withoutboats
Regarding the backwards compatibility issue, I think thanks to the orphan rules we can get away with these rules:
An impl is backwards compatible to add unless:
- The trait being impl'd is an auto trait.
- The receiver is a type parameter, and every trait in the impl previously existed.
That is, I think in all of the problematic examples the added impl is a blanket impl. We wanted to say that fully default blanket impls are also okay, but I think we just have to say that adding of existing blanket impls can be a breaking change.
A week and many discussions later, this has unfortunately turned out not to be the case.
Aaron Turon at 2017-02-07 01:43:25
The results we've had are :crying_cat_face:, but I think what I wrote there is the same as your conclusion. Adding blanket impls is a breaking change, no matter what. But only blanket impls (and auto trait impls); as far as I know we've not found a case where a non-blanket impl could break downstream code (and that would be very bad).
I did think at one point that we might be able to relax the orphan rules so that you could implement traits for types like
Vec<MyType>, but if we did that this situation would then play out in exactly the same way there://crate A trait Foo { } // new impl // impl<T> Foo for Vec<T> { }// crate B extern crate A; use A::Foo; trait Bar { type Assoc; } // Sadly, this impl is not an orphan impl<T> Bar for Vec<T> where Vec<T>: Foo { type Assoc = (); }// crate C struct Baz; // Therefore, this impl must remain an orphan impl Bar for Vec<Baz> { type Assoc = bool; }srrrse at 2017-02-07 02:00:22
@withoutboats Ah, I understood your two-bullet list as or rather than and, which it seems is what you meant?
Aaron Turon at 2017-02-07 02:05:55
@aturon Yea, I meant 'or' - those are the two cases where it is a breaking change. Any auto trait impl, no matter how concrete, is a breaking change because of the way we allow negative reasoning about them to propogate: https://is.gd/k4Xtlp
That is, unless it contains new names. AFAIK an impl that contains a new name is never breaking.
srrrse at 2017-02-07 02:10:53
@withoutboats I wonder if we can/should restrict people relying on negative logic around auto-traits. That is, if we said that adding new impls of auto traits is a legal breaking change, we might then warn about impls that could be broken by an upstream crate adding
Send. This would work best if we had:- stable specialization, one could overcome the warnings by adding
defaultin strategic places (much of the time); - some form of explicit negative impls, so that types like
Rccould declare their intention to never beSend-- but then we have those for auto traits, so we could take them into account.
Niko Matsakis at 2017-02-07 05:39:37
- stable specialization, one could overcome the warnings by adding
I don't know I think it depends on whether or not there's strong motivation. It seems especially unlikely you'll realize a type could have an
unsafe impl Send/Syncafter you've already released it; I think most of the time that would be safe, you'll have written a type with the foreknowledge that it would be safe (because that's the point of the type).srrrse at 2017-02-07 06:32:25
I add
unsafe impl Send/Syncafter the fact all the time. Sometimes because I make it thread safe, sometimes because I realize the C API I'm interfacing with is fine to share across threads, and sometimes it's just because whether something should beSend/Syncisn't what I'm thinking about when I introduce a type.Sage Griffin at 2017-02-07 23:37:41
I add them after the fact as well when binding C APIs - often because someone explicitly asks for those bounds so I then go through and check what the underlying library guarantees.
Steven Fackler at 2017-02-08 00:06:20
One thing I don't love about how specializing associated traits works right now, this pattern doesn't work:
trait Buffer: Read { type Buffered: BufRead; fn buffer(self) -> impl BufRead; } impl<T: Read> Buffer for T { default type Buffered = BufReader<T>; default fn buffer(self) -> BufReader<T> { BufReader::new(self) } } impl<T: BufRead> Buffer for T { type Buffered = Self; fn buffer(self) -> T { self } }This is because the current system requires that this impl would be valid:
impl Buffer for SomeRead { type Buffered = SomeBufRead; // no overriding of fn buffer, it no longer returns Self::Buffered }impl Traitin traits would release a lot of desire for this sort of pattern, but I wonder if there isn't a better solution where the generic impl is valid but that specialization doesn't work because it introduces a type error?srrrse at 2017-02-13 10:39:58
@withoutboats Yeah, this is one of the main unresolved questions about the design (which I'd forgotten to bring up in recent discussions). There's a fair amount of discussion about this on the original RFC thread, but I'll try to write up a summary of the options/tradeoffs soon.
Aaron Turon at 2017-02-13 16:15:16
@aturon Is the current solution the most conservative (forward compatible with whatever we want to do) or is it a decision we have to make before stabilizing?
srrrse at 2017-02-13 20:55:17
I personally think the only real solution to this problem that @withoutboats raised is to allow items to be "grouped" together when you specify the
defaulttag. It's kind of the better-is-better solution, but I feel like the worse-is-better variant (overriding any means overriding all) is quite a bit worse. (But actually @withoutboats the way you wrote this code is confusing. I think in place of usingimpl BufReadas the return type ofBuffer, you meantSelf::BufReader, right?)In that case, the following would be permitted:
trait Buffer: Read { type Buffered: BufRead; fn buffer(self) -> impl BufRead; } impl<T: Read> Buffer for T { default { type Buffered = BufReader<T>; fn buffer(self) -> BufReader<T> { BufReader::new(self) } } } impl<T: BufRead> Buffer for T { type Buffered = Self; fn buffer(self) -> T { self } }But perhaps we can infer these groupings? I've not given it much thought, but it seems that the fact that item defaults are "entangled" is visible from the trait definition.
Niko Matsakis at 2017-02-16 17:09:38
But actually @withoutboats the way you wrote this code is confusing. I think in place of using impl BufRead as the return type of Buffer, you meant Self::BufReader, right?
Yes, I had modified the solution to an impl Trait based one & then switched back but missed the return type in the trait.
srrrse at 2017-02-16 18:41:49
Maybe something like the type system of this language may also be interesting, since it seems to be similar to Rusts, but with some features, that may solve the current problems. (
A <: Bwould in Rust be true whenAis a struct and implements traitB, or whenAis a trait, and generic implementations for objects of this trait exist, I think)Fabio Krapohl at 2017-02-24 00:48:54
It seems there is an issue with the
Displaytrait for specialization. For instance, this example does not compile:use std::fmt::Display; pub trait Print { fn print(&self); } impl<T: Display> Print for T { default fn print(&self) { println!("Value: {}", self); } } impl Print for () { fn print(&self) { println!("No value"); } } fn main() { "Hello, world!".print(); ().print(); }with the following error:
error[E0119]: conflicting implementations of trait `Print` for type `()`: --> src/main.rs:41:1 | 35 | impl<T: Display> Print for T { | _- starting here... 36 | | default fn print(&self) { 37 | | println!("Value: {}", self); 38 | | } 39 | | } | |_- ...ending here: first implementation here 40 | 41 | impl Print for () { | _^ starting here... 42 | | fn print(&self) { 43 | | println!("No value"); 44 | | } 45 | | } | |_^ ...ending here: conflicting implementation for `()`while this compiles:
pub trait Print { fn print(&self); } impl<T: Default> Print for T { default fn print(&self) { } } impl Print for () { fn print(&self) { println!("No value"); } } fn main() { "Hello, world!".print(); ().print(); }Thanks to fix this issue.
antoyo at 2017-03-05 15:08:37
@antoyo are you sure that's because
Displayis special, or could it be becauseDisplayisn't implemented for tuples whileDefaultis?Jake Goulding at 2017-03-05 15:24:33
@shepmaster I don't know if it is about
Display, but the following works with aCustomtrait not implemented for tuples:pub trait Custom { } impl<'a> Custom for &'a str { } pub trait Print { fn print(&self); } impl<T: Custom> Print for T { default fn print(&self) { } } impl Print for () { fn print(&self) { println!("No value"); } } fn main() { "Hello, world!".print(); ().print(); }By the way, here is the real thing that I want to achieve with specialization:
pub trait Emit<C, R> { fn emit(callback: C, value: Self) -> R; } impl<C: Fn(Self) -> R, R, T> Emit<C, R> for T { default fn emit(callback: C, value: Self) -> R { callback(value) } } impl<C> Emit<C, C> for () { fn emit(callback: C, _value: Self) -> C { callback } }I want to call a function by default, or return a value if the parameter would be unit. I get the same error about conflicting implementations. It is possible (or will this be possible) to do that with specialization? If not, what are the alternatives?
Edit: I think I figured out why it does not compile:
Tinfor Tis more general than()infor ()so the firstimplcannot be the specialization. AndCis more general thanC: Fn(Self) -> Rso the secondimplcannot be the specialization. Please tell me if I'm wrong. But I still don't get why it does not work with the first example withDisplay.antoyo at 2017-03-05 15:28:38
This is currently the correct behavior.
In the
Customexample, those impls do not overlap because of special local negative reasoning. Because the trait is from this crate, we can infer that(), which does not have an impl ofCustom, does not overlap withT: Custom. No specialization necessary.However, we do not perform this negative reasoning for traits that aren't from your crate. The standard library could add
Display for ()in the next release, and we don't want that to be a breaking change. We want libraries to have the freedom to make those kinds of changes. So even though () doesn't impl Display, we can't use that information in the overlap check.But also, because () doesn't impl Display, it is not more specific than
T: Display. This is why specialization does not work, whereas in the Default case,(): Default, therefore that impl is more specific thanT: Default.Impls like this one are sort of in 'limbo' where we can neither assume it overlaps or doesn't. We're trying to figure out a principled way to make this work, but it's not the first implementation of specialization, it's a backwards compatible extension to that feature coming later.
srrrse at 2017-03-05 22:25:11
I filed #40582 to track the lifetime-related soundness issue.
David Tolnay at 2017-03-16 20:47:32
I had an issue trying to use specialization, I don't think its quite the same as what @antoyo had, I had filed it as a separate issue #41140, I can bring the example code from that into here if necessary
Afonso Bordado at 2017-04-21 17:44:40
@afonso360 No, a separate issue is fine.
As a general point: at this point further work on specialization is blocked on the work on Chalk, which should allow us to tackle soundness issues and is also likely to clear up the ICEs being hit today.
Aaron Turon at 2017-04-24 23:25:54
Can someone clarify if this is a bug, or something that is purposely forbidden? https://is.gd/pBvefi
Sage Griffin at 2017-06-01 19:32:49
@sgrif I believe the issue here is just that projection of default associated types is disallowed. Diagnostics could be better though: https://github.com/rust-lang/rust/issues/33481
robert at 2017-06-01 20:00:51
Could you elaborate on why it is expected to be disallowed? We know that no more specific impl could be added, since it would violate the orphan rules.
Sage Griffin at 2017-06-01 20:05:14
This comment indicates it's to necessary for some cases in order to require soundness (although I don't know why) and in others to force consumers of the interface to treat it as an abstract type: https://github.com/rust-lang/rust/blob/e5e664f/src/librustc/traits/project.rs#L41
robert at 2017-06-01 20:08:23
Was anyone ever able to look at https://github.com/rust-lang/rust/issues/31844#issuecomment-266221638 ? Those impls should be valid with specialization as far as I can tell. I believe there is a bug that is preventing them.
Sage Griffin at 2017-07-14 13:44:42
@sgrif I believe the issue with your code there may be similar to the issue in https://github.com/rust-lang/rust/issues/31844#issuecomment-284235369 which @withoutboats explained in https://github.com/rust-lang/rust/issues/31844#issuecomment-284268302. That being said, based on @withoutboats's comment, it seems that the present local reasoning should allow your example to compile, but perhaps I'm mistaken as to what's expected to work.
As an aside, I tried to implement the following, unsuccessfully:
trait Optional<T> { fn into_option(self) -> Option<T>; } impl<R, T: Into<R>> Optional<R> for T { default fn into_option(self) -> Option<R> { Some(self.into()) } } impl<R> Optional<R> for Option<R> { fn into_option(self) -> Option<R> { self } }I intuitively expected
Option<R>to be more specific than<R, T: Into<R>> T, but of course, nothing prevents animpl<R> Into<R> for Option<R>in the future.I'm not sure why this is disallowed, however. Even if an
impl<R> Into<R> for Option<R>was added in the future, I would still expect Rust to choose the non-defaultimplementation, so as far as I can see, allowing this code has no implication on forward-compatibility.In all, I find specialization very frustrating to work with. Just about everything I expect to work doesn't. The only cases where I've had success with specialization are those that are very simple, such as having an two
impls that includeT where T: AandT where T: A + B. I have a hard time getting other things to work, and the error messages don't indicate why attempts to specialize don't work. Of course, there's still a road ahead, so I don't expect very helpful error messages. But there seem to be quite a few cases where I really expect something to work (like above) but it just doesn't, and it's currently quite difficult for me to ascertain if that's because I've misunderstood what's allowed (and more importantly, why), if something is wrong, or if something just hasn't been implemented yet. A nice overview of what's going on with this feature as it stands would be very helpful.Sergio Benitez at 2017-08-30 03:38:37
I'm not positive this is in the right place, but we ran into a problem on the users forum that I'd like to mention here.
The following code (which is adapted from the RFC here) does not compile on nightly:
#![feature(specialization)] trait Example { type Output; fn generate(self) -> Self::Output; } default impl<T> Example for T { type Output = Box<T>; fn generate(self) -> Self::Output { Box::new(self) } } impl Example for bool { type Output = bool; fn generate(self) -> Self::Output { self } }This doesn't really seem like a glitch but more like a usability problem - if a hypothetical
implspecialized only the associated type in the example above, thedefaulti implofgeneratewouldn't typecheck.Link to the thread here
burns47 at 2017-09-02 15:44:50
@burns47 there is a confusing but useful workaround here: https://github.com/rust-lang/rust/issues/31844#issuecomment-263175793.
David Tolnay at 2017-09-02 15:57:24
@dtolnay Not quite satisfactory - what if we're specializing on traits we don't own (and can't modify)? We shouldn't need to rewrite/refactor trait definitions to do this IMO.
burns47 at 2017-09-02 19:36:01
Can anyone comment as to whether the code in the following issue is intentionally rejected? https://github.com/rust-lang/rust/issues/45542
bstrie at 2017-10-26 01:07:12
Would specialization allow adding something like the following to libcore?
impl<T: Ord> Eq for T {} impl<T: Ord> PartialEq for T { default fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal } } impl<T: Ord> PartialOrd for T { default fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } }This way you could implement
Ordfor your custom type and haveEq,PartialEq, andPartialOrdbe automatically implemented.Note that implementing
Ordand simultaneously derivingPartialEqorPartialOrdis dangerous and can lead to very subtle bugs! With these default impls you would be less tempted to derive those traits, so the problem would be somewhat mitigated.
Alternatively, we modify derivation to take advantage of specialization. For example, writing
#[derive(PartialOrd)]abovestruct Foo(String)could generate the following code:impl PartialOrd for Foo { default fn partial_cmp(&self, other: &Foo) -> Option<Ordering> { self.0.partial_cmp(&other.0) } } impl PartialOrd for Foo where Foo: Ord { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } }This way the default impl gets used if
Ordis not implemented. But if it is, thenPartialOrdrelies onOrd. Unfortunately, this doesn't compile:error[E0119]: conflicting implementations of trait `std::cmp::PartialOrd` for type `Foo`Deleted user at 2018-01-07 13:24:27
@stjepang I certainly hope the blankets like that can be added --
impl<T:Copy> Clone for Ttoo.scottmcm at 2018-01-07 19:04:25
I think
impl<T: Ord> PartialEq for Tshould be
impl<T, U> PartialEq<U> for T where T : PartialOrd<U>because
PartialOrdrequiresPartialEqand can provide it too.Jan Hudec at 2018-01-09 07:40:41
Right now, one cannot really use associated types to constrain a specialization, both because they cannot be left unspecified and because they trigger uneeded recursion. See https://github.com/dhardy/rand/issues/18#issuecomment-358147645
Jeff Burdges at 2018-01-16 23:59:26
Eventually, I'ld love to see what I'm calling specialization groups with the syntax proposed by @nikomatsakis here https://github.com/rust-lang/rust/issues/31844#issuecomment-249355377 and independently by me. I'ld like to write an RFC on that proposal later when we're closer to stabilizing specialization.
Mazdak Farrokhzad at 2018-01-22 22:05:35
Just in case nobody saw it, this blog post covers a proposal to make specialization sound in the face of lifetime-based dispatch.
Niko Matsakis at 2018-02-25 22:16:30
As copy closures were already stablized in Beta, developers have more motivation to stabilizing on specialization now. The reason is that
FnandFnOnce + Clonerepresent two overlapping set of closures, and in many case we need to implement traits for both of them.Just figure out that the wording of rfc 2132 seems to imply that there are only 5 types of closures:
FnOnce(amoveclosure with all captured variables being neitherCopynorClone)FnOnce + Clone(amoveclosure with all captured variables beingClone)FnOnce + Copy + Clone(amoveclosure with all captured variables beingCopyand soClone)FnMut + FnOnce(a non-moveclosure with mutated captured variables)Fn + FnMut + FnOnce + Copy + Clone(a non-moveclosure without mutated captured variables)
So if specification is not available in the near future, maybe we should update our definition of
Fntraits soFndoes not overlapping withFnOnce + Clone?I understand that someone may already implemented specific types that is
FnwithoutCopy/Clone, but should this be deprecated? I think there is always better way to do the same thing.earthengine at 2018-04-16 02:29:15
Is the following supposed to be allowed by specialization (note the absence of
default) or is it a bug?#![feature(specialization)] mod ab { pub trait A { fn foo_a(&self) { println!("a"); } } pub trait B { fn foo_b(&self) { println!("b"); } } impl<T: A> B for T { fn foo_b(&self) { println!("ab"); } } impl<T: B> A for T { fn foo_a(&self) { println!("ba"); } } } use ab::B; struct Foo; impl B for Foo {} fn main() { Foo.foo_b(); }without specialization, this fails to build with:
error[E0119]: conflicting implementations of trait `ab::B` for type `Foo`: --> src/main.rs:24:1 | 11 | impl<T: A> B for T { | ------------------ first implementation here ... 24 | impl B for Foo {} | ^^^^^^^^^^^^^^ conflicting implementation for `Foo`Mike Hommey at 2018-05-09 08:15:27
@glandium what on earth is going on there? Nice example, here the playground link: https://play.rust-lang.org/?gist=fc7cf5145222c432e2bd8de1b0a425cd&version=nightly&mode=debug
gnzlbg at 2018-05-09 08:52:26
@glandium that is https://github.com/rust-lang/rust/issues/48444
Niko Matsakis at 2018-05-10 15:08:58
is it? there is no empty impl in my example.
Mike Hommey at 2018-05-10 21:46:32
@glandium
impl B for Foo {}Mohammad AlSaleh at 2018-05-10 22:32:16
@MoSal but that impl "isn't empty" since
Badds a method with a default implementation.gnzlbg at 2018-05-11 07:54:16
@gnzlbg It is empty by definition. Nothing between the braces.
Alexander Regueiro at 2018-05-11 16:30:56
#![feature(specialization)] use std::borrow::Borrow; #[derive(Debug)] struct Bla { bla: Vec<Option<i32>> } // Why is this a conflict ? impl From<i32> for Bla { fn from(i: i32) -> Self { Bla { bla: vec![Some(i)] } } } impl<B: Borrow<[i32]>> From<B> for Bla { default fn from(b: B) -> Self { Bla { bla: b.borrow().iter().map(|&i| Some(i)).collect() } } } fn main() { let b : Bla = [1, 2, 3].into(); println!("{:?}", b); }error[E0119]: conflicting implementations of trait `std::convert::From<i32>` for type `Bla`: --> src/main.rs:17:1 | 11 | impl From<i32> for Bla { | ---------------------- first implementation here ... 17 | impl<B: Borrow<[i32]>> From<B> for Bla { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bla` | = note: upstream crates may add new impl of trait `std::borrow::Borrow<[i32]>` for type `i32` in future versionsWouldn't specialization prevent possible future conflicts?
Mohammad AlSaleh at 2018-06-11 21:27:25
Goodness me, this is a slow-moving feature! No progress in over two years, it seems (certainly according to the original post). Has the lang team abandoned this?
Alexander Regueiro at 2018-06-12 00:26:01
@alexreg see http://aturon.github.io/2018/04/05/sound-specialization/ for the latest development.
Mazdak Farrokhzad at 2018-06-12 00:28:40
@alexreg It turns out soundness is hard. I believe there is some work on the "always applicable impls" idea currently happening, so there is progress. See https://github.com/rust-lang/rust/pull/49624. Also, I believe that the chalk working group is working on implementing the "always applicable impls" idea too, but I don't know how far that has gotten.
mark at 2018-06-12 03:36:25
After a bit of wrangling, it seems it is possible to effectively implement intersection impls already via a hack using
specializationandoverlapping_marker_traits.https://play.rust-lang.org/?gist=cb7244f41c040db41fc447d491031263&version=nightly&mode=debug
Rimi Kanokawa at 2018-06-15 15:04:04
cc https://github.com/rust-lang/rfcs/pull/2532
Mazdak Farrokhzad at 2018-08-27 20:00:06
I tried to write a recursive specialized function to implement an equivalent to this C++ code:
<details> <summary>C++ code</summary>
</details><br/>#include <cassert> #include <vector> template<typename T> size_t count(T elem) { return 1; } template<typename T> size_t count(std::vector<T> vec) { size_t n = 0; for (auto elem : vec) { n += count(elem); } return n; } int main() { auto v1 = std::vector{1, 2, 3}; assert(count(v1) == 3); auto v2 = std::vector{ std::vector{1, 2, 3}, std::vector{4, 5, 6} }; assert(count(v2) == 6); return 0; }I tried this:
<details> <summary>Rust code</summary>
</details><br/>#![feature(specialization)] trait Count { fn count(self) -> usize; } default impl<T> Count for T { 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); }But I am getting an:
overflow evaluating the requirement `{integer}: Count`I do not think that this should happen because
impl<T> Count for T where T::Item: Countshould not overflow.EDIT: sorry, I just saw that this was already mentioned
Félix at 2018-08-28 09:17:04
@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); }Guillaume P. at 2018-08-28 09:42:50
Has the soundness hole still not been fixed yet?
Alexander Regueiro at 2018-09-21 01:44:31
@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...
mark at 2018-09-21 01:59:30
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.
Alexander Regueiro at 2018-09-22 16:23:46
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.
Joshua Yanovski at 2018-10-12 11:30:38
@pythonesque There's some discussion at https://github.com/rust-lang/rust/pull/42411
Sage Griffin at 2018-10-16 21:44:41
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
Joshua Yanovski at 2018-10-17 09:54:48
And it is indeed true that what I want is of a "negative reasoning" flavor (though closed traits would not really be sufficient).
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.
Timplements one of{ Float | Int | Bool | Ptr }).gnzlbg at 2018-10-17 11:13:19
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 :)
Joshua Yanovski at 2018-10-17 11:27:08
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?
rmanoka at 2018-10-26 21:01:18
@rmanoka Isn't this just the normal orphan rules? In the first case, no downstream crate could
impl Bar for ()so the compiler allows this, but in the second example, a downstream crate couldimpl Bar<CustomType> for ()which would conflict with your default impl.Boscop at 2018-10-27 16:00:41
@Boscop In that scenario, the default impl should anyway be overridden by the non-default one below. For instance, if I had:
impl Bar<bool> for () {}added before the other impls, then I would expect it to work (as per RFC / expectation). Isn't that correct?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.
rmanoka at 2018-10-27 17:21:30
This issue probably depends on #45814.
Ellen Emilia Anna Zscheile at 2018-12-31 00:02:31
Are there any plans to support trait bounds on the default that are not present in the specialisation?
As an example for which this would be very useful, such that you can easily compose handling of different types by creating a generic Struct with arbitrary Inner for the functionality that shouldn't be shared.
#![feature(specialization)] trait Handler<M> { fn handle(&self, m:M); } struct Inner; impl Handler<f64> for Inner { fn handle(&self, m : f64) { println!("inner got an f64={}", m); } } struct Struct<T>(T); impl<T:Handler<M>, M:std::fmt::Debug> Handler<M> for Struct<T> { default fn handle(&self, m : M) { println!("got something else: {:?}", m); self.0.handle(m) } } impl<T> Handler<String> for Struct<T> { fn handle(&self, m : String) { println!("got a string={}", m); } } impl<T> Handler<u32> for Struct<T> { fn handle(&self, m : u32) { println!("got a u32={}", m); } } fn main() { let s = Struct(Inner); s.handle("hello".to_string()); s.handle(5.0 as f64); s.handle(5 as u32); }Furthermore, in the example above, something odd that I've experienced - after removing the trait bound on the default Handler impl (and also self.0.handle(m)) the code compiles without issues. However, when you remove the implementation for u32, it seems to break the other trait deduction:
#![feature(specialization)] trait Handler<M> { fn handle(&self, m:M); } struct Struct<T>(T); impl<T, M:std::fmt::Debug> Handler<M> for Struct<T> { default fn handle(&self, m : M) { println!("got something else: {:?}", m); } } impl<T> Handler<String> for Struct<T> { fn handle(&self, m : String) { println!("got a string={}", m); } } // impl<T> Handler<u32> for Struct<T> { // fn handle(&self, m : u32) { // println!("got a u32={}", m); // } // } fn main() { let s = Struct(()); s.handle("hello".to_string()); s.handle(5.0 as f64); }Even though there is no code calling the handler for u32, the specialisation not being there causes the code to not compile.
Martijn Bakker at 2019-02-12 21:00:09
Edit: this seems to be the same as the second problem ("However, when you remove the implementation for u32, it seems to break the other trait deduction") that Gladdy mentioned a one post back.
With rustc 1.35.0-nightly (3de010678 2019-04-11), the following code gives an error:
#![feature(specialization)] trait MyTrait<T> { fn print(&self, parameter: T); } struct Message; impl<T> MyTrait<T> for Message { default fn print(&self, parameter: T) {} } impl MyTrait<u8> for Message { fn print(&self, parameter: u8) {} } fn main() { let message = Message; message.print(1_u16); }error:
error[E0308]: mismatched types --> src/main.rs:20:19 | 18 | message.print(1_u16); | ^^^^^ expected u8, found u16However, the code compiles and works when I omit the
impl MyTrait<u8>block:#![feature(specialization)] trait MyTrait<T> { fn print(&self, parameter: T); } struct Message; impl<T> MyTrait<T> for Message { default fn print(&self, parameter: T) {} } /* impl MyTrait<u8> for Message { fn print(&self, parameter: u8) {} } */ fn main() { let message = Message; message.print(1_u16); }Is this by design, is this because the implementation is incomplete, or is this a bug?
Also, I would like to know if this use case for specialization (implementing traits with overlapping type parameters for a single concrete type as opposed to implementing the same trait for overlapping types) will be supported. Reading section "Defining the precedence rules" in RFC 1210, I think it would be supported, but the RFC does not give such examples and I don't know if we are still strictly following this RFC.
PieterPenninckx at 2019-05-04 15:43:08
Report a weirdness:
trait MyTrait {} impl<E: std::error::Error> MyTrait for E {} struct Foo {} impl MyTrait for Foo {} // OK // But this one is conflicting with error message: // // "... note: upstream crates may add new impl of trait `std::error::Error` for type // std::boxed::Box<(dyn std::error::Error + 'static)>` in future versions" // // impl MyTrait for Box<dyn std::error::Error> {}Why is
Box<dyn std::error::Error>peculiar (avoid using word "special") in this case? Even if it implsstd::error::Errorin the future, theimpl MyTrait for Box<dyn std::error::Error>is still a valid specialization ofimpl<E: std::error::Error> MyTrait for E, no?Zihan Liu at 2019-05-10 13:38:09
is still a valid specialization
In your case the
impl<E: std::error::Error> MyTrait for Ecan not be specialized, as it doesnt have anydefaultmethods.bjorn3 at 2019-05-10 15:05:31
@bjorn3 This looks like it should work, but it doesn't even if you add in dummy methods
in crate
barpub trait Bar {} impl<B: Bar> Bar for Box<B> {}In crate
foo#![feature(specialization)] use bar::*; trait Trait { fn func(&self) {} } impl<E: Bar> Trait for E { default fn func(&self) {} } struct Foo; impl Trait for Foo {} // OK impl Trait for Box<dyn Bar> {} // Error error[E0119]: conflicting implementations of traitNote that if you change crate
bartopub trait Bar {} impl<B: ?Sized + Bar> Bar for Box<B> {}Then crate
foocompiles.RustyYato at 2019-05-10 23:02:24
@bjorn3 Seems that we don't need a
defaultmethod to specialized it (playground).@KrishnaSannasi I cannot reproduce the "conflicting implementations" error in your example (playground).
Update: Oh, I see. The trait
Barmust be from an upstream crate for the example to work.Zihan Liu at 2019-05-13 02:40:30
@updogliu you example does not show specialization because
Foodoes not implementError.RustyYato at 2019-05-13 14:10:34
Am I programming too late tonight, or should this not cause a stack overflow?
#![feature(specialization)] use std::fmt::Debug; trait Print { fn print(self); } default impl<T> Print for [T; 1] where T: Debug { fn print(self) { println!("{:?}", self); } } impl<T> Print for [T; 1] where T: Debug + Clone { fn print(self) { println!("{:?}", self.clone()); } } fn main() { let x = [0u8]; x.print(); }Lander Brandt at 2019-06-27 08:25:54
Coarse-grained
default implblocks have always been doing very weird things for me, I would suggest trying thedefault fnfine-grained specialization syntax instead.EDIT: Upon cross-checking the RFC, this is expected, as
default implactually does not mean that all items in theimplblock aredefaulted. I find those semantics surprising to say the least.Hadrien G. at 2019-06-27 08:54:30
@HadrienG2 I actually have always used
default fnin this project but this time I forgot thedefaultkeyword and the compiler suggested to add it to theimpl. Hadn't seen the stack recursion issue before and wasn't sure if it was expected at this stage. Thanks for the suggestion,default fnworks fine.Lander Brandt at 2019-06-27 19:11:54
Looking at the original RFC, there is a section about specialization of inherent impls. Did someone gave that I try ?
The approach proposed in the RFC might not work directly anymore, at least, for inherent const methods:
// This compiles correctly today: #![feature(specialization)] use std::marker::PhantomData; struct Foo<T>(PhantomData<T>); impl<T> Foo<T> { default const fn foo() -> Self { Self(PhantomData) } // ^^should't default here error? } // ---- // Adding this fails: impl<T: Copy> Foo<T> { const fn foo() -> Self { Self(PhantomData) } }The original RFC proposes lifting the method into a trait, implementing that for the type, and specializing the impl. I suppose that for const fn methods, those impls of the trait for the type would need to be const impls.
gnzlbg at 2019-08-28 18:28:30
For anyone coming across this and curious about the status -- there were a couple of significant conceptual advances in 2018: http://smallcultfollowing.com/babysteps/blog/2018/02/09/maximally-minimal-specialization-always-applicable-impls/ http://aturon.github.io/tech/2018/04/05/sound-specialization/
More recently, last month @nikomatsakis wrote (as an example, in another context; bold mine) that:
there was one key issue [in specialization] that never got satisfactorily resolved, a technical soundness concern around lifetimes and traits [...] Then, [those two posts linked above]. It seems like these ideas have basically solved the problem, but we’ve been busy in the meantime and haven’t had time to follow up.
Sounds hopeful though there's clearly still work to do.
(Posting this because I found this thread some weeks ago and had no idea of last year's progress, then more recently came across those posts by accident. There are comments above mentioning them, but GitHub makes it increasingly hard to see any but the first and last few comments on a long thread :cry: . It might be helpful if this update made it into the issue description.)
Greg Price at 2019-08-31 03:51:27
Hello everyone! Could someone tell me why this use case does not work? Bugs or expected behaviours?
As this example.
impl A for i32is ok, butimpl A for ()cannot be compiled in 1.39.0-nightly.#![feature(specialization)] trait A { fn a(); } default impl <T: ToString> A for T { fn a() {} } impl A for i32 { fn a() {} } impl A for () { fn a() {} }compiling message:
error[E0119]: conflicting implementations of trait `A` for type `()`: --> src/lib.rs:16:1 | 8 | default impl <T: ToString> A for T { | ---------------------------------- first implementation here ... 16 | impl A for () { | ^^^^^^^^^^^^^ conflicting implementation for `()` | = note: upstream crates may add new impl of trait `std::fmt::Display` for type `()` in future versionsxixi at 2019-09-03 09:01:04
@Hexilee Put
defaulton the methods not the impl.RustyYato at 2019-09-03 16:38:01
@KrishnaSannasi example 2
Ellen Emilia Anna Zscheile at 2019-09-03 17:00:44
@zserik yes, I know. I don't think it has been implemented yet, or it was dropped. In any case it doesn't work now.
RustyYato at 2019-09-03 18:50:20
It obviously doesn't work now, but I think it should work.
xixi at 2019-09-05 06:55:50
I'm asking this here, because I haven't noticed this topic come up anywhere else - are there any any plans to
default-ify various standard library functions, similarly to how we've hasconst-ified functions when it's been deemed safe to do so? The main reason I'm asking is that the default genericFromandIntoimplementations (impl<T, U: From<T>> Into<U> for Tandimpl<T> From<T> for T) make it difficult to write comprehensive genericFromandIntoimplementations downstream fromcore, and it would be nice if I could override those conversions in my own crates.Osspial at 2019-09-14 19:36:34
Even if we allow specialization for
From/Intoit wouldn't help generic impls because of the lattice problem.RustyYato at 2019-09-15 07:53:16
@KrishnaSannasi I don't believe that's the case. For example, this code should work if
FromandIntowere specializable, but doesn't because they aren't:impl<M: Into<[S; 2]>, S> From<M> for GLVec2<S> { fn from(to_array: M) -> GLVec2<S> { unimplemented!() } } impl<M, S> Into<M> for GLVec2<S> where [S; 2]: Into<M>, { fn into(self) -> M { unimplemented!() } } pub struct GLVec2<S> { pub x: S, pub y: S, }That works if you convert
FromandIntointo a custom trait that doesn't have those generic implementaitons: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cc126b016ff62643946aebc6bab88c98Osspial at 2019-09-15 20:10:11
@Osspial Well, if you try and simulate using a default impl, you will see the issue,
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e5b9da0eeca05d063e2605135a0b5ead
I will repeat, changing
From/Intoimpl to be a default impl in the standard library will not make generic impls forIntopossible. (and it doesn't affect generic impls ofFrom)RustyYato at 2019-09-15 21:53:35
Hi, there is a serious bug in the current specialization implementation. I label it as a bug because even if that was an explicit design decision, it prevents us from using one of the most powerful specialization features, namely the possibility of creation of "opaque types" (this is not a formal name). This pattern is one of the most primitive building blocks in other languages providing type classes, like Haskell or Scala.
This pattern is simple – we can define structures like
WithLabelorWithIDwhich add some fields and methods to underlying structures, so for example if we createWithLabel<WithID<MyType>>then we will be able to getid,labeland all fields / methods ofMyTypeas well. Unfortunately, with the current implementation, it is not possible.Below is an example code showing usage of this pattern. The commented-out code does not compile, while it should to make this pattern really useful:
#![feature(specialization)] use std::ops::Deref; use std::ops::DerefMut; // ================= // === WithLabel === // ================= struct WithLabel<T>(String, T); pub trait HasLabel { fn label(&self) -> &String; } impl<T> HasLabel for WithLabel<T> { fn label(&self) -> &String { &self.0 } } // THIS SHOULD COMPILE, BUT GETS REJECTED // impl<T> HasLabel for T // where T: Deref, <Self as Deref>::Target : HasLabel { // default fn label(&self) -> &String { // self.deref().label() // } // } impl<T> Deref for WithLabel<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.1 } } // ============== // === WithID === // ============== struct WithID<T>(i32, T); pub trait HasID { fn id(&self) -> &i32; } impl<T> HasID for WithID<T> { fn id(&self) -> &i32 { &self.0 } } // THIS SHOULD COMPILE, BUT GETS REJECTED // impl<T> HasID for T // where T: Deref, <Self as Deref>::Target : HasID { // default fn id(&self) -> &i32 { // self.deref().id() // } // } impl<T> Deref for WithID<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.1 } } // ============= // === Usage === // ============= struct A(i32); type X = WithLabel<WithID<A>>; fn test<T: HasID + HasLabel> (t: T) { println!("{:?}", t.label()); println!("{:?}", t.id()); } fn main() { let v1 = WithLabel("label1".to_string(), WithID(0, A(1))); // test(v1); // THIS IS EXAMPLE USE CASE WHICH DOES NOT COMPILE }In order to make the
test(v1)line working, we need to add such trait impl manually:impl<T: HasID> HasID for WithLabel<T> { fn id(&self) -> &i32 { self.deref().id() } }Of course, to make it complete, we would also need to make this trait impl:
impl<T: HasLabel> HasLabel for WithID<T> { fn label(&self) -> &String { self.deref().label() } }And this is VERY BAD. For just 2 types it's simple. However, imagine that we've got 10 different opaque-types definitions, which add different fields, like
WithID,WithLabel,WithCallback, ... you name it. With the current behavior of specializations, we would need to define ... over 1000 different trait implementations! Adding 11th such type would require us to add a ton of other implementations, for all other types. If the commented out code would be accepted, we would need only 10 trait implementations and implementing each new type will require only a single additional implementation.Wojciech Daniło at 2019-11-02 08:35:33
I'm not sure how your code relates to specialization. Your argument (your initial code compiles but the commented
test(v1);line doesn't compile without the manual impl you present) still applies if the first#![feature(specialization)]line is removed.Masaki Hara at 2019-11-02 08:48:13
@qnighy The code should compile after uncommenting the impls
HasLabel for TandHasID for T– they are using specialization. Currently, they are rejected (try uncommenting them in the code I provided!). Does it make sense now to you? 🙂Wojciech Daniło at 2019-11-02 08:50:06
Let's consider three instances
WithLabel<WithID<A>>,WithID<WithLabel<A>>andWithLabel<WithLabel<A>>. Then- the first impl covers
WithLabel<WithID<A>>andWithLabel<WithLabel<A>>. - the second impl covers
WithID<WithLabel<A>>andWithLabel<WithLabel<A>>.
Therefore the pair of impls doesn't satisfy the following clause from the RFC:
To ensure specialization is coherent, we will ensure that for any two impls
IandJthat overlap, we have eitherI < JorJ < I. That is, one must be truly more specific than the other.And it is a real problem in your case too because the
HasLabelimpl ofWithLabel<WithLabel<A>>could be interpreted in two ways.How we can cover this case is already discussed in the RFC too, and the conclusion is:
The limitations that the lattice rule addresses are fairly secondary to the main goals of specialization (as laid out in the Motivation), and so, since the lattice rule can be added later, the RFC sticks with the simple chain rule for now.
Masaki Hara at 2019-11-02 09:05:58
- the first impl covers
@qnighy, thanks for thinking about it.
And it is a real problem in your case too because the HasLabel impl of
WithLabel<WithLabel<A>>could be interpreted in two ways.This is true if we don't consider the
impl<T> HasLabel for WithLabel<T>as more specialized thanimpl<T> HasLabel for Tfor the input ofWithLabel<WithLabel<A>>. The part of the RFC you pasted does indeed covers that, however, I believe that this is a serious limitation and I would ask for re-considering support for this use case in the first release of this extension.In the meantime, I was playing with with
negative trait implsbecause they may actually resolve the points you covered. I created a code which does not have the problems you describe (unless I'm missing something), however, it still does not compile. This time, I don't understand where the constraints mentioned in the error came from, as the resolution should not be ambiguous.The good thing is that actually everything compiles now (including specializations) but not the
test(v1)usage:#![feature(specialization)] #![feature(optin_builtin_traits)] use std::ops::Deref; use std::ops::DerefMut; // ================= // === WithLabel === // ================= struct WithLabel<T>(String, T); auto trait IsNotWithLabel {} impl<T> !IsNotWithLabel for WithLabel<T> {} pub trait HasLabel { fn label(&self) -> &String; } impl<T> HasLabel for WithLabel<T> { fn label(&self) -> &String { &self.0 } } impl<T> HasLabel for T where T: Deref + IsNotWithLabel, <Self as Deref>::Target : HasLabel { default fn label(&self) -> &String { self.deref().label() } } impl<T> Deref for WithLabel<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.1 } } // ============== // === WithID === // ============== struct WithID<T>(i32, T); pub trait HasID { fn id(&self) -> &i32; } impl<T> HasID for WithID<T> { fn id(&self) -> &i32 { &self.0 } } auto trait IsNotWithID {} impl<T> !IsNotWithID for WithID<T> {} impl<T> HasID for T where T: Deref + IsNotWithID, <Self as Deref>::Target : HasID { default fn id(&self) -> &i32 { self.deref().id() } } impl<T> Deref for WithID<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.1 } } // ============= // === Usage === // ============= struct A(i32); type X = WithLabel<WithID<A>>; fn test<T: HasID + HasLabel> (t: T) { println!("{:?}", t.label()); println!("{:?}", t.id()); } fn main() { let v1 = WithLabel("label1".to_string(), WithID(0, A(1))); test(v1); }Wojciech Daniło at 2019-11-02 09:19:26
In the meantime, you can exploit RFC1268
overlapping_marker_traitsto allow overlapping non-marker traits, but this hack requires three more traits (one for going through marker traits, two for re-acquiring erased data through specialization).https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=b66ee0021db73efaaa5d46edfb4f3990
Masaki Hara at 2019-11-02 09:29:19
@qnighy I've created a separate issue about this bug: https://github.com/rust-lang/rust/issues/66041
Wojciech Daniło at 2019-11-02 09:35:33
Ok, I just discovered that
auto traitswill never be solution here, as (according to https://doc.rust-lang.org/nightly/unstable-book/language-features/optin-builtin-traits.html) they propagate to all the fields in a struct:Auto traits, like Send or Sync in the standard library, are marker traits that are automatically implemented for every type, unless the type, or a type it contains, has explicitly opted out via a negative impl.
EDIT @qnighy somehow I overlooked that you provided a link to the playground. ❤️ Thank you so much for it. It works and I am amazed by how hacky this solution is. Amazing that we are able to express that currently and I hope this possibility will not disappear in the future!
In such a situation,
overlapping marker traitsare the only hack we can use now, but I think it would be nice to allow in the future some kind of easier solution to express opaque types (as described in my previous post: https://github.com/rust-lang/rust/issues/31844#issuecomment-549023367).Wojciech Daniło at 2019-11-02 09:59:07
A very simple example (simplification of above example) which fails:
trait Trait<T> {} impl<T> Trait<T> for T {} impl<T> Trait<()> for T {}I don't believe this hits the problem identified with lattice rules, however perhaps an overly simplistic solver thinks it does?
Without this, the current implementation is useless for my purposes. If the above were allowed, then I believe it would also be possible to implement
Fromon wrapper types (though I'm unsure aboutInto).Diggory Hardy at 2019-11-11 18:37:02
For any body that doesn't know yet: there is this awesome trick discovered by dtolnay that allows to use (very limited) specialization on stable rust
macpp at 2019-11-24 19:17:31
I'm not sure whether this has already been addressed, but traits with default implementations for their methods have to be redefined just so that they can be marked as
default. Example;trait Trait { fn test(&self) { println!("default implementation"); } } impl<T> Trait for T { // violates DRY principle default fn test(&self) { println!("default implementation"); } }I propose the following syntax to fix this (if it needs fixing):
impl<T> Trait for T { // delegates to the already existing default implementation default fn test(&self); }Moved to #68309
jazzfool at 2020-01-17 11:13:04
@jazzfool Please refile this as an issue (applies generally to everyone asking similar questions here) and cc me on that one.
Mazdak Farrokhzad at 2020-01-17 11:51:17
Is there an approach for testing specialization? E.g. when writing a test that checks the correctness of a specialization you first need to know whether the specialization you're trying to test is actually applied instead of the default implementation.
the8472 at 2020-01-25 16:00:41
@the8472 do you mean testing the compiler, or do you mean testing in your own code? You can certainly write unit tests that behave differently (i.e., call a fn, and see if you get the specialized variant). Perhaps you are saying that the two variants are equivalent, except for one is supposed to be faster, and hence you're not sure how to test which version you are getting? In that case, I agree, I don't know how you can test that right now.
You could I suppose make some other trait with the same set of impls, but where the fns behave differently, just to reassure yourself.
Niko Matsakis at 2020-01-27 04:44:38
Perhaps you are saying that the two variants are equivalent, except for one is supposed to be faster, and hence you're not sure how to test which version you are getting? In that case, I agree, I don't know how you can test that right now.
You can test that using a macro. I'm a bit rusty with my Rust, but something along these lines...
[#cfg(test)] static mut SPECIALIZATION_TRIGGERED : bool = false; [#cfg(test)] macro_rules! specialization_trigger { () => { SPECIALIZATION_TRIGGERED = true; }; } [#cfg(not(test))] macro_rules! specialization_trigger { () => {}; }Then use
specialization_trigger!()in the specialized impl, and in tests useassert!(SPECIALIZATION_TRIGGERED);Phlosioneer at 2020-01-27 07:30:46
[#cfg(test)] static mut SPECIALIZATION_TRIGGERED : bool = false; ...You will want to use
thread_local! { static VAR: Cell<bool> = Cell::new(false); }instead ofstatic mutbecause otherwise the variable could get set in one test-case thread and mistakenly read from another thread. Also, remember to reset the variable at the beginning of each test, otherwise you will get thetruefrom the previous test.Jacob Lifshay at 2020-01-27 08:47:10
I have a question regarding the RFC text, hopefully this is a good place to ask.
In the reuse section, this example is given:
trait Add<Rhs=Self> { type Output; fn add(self, rhs: Rhs) -> Self::Output; fn add_assign(&mut self, rhs: Rhs); } // the `default` qualifier here means (1) not all items are implied // and (2) those that are can be further specialized default impl<T: Clone, Rhs> Add<Rhs> for T { fn add_assign(&mut self, rhs: Rhs) { let tmp = self.clone() + rhs; *self = tmp; } }I wonder how this is supposed to type-check, given that
tmphas typeSelf::Outputand nothing is known about this associated type. The RFC text does not seem to explain that, at least not anywhere near where the example is given.Is there some mechanism here that is/was supposed to make that work?
Ralf Jung at 2020-04-19 11:54:23
Could that default be constrained
where T: Add<Output = T>? Or is that a causality loop?Josh Stone at 2020-04-19 15:18:56
@RalfJung I agree that seems wrong.
Niko Matsakis at 2020-04-20 22:31:00
I have a question about procedure: how meaningful is this issue and how meaningful is it for people to try out this feature? As I understand it the current implementation is unsound and incomplete and will likely be completely replaced by chalk or something else. If that's true, should we just de-implement this feature and others (e.g. GATs) until they can be properly redone?
mark at 2020-04-20 23:06:20
Please don't de-implement. Broken, unsound and incomplete still allows experimentation.
Avi Dessauer at 2020-04-20 23:13:10
If that's true, should we just de-implement this feature and others (e.g. GATs) until they can be properly redone?
Please don't, PyO3 (Python bindings library) currently depends on specialization. See https://github.com/PyO3/pyo3/issues/210
Jacob Lifshay at 2020-04-20 23:15:05
Doesn't a fair amount of the
stddepend on it as well? I though I remembered seeing a lot of specialized internal implementations for vector & string related stuff. Not that that should prevent de-implementing, just that it wouldn't be as simple as removing the relevant sections from the type checker.Nathan West at 2020-04-20 23:36:38
@Lucretiel yes, many useful optimizations (especially around iterators) depend on specialization, so it would be a huge perf regression to de implement it.
For example,
FusedIteratorandTrustedLenare useless without specialization.RustyYato at 2020-04-21 04:00:49
PyO3 (Python bindings library) currently depends on specialization
That's scary, because of the "unsound" parts. The standard library had critical soundness bugs due to using specialization wrong. How sure are you that you do not have the same bugs? Try using
min_specializationinstead, it is hopefully at least less unsound.Maybe
specializationshould get a warning similar toconst_genericssaying "this feature is incomplete, unsound and broken, do not use in production".many useful optimizations (especially around iterators) depend on specialization, so it would be a huge perf regression to de implement it.
These days they depend on
min_specialization(see e.g. https://github.com/rust-lang/rust/pull/71321), which has the biggest soundness holes plugged.Ralf Jung at 2020-04-21 07:47:12
@nikomatsakis
I agree that seems wrong.
Any idea what the intended code is? I first thought the
default implwas intended to also settype Output = Self;, but that is actually impossible in the proposed RFC. So maybe the intention was to have anOutput = Tbound?Ralf Jung at 2020-04-21 07:52:57
@RalfJung Any chance
min_specializationcould get documented? I feel it's more risky to use a completely undocumented feature on a crate than one that has known (and possibly unknown) soundness bugs. Neither is good, but at least the latter one isn't just compiler internals.I couldn't find any mention of
min_specializationfrom this tracking issue outside of the #71321 PR - and according to the Unstable book this is the tracking issue for that feature.Mikko Rantanen at 2020-04-21 12:55:02
I don't know much about that feature either, I just saw the libstd soundness fixes. It got introduced in https://github.com/rust-lang/rust/pull/68970 which explains a few more things about it.
@matthewjasper would it make sense to document this a bit more and ask nightly users of
feature(specialization)to migrate?Ralf Jung at 2020-04-21 13:06:51
It seems like there should at least be a warning. It seems like this feature is blatantly broken and dangerous to use in its current state.
mark at 2020-04-21 18:10:11
I'd think
specializationcould become a synonym formin_specialization, but add anotherunsound_specializationfeature if necessary for existing projects, like PyO3 or whatever. It'd save anyone who only usesmin_specializationconsiderable effort, but anyone else gets the error message, and can look up here the new name.Jeff Burdges at 2020-04-21 20:34:24
@RalfJung
Any idea what the intended code is?
Well, at some point, we had been considering a mode where defaults could rely on one another. So I imagine at that point the following would've worked:
default impl<T: Clone, Rhs> Add<Rhs> for T { type Output = T; fn add_assign(&mut self, rhs: Rhs) { let tmp = self.clone() + rhs; *self = tmp; } }The caveat was that if you override any member of the
impl, then you had to override them all. We later backed off from this idea, and then kicked around various iterations, such as "default groups" (which would also work here), and ultimately didn't adopt any solution because we figured we could get to it later once we deal with the other, er, pressing problems (cc #71420).Niko Matsakis at 2020-04-22 14:53:53
Please don't, PyO3 (Python bindings library) currently depends on specialization. See PyO3/pyo3#210
PyO3 maintainer here - we're in favour of moving away from specialization so that we can get onto stable Rust. Will
min_specializationbe likely to be stabilized before the rest of specialization is complete?David Hewitt at 2020-04-27 07:13:02
I think there was some discussion of trying to stabilize min_specialization in the 2021 edition planning lang design meeting (it's on youtube; sorry, I'm on my phone, or i would try to find a link). I forgot what they said about it though
mark at 2020-04-28 05:19:33
I think there was some discussion of trying to stabilize min_specialization in the 2021 edition planning lang design meeting (it's on youtube; sorry, I'm on my phone, or i would try to find a link). I forgot what they said about it though
I think this is the correct YouTube link: https://youtu.be/uDbs_1LXqus (also on my phone)
Jacob Lifshay at 2020-04-28 06:24:52
Yep, that's it. Here is a link to the specific discussion: https://youtu.be/uDbs_1LXqus?t=2073
mark at 2020-04-28 21:03:03
I've been using
#[min_specialization]in an experimental library I've been developing so I thought I'd share my experiences. The goal is to use specialization in it's simplest form: to have some narrow cases with faster implementations than the general case. In particular, to have cryptographic algorithms in the general case run in constant time but then if all the inputs are markedPublicto have a specialized version that runs in faster variable time (because if they are public we don't care about leaking info about them via execution time). Additionally some algorithms are faster depending on whether the elliptic curve point is normalized or not. To get this to work we start with#![feature(rustc_attrs, min_specialization)]Then if you need to make a specialization predicate trait as explained in maximally minimal specialization you mark the trait declaration with
#[rustc_specialization_trait].All my specialization is done in this file and here's an example of a specialization predicate trait.
The feature works and does exactly what I need. This is obviously using rustc internal marker and is therefore prone to break without warning.
The only negative bit of feedback is that I don't feel the
defaultkeyword makes sense. Essentially whatdefaultmeans right now is: "this impl is specializable so interpret impls that cover a subset of this one this one as specialization of it rather than a conflicting impl". The problem is it leads to very weird looking code:https://github.com/LLFourn/secp256kfun/blob/6766b60c02c99ca24f816801fe876fed79643c3a/secp256kfun/src/op.rs#L196-L206
Here the second impl is specializing the first but it is also
default. The meaning ofdefaultseems to be lost. If you look at the rest of the impls it is is quite hard to figure out which impls are specializing which. Furthermore, when I made a erroneous impl that overlapped with an existing one it was often hard to figure out where I went wrong.It seems to me this would be simpler if everything was specializable and when you specialize something you declare precisely which impl you are specializing. Transforming the example in the RFC into what I had in mind:
impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A> { // no need for default fn extend(&mut self, iterable: T) { ... } } // We declare explicitly which impl we are specializing repeating all type bounds etc specialize impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A> // And then we declare explicitly how we are making this impl narrower with ‘when’. // i.e. This impl is like the first except replace all occurances of ‘T’ with ‘&'a [A]’ when<'a> T = &'a [A] { fn extend(&mut self, iterable: &'a [A]) { ... } }Lloyd Fournier at 2020-06-06 04:16:36
Thanks for the feedback.
Niko Matsakis at 2020-06-06 10:24:33
My comment here, specifically item 6, provides a concrete case in the standard library where it may be desirable to have a specialization that is only partly overridable:
IndexSetwould need a distinctOutputtype becauseIndexSetcould be implemented withoutIndex, but we probably do not want to allow the two types to coexist with differentOutputtypes. SinceIndexSetcould have a default implementation in terms ofIndexMut, it would be reasonable to allow specialization of theindex_setmethod without allowing specialization ofOutput.Alexis Hunt at 2020-06-24 22:39:30
I have a difficult time with videos, so I can't look up the linked video, however, I do have one question about
#[min_specialization]. As-is, there is anrustc_unsafe_specialization_markerattribute on traits likeFusedIteratorthat provide optimization hints, so that they can be specialized on. @matthewjasper wrote:This is unsound but we allow it in the short term because it can't cause use after frees with purely safe code in the same way as specializing on traits methods can.
I assume that the plan is to implement @aturon's proposal and add a specialization modality for traits such as these (
where specialize(T: FusedIterator)). But currently, it appears that any code can specialize on these traits. If it's stabilized as-is, people could write stable specializations that depend on it, meaning that this unsoundness would be stabilized.Should specialization on these traits also be limited to the standard library, then? Does the standard library derive enough benefit from being able to specialize on them?
Alexis Hunt at 2020-06-24 23:28:23
If it's stabilized as-is, people could write stable specializations that depend on it, meaning that this unsoundness would be stabilized.
It is my understanding that
min_specializationas-is is not intended for stabilization.Ralf Jung at 2020-06-25 06:38:07
I would like to second having some sort of marker on specializing impls. There have been quite a few cases of code in rustc and the standard library not doing what it looks like because there's no way to know that specialization is actually happening:
An unnecessary specialization of
Copy: https://github.com/rust-lang/rust/pull/72707/files#diff-3afa644e1d09503658d661130df65f59L1955A "Specialization" that isn't: https://github.com/rust-lang/rust/pull/71321/files#diff-da456bd3af6d94a9693e625ff7303113L1589
An implementation generated by a macro unless a flag is passed overriding a default impl: https://github.com/rust-lang/rust/pull/73851/files?file-filters%5B%5D=#diff-ebb36dd2ac01b28a3fff54a1382527ddR124
matthewjasper at 2020-06-28 22:19:37
@matthewjasper the last link doesn't appear to link to any specific snippet.
Ellen Emilia Anna Zscheile at 2020-06-28 22:35:43
I'm not sure if this is an explicit goal, but AIUI the fact that specializing impls aren't marked gives you a way to avoid breaking changes on blanket impls. A new
default impl<T> Trait for Tdoesn't conflict with downstream impls -- those just become specializing.Josh Stone at 2020-06-28 22:45:40
Could it be a warning only to have it unmarked?
Lokathor at 2020-06-28 23:12:26
There have been quite a few cases of code in rustc and the standard library not doing what it looks like because there's no way to know that specialization is actually happening
My experience with java is similar (though not exactly analogous). It can be hard to find out which subclass of a class is actually running...
mark at 2020-06-28 23:37:23
We'd want some marker on specializable impls too though, also for clarity when reading, right?
We could put the markers in both places, which then improves rustc error or warning messages because they now know if specialization is desired and can point to the other place if it exists.
If an upstream crate adds an impl then, aside from simply upgrading, a downstream crate could employ tricks that permit compiling against both the new and old version, not sure that's beneficial.
Jeff Burdges at 2020-06-29 08:46:56
I think that the diff may be too large to show the change. It's pointing to this: https://github.com/rust-lang/rust/blob/fb818d4321dee29e1938c002c1ff79b0e7eaadff/src/librustc_span/def_id.rs#L124
Re: Blanket impls, they are breaking changes anyway:
- They might partially overlap a downstream impl, which is not allowed
- Coherence can assume their non-existence in more subtle ways (which is why reserveration impls were added internally)
- Specializing impls have to be always applicable, which either means:
- We break peoples impls (what
min_specializationdoes). - We require them to somehow annotate their trait bounds as being always applicable where necessary.
- We make the always applicable change for them implicitly and potentially introduce subtle runtime bugs when the default impl now applies.
- We break peoples impls (what
matthewjasper at 2020-06-29 11:46:31
@cuviper actually, I feel like there were still edge cases around adding new blanket impls, even with specialization. I remember I was trying to figure out what it would take to permit us to add a
impl<T: Copy> Clone for T { }impI wrote this blog post about it, in any case... but I can't remember now what my conclusion was.Regardless, we could make it a lint warning to not have an
#[override]annotation.That said, if we could have the user declare which impls they are specializing (no idea how we would do that), it would simplify some things. Right now the compiler has to deduce the relationships between impls and that's always a bit tricky.
One of the pending items we have to do in the chalk project is to try and go back and spell out how specialization should be expressed there.
Niko Matsakis at 2020-06-29 17:10:30
There have been quite a few cases of code in rustc and the standard library not doing what it looks like because there's no way to know that specialization is actually happening
My experience with java is similar (though not exactly analogous). It can be hard to find out which subclass of a class is actually running...
Back in May, I proposed an alternative to specialization on IRLO that doesn't actually rely on overlapping impls, but rather allows a single impl to
where matchon its type parameter:impl<R, T> AddAssign<R> for T { fn add_assign(&mut self, rhs: R) where match T { T: AddAssignSpec<R> => self.add_assign(rhs), T: Add<R> + Copy => *self = *self + rhs, T: Add<R> + Clone => { let tmp = self.clone() + rhs; *self = tmp; } } }Crates downstream can then use such
implto implement "specialization", because by convention such impl for traitTraitwould first match on types that implement another traitTraitSpec, and downstream types would be able to implement that trait to override the generic behavior:// Crate upstream pub trait Foo { fn foo(); } pub trait FooSpec { fn foo(); } impl<T> Foo for T { fn foo() where T { T : FooSpec => T::foo(), _ => { println!("generic implementation") } } } fn foo<T : Foo>(t: T) { T::foo() } // crate downstream struct A {} struct B {} impl upstream::FooSpec for A { fn foo() { println!("Specialized"); } } fn main() { upstream::foo(A); // prints "specialized" upstream::foo(B); // prints "generic" }This formulation gives more control to upstream to choose the order of the applicable impls, and as this is part of the trait/function signature, this would appear in documentation. IMO this prevent "impl chasing" to know which branch is actually applicable, as the resolution order is explicit.
This could maybe make the errors around lifetimes and type equality more apparent as only upstream could meet them while implementing the specialization (since downstream is only implementing a "specialization trait".
Drawbacks to this formulation are that it is a very different route than the one in the RFC, and being implemented since 2016, and that at least some people on the thread expressed concerns that it would not be as expressive and/or intuitive as the current specialization feature (I find "matching on types" pretty intuitive, but I'm biased as I propose the formulation).
Louis Dureuil at 2020-07-03 11:55:02
The match syntax might have another (syntactical) benefit: If it were at some point in the future extended with const-evaluated match guards then one wouldn't need to do type gymnastics to express bounds conditional on const expressions. E.g. one could apply specializations based on
size_of,align_of,needs_dropor array sizes.the8472 at 2020-07-03 13:39:06
@dureuill thanks for the info! That is indeed an interesting idea. One concern I have is that it doesn't necessarily solve some of the other anticipated use cases for specialization, especially the "incrementally refining behavior" case as described by @aturon in this blog post. Still, worth keeping in mind.
Niko Matsakis at 2020-07-08 22:40:50
@dureuill The idea is indeed interesting and may have a lot of potential, but alternative isn't always equivalent exchange. The reason I don't think it is, is that one isn't given the opportunity to fully replace the more general implementation. Also another problem might be the fact that we don't actually have support for all the features present in the
wheresyntax RFC on which your suggestion depends. The suggestion is intriguing, so maybe it could have it's own RFC as a separate feature rather than an competitor to specialization because both would be useful and I see no reason why they can't live together.Deleted user at 2020-07-09 13:59:58
@the8472 @nikomatsakis, @Dark-Legion : Thank you for the positive feedback! I try answering some of your remarks in the IRLO thread, since I don't want to be too noisy on the tracking issue (I'm sorry for each of you who expected news on specialization and just found my ramblings :flushed:).
I may open a separate RFC if I manage to write something publishable. Meanwhile, I'm very open to feedback on the linked IRLO thread. I added a longer example from aturon's blog post, so feel free to comment on that!
Louis Dureuil at 2020-07-12 09:26:40
I'm also in favour of having some sort of marker on specializing impls.
The 2021 Edition approaches, which allows us to reserve further keywords (like
specialize). Looking at the complexity and history of this feature, I don't think it will stabilize before the release of the 2021 Edition (feel free to prove me wrong) which means - in my opinion - playing around with (a) new keyword(s) is reasonable.~~Otherwise, the only existing keyword that seems ... well.. suitable as marker, might be
super?~~Summary by reusing the example of @LLFourn from https://github.com/rust-lang/rust/issues/31844#issuecomment-639977601:
super(already reserved, but it could also be misinterpreted as alternative todefault)
super impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>specialize
specialize impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>spec(short forspecializelikeimplis forimplement) (valid concern raised by @ssokolow in https://github.com/rust-lang/rust/issues/31844#issuecomment-690980762)
spec impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>override(already reserved, thanks @the8472 https://github.com/rust-lang/rust/issues/31844#issuecomment-691042082)
override impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>already reserved keywords are to be found here
Michael Watzko at 2020-09-11 08:14:36
or
spec(short forspecializelikeimplis forimplement)"spec" is already more familiar to people as a shorthand for "specification" (eg. "The HTML 5 spec") so I don't think it'd be a good shorthand for "specialize".
Stephan Sokolow at 2020-09-11 09:25:16
overrideis a reserved keyword, I assume it was intended for functions, so it might be usable for an impl block.the8472 at 2020-09-11 11:36:08
specialize Is also locale dependent - as an Australian it's specialise for me, so using 'spec' removes the locale ambiguity.
Firstyear at 2020-11-15 10:56:55
specialize Is also locale dependent - as an Australian it's specialise for me, so using 'spec' removes the locale ambiguity.
that works, except for that 'spec' is a common abbreviation for specification, so I think using 'spec' to mean specialization would be confusing. Even if the words are spelled differently in Australia, everyone can still understand which word is intended if it's spelled with either a 'z' or an 's'.
Jacob Lifshay at 2020-11-15 18:57:18
As a Canadian, I have to say that specialize/specialise isn't the only word used in programming that varies with locale.
Here, we use "colour", but it always trips me up on the rare occasions when a programming language or library uses that instead of "color". For better or worse, American English is a bit of a de facto standard in API design and, with words like colour/color, choosing to favour one spelling over the other is unavoidable without getting really contrived.
Given how strongly I expect "spec" to mean "specification", I think this is another situation where we should just consider the American English spelling the least worst option.
Stephan Sokolow at 2020-11-15 19:36:42
It may be the defacto, but that doesn't mean it's okay to use them. I find myself doing imports like "use color as colour" for example. I always trip up on the s vs z as well. I think given Rust's positive attitude to inclusivity and accessibility, it makes sense to chose language terms that are not locale dependent, as small user frustrations like color/colour and s/z do build up.
Firstyear at 2020-11-15 23:22:07
I agree in principle. I'm just skeptical that, for this case, there's a locale-neutral choice which doesn't cause more problems than it solves.
Stephan Sokolow at 2020-11-15 23:31:22
As a non-English native speaker, I find it somewhat amusing that native English speakers would complain about extra
uas being a barrier to inclusivity. Just imagine what it'd be like if everything was not spelled a bit weird, but written in an entirely different language.Put differently: every term used in Rust is locale dependent.
For better or worse, things in Rust are spelled in US English. For many here this means working in their second or third language; for others it means having to adjust spelling a bit. This is what it takes to make a whole bunch of people work together. I think the benefit of of trying to pick words that are spelled the same across many variants of English is marginal compared to picking a good an unambiguous term -- and
specis ambiguous as pointed out above.Ralf Jung at 2020-11-15 23:45:36
use
specialas the keyword?Jacob Lifshay at 2020-11-15 23:46:09
Alternative, make two keywords:
specializeandspecialiseand make them equivalent...(Or you funny non-Americans can learn to spell real proper :us: 😂 )
mark at 2020-11-16 04:00:43
I can't speak was to what most languages do, but CSS uses American English spellings for everything new. Anecdotally American English seems to be used more frequently in programming as well.
Jacob Pratt at 2020-11-16 04:04:54
@mark-i-m Unfortunately, that's a slippery slope leading to arguments that Rust should have alternative sets of keywords in every major language that learners might be coming from.
It also needlessly complicates the language to have multiple synonyms for keywords since people and parsers are only used to the idea that whitespace can vary like that.
(Not to mention that it would potentially touch off a push for equivalent synonyms in libraries, which would then necessitate rustdoc design and implementation work to keep them from being a net negative.)
Stephan Sokolow at 2020-11-16 08:46:09
Rather than arguing about which dialect of English we want this identifier to be in, perhaps we can compromise and put it in Hebrew instead?
Alexis Hunt at 2020-11-16 09:11:13
@ssokolow while slippery slope argument in general is not a strong argument to use, I do agree with you in this case. One could argue that multiple languages is fine but there are at least two reasons why it's not:
- Some words in different languages look the same but mean different things (can't come up with programming-related example right now, but a random example:
ain Slovak isandin English) - People will have a huge trouble reading code in another language even if they know the language. I know from experience. (Long story short: I had a huge trouble understanding some texts with terms directly translated from English to my "mother language" at ~~university~~ education scam.)
Now working backwards, why different dialects of English should be preferred if not other languages? I don't see a point. Consistency (everything is US English) seems simplest, easiest to understand and least error-prone.
All this being said, I'd be very happy with "did you mean XXX?" error message approach. Neutral words that don't have other problems are fine as well.
Martin Habovštiak at 2020-11-16 09:15:10
- Some words in different languages look the same but mean different things (can't come up with programming-related example right now, but a random example:
At least nobody needs to discuss football in code. ;)
Around 70% of native English speakers live in countries using US spelling.
Also..
"The -ize spelling is often incorrectly seen as an Americanism in Britain. It has been in use since the 15th century, predating -ise by over a century. -ize comes directly from Greek -ιζειν -izein and Latin -izāre, while -ise comes via French -iser. The Oxford English Dictionary (OED) recommends -ize and lists the -ise form as an alternative."
"Publications by Oxford University Press (OUP)—such as Henry Watson Fowler's A Dictionary of Modern English Usage, Hart's Rules, and The Oxford Guide to English Usage—also recommend -ize. However, Robert Allan's Pocket Fowler's Modern English Usage considers either spelling to be acceptable anywhere but the US."
ref. https://en.wikipedia.org/wiki/American_and_British_English_spelling_differences#-ise,-ize(-isation,_-ization)
It appears Spanish and Italian have a z or two, so not sure where the French gets -iser, maybe from German?
Jeff Burdges at 2020-11-16 10:45:59
Can further bikeshedding around specific keywords and naming be moved to an internals thread? I'm following this issue for progress updates on the feature, and this discussion is starting to get a bit noisy.
John-John Tedro at 2020-11-16 10:51:57
Currently specialization interacts strangely with deref coercion. It appears that a type will be automatically dereferenced as many times a needed, but never for specialization.
To highlight why this is the problem consider a trait
Foowhich is implemented for several types including[u8]. To add a fast path for some method that takes aFoofor the case where it happens to be a[u8]is simple enough. However if the method is invoked with aStringthat can auto deref into a&strwhich can deref into a&[u8]and so the method can be invoked. However it will not get the specialized impl, and will instead invoke the non-specialized method using the slow path, even though the fast path exists for[u8].This is confusing because the type of the impl being passed to the generic implementation for
Foois of a type for which specialization exists. It's also annoying in practical terms because it means that if one wants to create a fast path for[u8]one also needs to write duplicate code forVec<u8>,Box<[u8]>,&[u8],&&[u8],&Vec<u8>,&Box<[u8]>,str,&str,String,&String, etc. etc. Which is a lot of extra code, and there is no way to generically declare specialization for "anything derefable into [u8]" because the signature could overlap with other implementations and then the call would be ambiguous.I think for specialization to be ergonomic, it should first attempt to deref into a specialized implementation, and only if none exist attempt to deref into the generic implementation. This ensures the specialized path is always run if it is possible.
Tom Kaitchuck at 2020-11-22 22:10:48
One minor technicality:
strdoesn't deref to[u8], yes&strtrivially converted to&[u8], but noDerefimpl. That said, trying deref exhaustively would make it nicer in some cases, but you still need those extra impls in the generic case where specialization is really useful. One more point of consideration is that exhaustive defer coercions will noticeably negatively impact compiler performance, which may not be desirable, especially when the fix is relatively straightforward (especially using macros).RustyYato at 2020-11-23 03:15:06
It is already true today on stable that you can accidentally use a slow (unspecialised) trait impl because of extra refences. There is a clippy link for it: https://rust-lang.github.io/rust-clippy/master/#inefficient_to_string
Harry Sarson at 2020-11-23 09:23:18
strdoesn'tDerefto[u8]but doesAsRef, wonder if that should play a part at all?Diana at 2020-11-25 00:01:05
AsRef isn't something the compiler is aware of, so it shouldn't be considered.
Two thoughts: first, making the suggested changes to deref in method resolution would quite possibly interact poorly with the existing corner cases around lookup, and that needs to be carefully thought through. Any proposal for a change specific to specializarion should definitely not be in this thread; possibly even a separate RFC because of the complexity of the existing resolution system.
Second, that separate conversation should address if the unintuitive behaviour can be addressed in libraries. If it can be done with library code, there should be an explanation of how, and why changes to the language are still needed (e.g. ergonomics). If it can't, that sound be explained.
On Tue., Nov. 24, 2020, 19:01 Diana, notifications@github.com wrote:
str doesn't Deref to [u8] but does AsRef https://doc.rust-lang.org/stable/std/primitive.str.html#impl-AsRef%3C%5Bu8%5D%3E, wonder if that should play a part at all?
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/31844#issuecomment-733322839, or unsubscribe https://github.com/notifications/unsubscribe-auth/AE7AOVNZ6TEG432UCDS4CDLSRRCN7ANCNFSM4B4DLZZQ .
Alexis Hunt at 2020-11-25 07:52:10
AsRef isn't something the compiler is aware of, so it shouldn't be considered.
Can one just define the specialization for, say,
AsRef<[u8]>instead of[u8]? I am not sure it won't run afoul of the overlap rules, but that would solve the ergonomics side of things.Jan Hudec at 2020-12-03 13:21:37
Pardon my ignorance, but it seems rather difficult to access the current status of this feature at a glance (I'd rather not read through 300+ comments). Does this have any chance of landing sooner rather than later? The OP makes it look like it might not land for a while.
Robert Quattlebaum at 2021-01-26 02:55:40
As I understand it, this feature is unlikely to be stabilized in its current form. There is some significant design work needed to resolve soundness bugs in the interaction between lifetimes and specialization. There are some blog posts by nikomatsakis and aturon from a couple years ago about a possible design, but I'm not aware of significant progress since then.
mark at 2021-01-26 03:00:29
Will this feature ever be Stabilised?
Jakob Neufeld at 2021-02-25 17:27:18
#![feature(specialization)]will not be stabilized as it is unsound.#![feature(min_specialization)]may be stabilized in the future, or a better way to avoid the unsoundness of#![feature(specialization)]may be found before that.bjorn3 at 2021-02-25 17:42:05
#![feature(specialization)] will not be stabilized as it is unsound.
It will not be stabilized as-is -- but as you say, the plan is to eventually stabilize some version with the soundness holes fixed (and remove the unsound feature entirely).
Ralf Jung at 2021-02-25 18:06:28
It would be great if we had #![feature(min_specialization)] for inclusion in Rust 2021. The bigger the rust project the more this feature is typically needed. Is there a working group focused on delivering specialisation? / Is there anything we can do to help?
Squirrel at 2021-04-09 09:49:19
for inclusion in Rust 2021
Can you expand on what part of specialization requires an edition bump? This nightly code works fine in edition 2015:
#![feature(min_specialization)] trait Thing { fn thing(&self); } impl<T> Thing for T where T: std::fmt::Display, { default fn thing(&self) { println!("default"); } } impl Thing for String { fn thing(&self) { println!("string"); } } fn main() { 42.thing(); "".thing(); String::new().thing(); }Jake Goulding at 2021-04-09 13:46:39
Nothing, just 2021 we roll up all the things that have happened to rust since 2018 and explain them all in one place. If we can have it earlier I wouldn't be complaining, but it's as good a target as any to aim for.
Squirrel at 2021-04-09 14:57:08
just 2021 we roll up all the things that have happened to rust since 2018 and explain them all in one place
That is not the plan:
Rather than representing a major marketing push, the 2021 Edition represents a chance for us to introduce changes that would otherwise be backwards incompatible.
Jake Goulding at 2021-04-09 15:33:42
@shepmaster you're splitting hairs. In effect, that is what happened with Rust 2018.
Per @gilescope's original question, there is a rough summary a few posts up. Regarding working groups I don't know.
Diggory Hardy at 2021-04-09 16:08:47
@dhardy I believe that the point was that yeah it happened in 2018 but it's not intended to happen this time?
Martin Habovštiak at 2021-04-09 16:50:27
Would it be possible/sensible to take the const generics approach here, and stabilize
min_specializationor an even stricter subset of it? If not, why not?daemoness at 2021-10-26 04:39:34
I guess the difference with const generics is that we don't have a complete picture of specialization that resolves the soundness issues. Not saying this is the reason that
min_specializationisn't stable yet, but I would feel uncomfortable going down a path of stabilization that might turn out to be a dead end.Louis Dureuil at 2021-10-26 05:35:38
Foois not a type, but a trait. You used to be able to writedyn FooasFoo(with both being equivalent), but this has been deprecated and in the 2021 edition removed.bjorn3 at 2021-12-13 12:02:41
@bjorn3 sorry I made a foolish mistake, but I tried this:
trait Bar { fn bar(&self); } trait Foo {} impl<T> Bar for T { fn bar(&self) {} } impl Bar for dyn Foo { fn bar(&self) {} }And it compiles (with stable), doesn't generic
Tcover typedyn Foo, does this count a specialization?psionic12 at 2021-12-13 12:51:40
@psionic12 no, because these
impls do not overlap, actually.When you're writing
impl<T> Bar for T, you have an implicitT: Sizedbound. Anddyn Foodoesn't implementSized.If you write instead:
impl<T: ?Sized> Bar for T { fn bar(&self) {} } impl Bar for dyn Foo { fn bar(&self) {} }they will overlap, and you'll receive a compile error as expected.
Specialization solves the problem of overlapping impls. If your impls do not overlap, you're lucky and you don't even need specialization.
Kai Ren at 2021-12-13 13:15:37
Hello :wave:, I noticed something weird when trying to specialize over a generic trait parameter and wanted to ping you guys here. Maybe you can help me understand what is going on.
Jan Kleinert at 2021-12-15 17:00:42
@joergbrech definitely a bug; can't see any existing issue that covers it. Would you like to open one (or I can)?
eggyal at 2021-12-15 17:45:20
definitely a bug; can't see any existing issue that covers it. Would you like to open one (or I can)?
@eggyal, thanks for the confirmation, that's what I suspected. I opened the issue.
Jan Kleinert at 2021-12-15 18:03:39
In a follow-up of @dtolnay's comment https://github.com/rust-lang/rust/issues/31844#issuecomment-263175793 I have a use case for something similar to the example using associated types given in the RFC. In addition to specializing over a blanket implementation, I want to be able to specialize over a generic trait parameter:
#![feature(specialization)] /////////////////////////////////////////////////////////////////////////////// // traits provided by libary // /////////////////////////////////////////////////////////////////////////////// // a marker trait trait MyTrait {} trait Example<T> { type Output; fn generate(self, _: T) -> Self::Output; } // default implementation of Example<T> for anything that implements MyTrait default impl<T, U: MyTrait> Example<T> for U { type Output = Box<T>; fn generate(self, t: T) -> Box<T> { Box::new(t) } } /////////////////////////////////////////////////////////////////////////////// // user of lib can specialize Example for custom types // /////////////////////////////////////////////////////////////////////////////// struct Bar; impl MyTrait for Bar {} impl Example<bool> for Bar { type Output = u8; fn generate(self, b: bool) -> Self::Output { match b { true => 1, _ => 0 } } } fn main() { // specialized: 1 println!("{:?}", Bar.generate(true)); // default: box("s") println!("{:?}", Bar.generate("s")); // default: box(42) println!("{:?}", Bar.generate(42)); }This code block will not compile.
If anybody is interested, I managed to adapt the workaround from https://github.com/rust-lang/rust/issues/31844#issuecomment-263175793 for this use case.
<details> <summary>Click to expand workaround</summary>
</details>#![feature(specialization)] use std::fmt::{self, Debug}; /////////////////////////////////////////////////////////////////////////////// trait MyTrait {} trait Example<T> : Pass<T> { fn generate(self, _: T) -> Self::Output; } /// In its own trait for reasons, presumably. trait Output<T: ?Sized> { type Output: Debug + Valid<T>; } ///////////////////////////////////////////////////////////////////// #[derive(Debug)] struct Bar; impl MyTrait for Bar {} // specialized implementation for Example<bool> impl Output<bool> for Bar { type Output = u8; } impl Example<bool> for Bar { fn generate(self, b: bool) -> Self::Output { match b { true => 1, _ => 0 } } } fn main() { // specialized: 1 println!("{:?}", Bar.generate(true)); // default: box("s") println!("{:?}", Bar.generate("s")); // default: box(42) println!("{:?}", Bar.generate(42)); } /////////////////////////////////////////////////////////////////////////////// // Need an extra implementation until https://github.com/rust-lang/rust/issues/91973 // gets fixed #[derive(Debug)] struct Dummy; impl Output<Dummy> for Bar { type Output = Dummy; } impl Example<Dummy> for Bar { fn generate(self, _: Dummy) -> Self::Output { Dummy{} } } /////////////////////////////////////////////////////////////////////////////// /// Instead of `Box<T>` just so the "{:?}" in main() clearly shows the type. struct MyBox<T: ?Sized>(Box<T>); impl<T: ?Sized> Debug for MyBox<T> where T: Debug { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "box({:?})", self.0) } } /////////////////////////////////////////////////////////////////////////////// /// Return type of the impl containing `default fn`. type DefaultOutput<T> = MyBox<T>; impl<T: ?Sized + Debug, U: MyTrait> Output<T> for U { default type Output = DefaultOutput<T>; } // default implementation for Example<T> impl<T, U: MyTrait + Pass<T>> Example<T> for U { default fn generate(self, t: T) -> Self::Output { Self::pass( // This is the impl you wish you could write MyBox(Box::new(t)) ) } } trait Valid<T: ?Sized> { fn valid(_: DefaultOutput<T>) -> Self; } impl<T: ?Sized> Valid<T> for DefaultOutput<T> { fn valid(ret: DefaultOutput<T>) -> Self { ret } } impl<T: ?Sized, U> Valid<T> for U { default fn valid(_: DefaultOutput<T>) -> Self { unreachable!() } } trait Pass<T: ?Sized>: Output<T> + Debug { fn pass(_: DefaultOutput<T>) -> <Self as Output<T>>::Output; } impl<T, U> Pass<T> for U where T: ? Sized + Debug, <U as Output<T>>::Output: Valid<T>, U: MyTrait + Debug { fn pass(ret: DefaultOutput<T>) -> <U as Output<T>>::Output { <U as Output<T>>::Output::valid(ret) } }Jan Kleinert at 2021-12-16 09:45:58
Sorry for spamming (I am just so excited about the
specializationfeature :heart: 😃 ), but I have one suggestion if it is not too late, as https://github.com/rust-lang/rust/issues/37653 is well on its way.The RFC example
#![feature(specialization)] trait Example { type Output; fn generate(self) -> Self::Output; } impl<T> Example for T { default type Output = Box<T>; default fn generate(self) -> Box<T> { Box::new(self) } } impl Example for bool { type Output = bool; fn generate(self) -> bool { self } }does not compile, which kind of makes sense to me, because in theory, I could've provided only a partial specialized implementation
impl Example for bool { type Output = bool; }which is incompatible with the
default fn generate(self) -> Box<T>.Now I admit I haven't thought through all of the intricacies and pitfalls, but would it makes sense to define that a
default implrequires all items to be implemented in a specialized implementation, while animplwithdefaultitems allows for partial specializations?That way, if I wrote
default impl<T> Example for T { type Output = Box<T>; fn generate(self) -> Self::Output { Box::new(self) } }the partial specialization
impl Example for bool { type Output = bool; }should yield a compiler error. I have to specialize
generateas well, and the compiler should know that the implementation is "final" and that any item from the specialized final impl will use only the items from the same impl.Jan Kleinert at 2021-12-16 10:12:25
You should use
#![feature(min_specialization)]instead of#![feature(specialization)].#![feature(specialization)]is unsound (allows you to violate memory safety using safe code).#![feature(min_specialization)]is a restricted version that should be sound.bjorn3 at 2021-12-16 10:45:57
Sure, but I assumed that default associated types are not supported by
min_specializationyet, so my comment only applies to the currently unsoundspecializationfeature:#![feature(min_specialization)] trait Example { type Output; } impl<T> Example for T { default type Output = i32; }Errors:
error[E0658]: specialization is unstable --> src/lib.rs:8:5 | 8 | default type Output = i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information = help: add `#![feature(specialization)]` to the crate attributes to enable For more information about this error, try `rustc --explain E0658`. error: could not compile `playground` due to previous errorAm I wrong?
Jan Kleinert at 2021-12-16 10:50:43
Why do we need the
defaultkeyword? Wouldn't it suffice to define an ordering on the implementations (impl) meaning how specific they are? I.e.impl Trait for Typewill be prioritized even ifTypematches a more generalimpl<T: Bound> Trait for T.And for the case where there are two
impl<T: Bound1>andimpl<T: Bound2>andTypeimplements bothBound1andBound2we would throw a collision and the correct impl can still be implemented by an explicitimpl Trait for Typeorimpl<T: Bound1 + Bound2> Trait for T.I think this is the main purpose of specialization and I don't see why a new keyword would be needed for this. Seems like a different feature actually.
Linus Behrbohm at 2021-12-16 18:59:55
Why do we need the default keyword?
This section of the RFC is a motivation for the
defaultkeyword and the following section gives some more details: https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md#hazard-interactions-with-type-checkingJan Kleinert at 2021-12-16 21:31:39
As a question, will min_specialization be stabilized? I'm unsure of how this works. I have a use case that is well supported by it, but it's hard for me to justify the use of nightly for it. If the answer is affirmative, is there a timeline for stabilization?
rwthompsonii at 2022-01-07 01:27:24
min_specializationas is it is today still has a few safety holes due torustc_unsafe_specialization_markerand all traits marked with that. See #89948 and #88901 examples. AIUI those markers are temporary workarounds for the limitations ofmin_specialization, they're not part of the RFC, so they probably shouldn't get stabilized (as therustc_prefix also suggests).Perhaps an even smaller subset without those markers could be stabilized, but that's fairly restrictive, you can only specialize on specific types, not on traits.
the8472 at 2022-01-07 10:36:41
Yeah I have a use case where I'm trying to specialize on specific types lol.
rwthompsonii at 2022-01-07 17:19:42
IMHO stabilizing specialization for specific types would be a huge help to me as well! Currently there is no way to specialize on types (see also #77125). This requires
unsafe(but sound) workarounds for performance-sensitive code.Specializing on traits would, of course, be great and allow for some neat inheritance patterns, but it looks like there's still some difficult work to be done. Given that
min_specializationhas lived in nightly since 2018, unfortunately, I have little expectation it will be stabilized anytime soon.Benjamin Kay at 2022-01-07 17:53:45
I'm also in favor of a hypothetical
min_min_specializationfeature. Having any semblance of this feature at all seems like a win while waiting for the larger kinks to be worked out. The only risk I can see is if people think that the shape of a future specialization feature that actually works might be so radically different frommin_min_specializationas to make this foundation unusable; is there anyone who thinks that?bstrie at 2022-01-22 02:30:30
The syntax might need to be changed depending on the solution for overlapping impls.
the8472 at 2022-01-22 06:52:28
Are there any rough ideas on what those solutions might look like?
bstrie at 2022-01-22 18:24:35
I'd settle for literally anything at this point syntactically, including some kind of attribute that punts the question of first-class syntax.
Ashley Mannix at 2022-03-08 22:53:00
Please support specialization on inherent impls for
min_specialization. This seems like the most obvious use-case for specialization. For example, this pattern comes up all the time:struct S<R: Read>; impl<R: Read> S<R> { pub fn skip() { println!("read bytes to skip"); } } impl<R: Read + Seek> S<R> { pub fn skip() { println!("use seek to skip (faster)"); } }Not having this makes me very sad.
kalcutter at 2022-04-16 18:30:50
Six years since the creation of this thread.
Couldn't find any specific mention of
specializationin the 2024 roadmap, very few developers have the necessary knowledge to implement or fix the remaining issues and probably fewer have the bandwidth, motivation or free-time.It is not even clear whether it is possible to have a sound implementation so it seems that everything points to a future without
specialization. Such a pity.For those interested in moving things forward, here is a little list of related material:
- http://smallcultfollowing.com/babysteps/blog/2018/02/09/maximally-minimal-specialization-always-applicable-impls/ (Blog post)
- http://aturon.github.io/tech/2018/04/05/sound-specialization/ (Blog post)
- https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md (Safer subset)
- https://github.com/rust-lang/rust/pull/68970 (RFC)
- https://github.com/rust-lang/rust/pull/30652 (Initial implementation)
It is also worth asking the relevant participants in the Zulip channel (https://rust-lang.zulipchat.com) about potential directions.
Caio at 2022-04-16 19:13:42
Given how difficult and subtle it has proven to formulate a sound version of this feature, I suspect that no progress will be made until the types team is formed (https://github.com/rust-lang/rfcs/pull/3254) and they have established some sort of formal model against which a proposed specialization design can be verified.
bstrie at 2022-04-17 04:18:21
@bstrie
I suspect that no progress will be made until the types team is formed
It's there now. What are the next steps? I want to get involved!
Deleted user at 2022-06-03 09:10:45
@c410-f3r
so it seems that everything points to a future without
specialization.Why settle? It's certainly possible.
Deleted user at 2022-06-03 09:13:11
@c410-f3r
so it seems that everything points to a future without
specialization.Why settle? It's certainly possible.
Like previously said in https://github.com/rust-lang/rust/issues/31844#issuecomment-1100736437, it is possible to reach out the responsible individuals (https://rust-lang.github.io/rfcs/3254-types-team.html#leads-and-membership) in the Rust Zulip platform at https://rust-lang.zulipchat.com/ or maybe through email for further instructions. I personally don't know their schedule, but chances are they want some people to get involve (https://rust-lang.zulipchat.com/#narrow/stream/122651-general/topic/.22good.20first.20issue.22/near/284068702).
Good luck!
Caio at 2022-06-03 11:02:24
Please support specialization on inherent impls for
min_specialization. This seems like the most obvious use-case for specialization. For example, this pattern comes up all the time:struct S<R: Read>; impl<R: Read> S<R> { pub fn skip() { println!("read bytes to skip"); } } impl<R: Read + Seek> S<R> { pub fn skip() { println!("use seek to skip (faster)"); } }Not having this makes me very sad.
@kalcutter for such simple cases you can use https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html
Artur Sinila at 2022-06-18 20:44:14
Please support specialization on inherent impls for
min_specialization. This seems like the most obvious use-case for specialization. For example, this pattern comes up all the time:struct S<R: Read>; impl<R: Read> S<R> { pub fn skip() { println!("read bytes to skip"); } } impl<R: Read + Seek> S<R> { pub fn skip() { println!("use seek to skip (faster)"); } }Not having this makes me very sad.
@kalcutter for such simple cases you can use https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html
Linked article literally said it can't solve the issue in a generic context at foreword.
Ziru Niu at 2022-06-29 02:56:16
I found a strange compiler behavior, and I think it's a specialization bug.
#![feature(specialization)] pub struct True{} pub struct False{} pub trait Boolean {} impl Boolean for True{} impl Boolean for False{} pub trait Same_type< T > {} impl< T > Same_type< T > for T {} pub trait Is_pair { type Result: Boolean; } impl< First_g, Second_g > Is_pair for (First_g, Second_g) { type Result = True; } impl< T > Is_pair for T { default type Result = False; } fn implements_the_trait<U, T: Same_type< U >>() {} fn main() { type Test_true = <(usize, usize) as Is_pair>::Result; println!("{}", std::any::type_name::<Test_true>()); implements_the_trait::<Test_true, True>(); type Test_false = <usize as Is_pair>::Result; println!("{}", std::any::type_name::<Test_false>()); implements_the_trait::<Test_false, False>(); }error[[E0277]](https://doc.rust-lang.org/nightly/error-index.html#E0277): the trait bound `False: Same_type<<usize as Is_pair>::Result>` is not satisfied --> src/main.rs:36:38 | 36 | implements_the_trait::<Test_false, False>(); | ^^^^^ the trait `Same_type<<usize as Is_pair>::Result>` is not implemented for `False` | note: required by a bound in `implements_the_trait`Centimo at 2022-07-24 11:26:28
Any updates on this? @balanceglove2 have you been able to contact anyone?
Patrick Elsen at 2022-08-17 21:03:32
I think they are just waiting for pull requests at this point.
Linus Behrbohm at 2022-08-17 21:13:35
I have two versions of code, one that does compile and one that I think should compile but does not.
This works, with the flags
return_position_impl_trait_in_trait,specializationandnegative_impls.pub trait Equality { fn eq(&self); } struct EqualityModel(); pub trait HasEquality { fn equalityModel(&mut self) -> &mut impl Equality; } impl !HasEquality for EqualityModel {} impl Equality for EqualityModel { fn eq(&self) {} } impl <M: HasEquality> Equality for M { default fn eq(&self) { self.equalityModel().eq() } }However, this code, which I expected to be only trivially different does not:
pub trait Equality<V> { fn eq(&self); } struct EqualityModel<V>(); pub trait HasEquality<V> { fn equalityModel(&mut self) -> &mut impl Equality<V>; } impl <V> !HasEquality<V> for EqualityModel<V> {} impl <V> Equality<V> for EqualityModel<V> { fn eq(&self) {} } impl <V, M: HasEquality<V>> Equality<V> for M { default fn eq(&self) { self.equalityModel().eq() } }It fails with:
error[E0119]: conflicting implementations of trait `test::Equality<_>` for type `test::EqualityModel<_>` --> src/test.rs:17:1 | 13 | impl <V> Equality<V> for EqualityModel<V> { | ----------------------------------------- first implementation here ... 17 | impl <V, M: HasEquality<V>> Equality<V> for M { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `test::EqualityModel<_>`The only difference is threading the type parameter
<V>everywhere.Matthew Pocock at 2022-12-05 22:47:25
I was playing around with default associated types, and I am running into what seems to be a bug:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=71b0812850f3b60475152ad720c9d32c
#![feature(specialization)] pub trait AlternativeRepresentation { type Inner; } impl<T> AlternativeRepresentation for T { default type Inner = T; } pub struct AlternativeField<T: AlternativeRepresentation>(<T as AlternativeRepresentation>::Inner); impl AlternativeRepresentation for char { type Inner = u8; } struct Foo { a: AlternativeField<char>, b: AlternativeField<String>, } fn main() { let _foo = Foo { a: AlternativeField(1), b: AlternativeField(String::new()), }; }Output:
error[[E0271]](https://doc.rust-lang.org/nightly/error-index.html#E0271): type mismatch resolving `<String as AlternativeRepresentation>::Inner == String` --> src/main.rs:25:12 | 25 | b: AlternativeField(String::new()), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<String as AlternativeRepresentation>::Inner == String` | note: expected this to be `<String as AlternativeRepresentation>::Inner` --> src/main.rs:8:26 | 8 | default type Inner = T; | ^ = note: expected associated type `<String as AlternativeRepresentation>::Inner` found struct `String` = help: consider constraining the associated type `<String as AlternativeRepresentation>::Inner` to `String` or calling a method that returns `<String as AlternativeRepresentation>::Inner` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html For more information about this error, try `rustc --explain E0271`. warning: `playground` (bin "playground") generated 1 warning error: could not compile `playground` due to previous error; 1 warning emittedThe compiler gets confused:
type mismatch resolving `<String as AlternativeRepresentation>::Inner == String`I also tried to be the
defaulton theimplrather than the type itself, but then the compiler overflows trying to resolve the type.Paul Lesur at 2023-01-18 09:00:43
I have a question about this feature. Should the following code compile?
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=c430ff250a95a3656a8a835299806663
Anselm Schüler at 2023-02-16 18:39:10
default implis a default implementation. That is if this default implementation has a function implementation, then another implementation covered by this default implementation doesn't have to provide this function implementation too. A default implementation doesn't count as a full implementation though, which would be required for your code to compile. From the rfc:The specialization design in this RFC also allows for default impls, which can provide specialized defaults without actually providing a full trait implementation:
// the `default` qualifier here means (1) not all items are implied // and (2) those that are can be further specialized default impl<T: Clone, Rhs> Add<Rhs> for T { fn add_assign(&mut self, rhs: Rhs) { let tmp = self.clone() + rhs; *self = tmp; } }This default impl does not mean that Add is implemented for all Clone data, but just that when you do impl Add and Self: Clone, you can leave off add_assign:
#[derive(Copy, Clone)] struct Complex { // ... } impl Add<Complex> for Complex { type Output = Complex; fn add(self, rhs: Complex) { // ... } // no fn add_assign necessary }bjorn3 at 2023-02-16 19:28:16
I see, thank you!
Anselm Schüler at 2023-02-16 19:34:39
Is there a good reference somewhere with some of the justifications for the
min_specializationrules? I specifically found out thatSynccan't be specialised when I was trying to see if I could makeExclusive<T>actually show the inner value forDebugbut only when the inner type isSync:trait SpecDebug { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; } impl<T: ?Sized> SpecDebug for Exclusive<T> { default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Exclusive").finish_non_exhaustive() } } impl<T: ?Sized + Sync> SpecDebug for Exclusive<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Exclusive").field(&self.0).finish() } }I figure that specialising on auto traits like this could be unsound, but I don't actually know why, and it would be nice if there were a place I could read up on it.
For reference, the error just says you can't specialise on it, but not why:
error: cannot specialize on trait `Sync` --> library/core/src/sync/exclusive.rs:100:18 | 100 | impl<T: ?Sized + Sync> SpecDebug for Exclusive<T> { | ^^^^ error: could not compile `core` due to previous errorClar Fon at 2023-02-17 20:40:22
It is possible to implement
SyncforFoo<'static>but not any other lifetimes. If you then specialize based onSync, it is possible to get an implementation which applies forFoo<'static>and typechecks accordingly, but then during codegen we don't know what the lifetime was anymore and thus have to use the baseline implementation which might for example use a different associated type and thus allow transmuting from one type to another using safe code.bjorn3 at 2023-02-17 21:02:44
Ah! Honestly the fact that you can do that feels wrong, but we're stuck with it regardless.
That said… wouldn't it make sense to allow specialising on
Sync + 'static? Since the lifetimes are effectively removed in that case, I would assume it would work fine?EDIT: Wait… I'm facepalming right now. That's literally specialising on lifetimes. Ignore me.
Clar Fon at 2023-02-17 21:51:07
So, I'm wondering what exactly the status of
rustc_unsafe_specialization_markeris (cc #89948) and what exactly the solution is under themin_specializationworld. Because as it stands, optimisations likeFusedIteratorrely on it, and as far as I can see, there's really no good way to deal with this besides marking the specializations as unsafe and saying "I pinky promise to not make this unsound," which… seems to defeat most of the point ofmin_specializationimho. Like, the existing implementations are still "sound" in the sense that they don't cause UB, but they still aren't sound in the sense that the exact generated code depends on lifetimes and whether checks are added or not isn't 100% deterministic.Clar Fon at 2023-05-11 18:18:11
Note that one can use
min_specializationwith#[rustc_specialization_trait]instead of#[rustc_unsafe_specialization_marker]which doesn't have the lifetime hole.the8472 at 2023-05-11 18:34:03
The status is it exists to avoid having to remove the specializations on
FusedIteratorin the standard library until an actual design for sound specialization exists and is implemented (which is essentially the status of all ofmin_specialization).matthewjasper at 2023-05-11 20:45:16
Right, I get that no solution has actually been implemented, but… is there any path to what such a solution would be? Does a sound design even exist?
From my understanding, specialising on traits, even marker traits, is inherently unsound because you can implement them in ways that depend on lifetimes. And I don't see any path forward in the language that would make that work unless you had a separate version of traits which only applied to lifetime-erased types, which just seems like a super intrusive change.
Basically, I'm wondering what the status of any designs are right now, or if it's mostly just stuffed into the back of people's minds and the answer is to not worry about it.
Clar Fon at 2023-05-11 23:53:08
I just wrote this silly program to calculate the Fibonacci sequence at compile-time, and rustc crashes during compilation.
<details> <summary>Error message</summary>#![feature(specialization, generic_const_exprs)] #![allow(incomplete_features)] #![recursion_limit = "2048"] /* template<unsigned int n> const unsigned int fib = fib<n-1> + fib<n-2>; template<> const unsigned int fib<0> = 0u; template<> const unsigned int fib<1> = 1u; template<> const unsigned int fib<2> = 1u; int main() { const unsigned int N = 24u; return fib<N>; } */ pub struct Fibonacci<const N: usize>; impl<const N: usize> Fibonacci<N> where [(); N]: FibonacciValue<N>, { pub const VALUE: usize = <[(); N] as FibonacciValue<N>>::VALUE; } trait FibonacciValue<const N: usize> { const VALUE: usize; } impl<const N: usize> FibonacciValue<N> for [(); N] where [(); usize::saturating_sub(N, 1)]: FibonacciValue<{ usize::saturating_sub(N, 1) }>, [(); usize::saturating_sub(N, 2)]: FibonacciValue<{ usize::saturating_sub(N, 2) }>, { default const VALUE: usize = <Self as FibonacciValue<{ usize::saturating_sub(N, 1) }>>::VALUE + <Self as FibonacciValue<{ usize::saturating_sub(N, 2) }>>::VALUE; } impl FibonacciValue<0> for [(); 0] { const VALUE: usize = 0; } impl FibonacciValue<1> for [(); 1] { const VALUE: usize = 1; } impl FibonacciValue<2> for [(); 2] { const VALUE: usize = 1; } fn main() { println!("{}", Fibonacci::<3>::VALUE); }
</details>aandreba@aandreba-ThinkPad-X13-Gen-2i:~/mismierdas$ cargo check Blocking waiting for file lock on build directory Checking mismierdas v0.1.0 (/home/aandreba/mismierdas) /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x315eb33)[0x7f0ccaf5eb33] /lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x7f0cc7a42520] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x22)[0x7f0cc8ff23a2] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x344823c)[0x7f0ccb24823c] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(_RNvXs1_NtNtCsb8UuTN1Agdw_11rustc_infer5infer7combineNtB5_17ConstInferUnifierINtNtCsarEwmg75Jzo_13rustc_type_ir4fold18FallibleTypeFolderNtNtNtCskHNjJQCvyFn_12rustc_middle2ty7context6TyCtxtE14try_fold_const+0x1b5)[0x7f0cc8ff2535] /home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/librustc_driver-aa58173d1e849e8b.so(+0x346072d)[0x7f0ccb26072d] error: could not compile `mismierdas` (bin "mismierdas") Caused by: process didn't exit successfully: `/home/aandreba/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name mismierdas --edition=2021 src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=86 --crate-type bin --emit=dep-info,metadata -C embed-bitcode=no -C debuginfo=2 -C metadata=3c95638ef1fb3ef2 -C extra-filename=-3c95638ef1fb3ef2 --out-dir /home/aandreba/mismierdas/target/debug/deps -C incremental=/home/aandreba/mismierdas/target/debug/incremental -L dependency=/home/aandreba/mismierdas/target/debug/deps` (signal: 11, SIGSEGV: invalid memory reference)Alex Andreba at 2023-05-12 08:41:07
That is probably a stackoverflow. By the way please put the error message in
<summary> ``` the error message here ``` </summary>bjorn3 at 2023-05-12 09:02:00
@bjorn3 Done! I thought so too, but the problem is that the overflow is happening, isn't it? I may not be getting something.
Alex Andreba at 2023-05-12 10:05:40
Rustc has code to grow the stack in some places and code to give an error when requiring too muvh recursion in other places. But it seems like neither exists in this specific spot. That should probably be fixed. As for how to get your code working, I'm not sure.
bjorn3 at 2023-05-12 10:43:03
Does const eval try to interpret the const expressions, or does it compile it down to a binary and run it, or something else entirely?
On Fri, 12 May 2023 at 11:43, bjorn3 @.***> wrote:
Rustc has code to grow the stack in some places and code to give an error when requiring too muvh recursion in other places. But it seems like neither exists in this specific spot. That should probably be fixed. As for how to get your code working, I'm not sure.
— Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/31844#issuecomment-1545544653, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEOOXGTEVKLBJO2A42H72TXFYH4XANCNFSM4B4DLZZQ . You are receiving this because you commented.Message ID: @.***>
-- Dr Matthew Pocock Turing ate my hamster LTD mailto: @.*** gchat: @.*** msn: @.*** irc.freenode.net: drdozer skype: matthew.pocock mob: +447535664143
Matthew Pocock at 2023-05-12 11:03:48
Const eval interprets MIR (one of rustc's intermediate representations) without compiling to machine code.
bjorn3 at 2023-05-12 11:40:54
@Aandreba are you sure you posted this to the right issue? This is the tracking issue for specialization; the tracking issue for complex const bounds is #76560.
Clar Fon at 2023-05-13 18:36:29
@Aandreba are you sure you posted this to the right issue? This is the tracking issue for specialization; the tracking issue for complex const bounds is #76560.
Your're right, this has probably more to do with complex bounds than specialization
Alex Andreba at 2023-05-13 20:48:05
Should associated type be specializable at all?
Some experience from uses of min_specialization in the standard library:
A) It would be useful to have associated type specialization as a way to get completely disjoint implementations. The
default fnmechanic means one must take care to override all methods in the specialization if their semantics are incompatible. If one could delegate the logic to an associated type instead (which might implement a trait) then those could be distinct implementations with no risk of overlap. This would even work with zero-sized associated types just to hold those methods.B) While ZST or same-layout associated-type specialization would be sufficient for the previous point, different-layout specialization would also be useful. E.g. when the added fields are only used in a specialized impl.
C) The possibility of coercions are a risk source for incompatible specializations. A way to tell the compiler to error if an impl depends on something that could be coerced into a different impl could improve safety.
the8472 at 2023-07-04 21:19:19
Is there any documentation on what's included in
min_specialization? Does anyone have a sense of the blockers for stabilization?Joshua Liebow-Feeser at 2023-09-05 17:03:00
I don't think there's much documentation because nobody is working on stabilizing it.
min_specializationcovers- the
defaultkeyword on fn in impls - specializing concrete type/const generic values where the base is a strict superset.
- some weird rules about how generic args can and can't be repeated in specializations (parts of the "always applicable" logic)
It doesn't include trait specialization or associated item specialization. It also doesn't include a good way to handle partially overlapping cases. In a few cases one can build hierarchies of multiple specializations, but not always.
To specialize on traits instead of concrete types you'll additionally need the
rustc_attrsfeature to get-
#[rustc_specialization_trait] -
#[rustc_unsafe_specialization_marker]
You can grep through the standard library looking for
default fnto see some uses of specialization. Afaik nobody is working on stabilizing it and the rustc_ traits aren't meant for stabilization in the first place, so there's no documentation for downstream use. The std dev guide has a page though: https://std-dev-guide.rust-lang.org/policy/specialization.htmlthe8472 at 2023-09-05 17:29:45
- the
I believe either Niko or Aaron Turon wrote a blog post about min specialization a fee years ago.
mark at 2023-09-06 05:22:54
https://aturon.github.io/blog/2017/07/08/lifetime-dispatch/ https://internals.rust-lang.org/t/shipping-specialization-a-story-of-soundness/5507 https://smallcultfollowing.com/babysteps/blog/2018/02/09/maximally-minimal-specialization-always-applicable-impls/ https://internals.rust-lang.org/t/blog-post-maximally-minimal-specialization-always-applicable-impls/6739 Perhaps the trait system rework will spur this on?
vacuus at 2023-09-08 23:17:45
Hello!
I have been writing code using specialization, and come upon one more issue with specialization & lifetimes. As there are a lot of problems with that, I might have missed that this particular situation has been discussed before - in that case, I apologize.
More concretely, the problem is shown in this example: playground. Using again a specialization for the case that two types are equal, we can change the lifetime of a reference arbitrarily, thus leading to UB. This does not require specializing associate types.
Many thanks, Simon
Simon Pohmann at 2023-11-10 10:58:30
Hello!
I have been writing code using specialization, and come upon one more issue with specialization & lifetimes. As there are a lot of problems with that, I might have missed that this particular situation has been discussed before - in that case, I apologize.
More concretely, the problem is shown in this example: playground. Using again a specialization for the case that two types are equal, we can change the lifetime of a reference arbitrarily, thus leading to UB. This does not require specializing associate types.
Many thanks, Simon
Isn't it a specialization over lifetimes?
Oleksandr Babak at 2023-11-10 12:26:08
Yes, it is (although "hidden").
Simon Pohmann at 2023-11-10 12:29:07
The
specializationfeature is marked unstable for that reason (as also explained in these blog posts https://github.com/rust-lang/rust/issues/31844#issuecomment-1712327948), among others.The compiler warns:
warning: the feature
specializationis incomplete and may not be safe to use and/or cause compiler crashes = note: see issue #31844 https://github.com/rust-lang/rust/issues/31844 for more information = help: consider usingmin_specializationinstead, which is more stable and complete = note:#[warn(incomplete_features)]on by defaultSo, for now use
min_specializationand the specialization traits. https://github.com/rust-lang/rust/issues/31844#issuecomment-1707023896 Those are more usable.the8472 at 2023-11-10 13:11:41
I feel like
openwould be a better keyword thandefault, but that's just my two cents.Daniel at 2023-11-20 12:19:36
Tracking issues have a tendency to become unmanageable. Please open a dedicated new issue and label it with https://github.com/rust-lang/rust/labels/F-specialization for absolutely any topics you want to discuss or have questions about. See https://github.com/rust-lang/compiler-team/issues/739 for details and discussions on this prospective policy.
Oli Scherer at 2023-11-20 13:19:50
Ugh, Github's noise combined with Github's infuriating collapsing comment behavior has hidden Oli's most recent comment. Reposting:
Tracking issues have a tendency to become unmanageable. Please open a dedicated new issue and label it with F-specialization for absolutely any topics you want to discuss or have questions about. See https://github.com/rust-lang/compiler-team/issues/739 for details and discussions on this prospective policy.
bstrie at 2024-11-26 11:59:41
I've added that to the top comment for visibility.
Jacob Pratt at 2024-11-26 22:16:13
I needed to create a blanket transformation from type
TtoT'with default identity implementation(T->T). Sadly, this needs specialization. But even with specialization this doesn't work correctly. Consider the following simple attempt (this compiles)pub trait ConvertOutput { type Output; } impl<T> ConvertOutput for T { default type Output = Self; } pub trait Convert: ConvertOutput { fn into(input: Self) -> Self::Output; } impl<T: ConvertOutput<Output = T>> Convert for T { fn into(input: Self) -> Self::Output { input } }The idea is to only specialize
ConvertOutput::Outputand have a blanket implementation ofConvertonly for types for whichConvertOutput::Output = Self. However, this doesn't work as expected. Consider this:fn accept_only_i32(_value: i32){} fn example() { let x = 42; let x = Convert::into(x); accept_only_i32(x) }Now the code does not compile since the value of
xis supposedly<i32 as ConvertOutput>::Outputwhich the compiler fails to see that isSelf(which in this case isi32). This is weird since there are no generics involved in the code and so it should be easy to infer the type.I think this is related to Hazard: interactions with type checking, but I don't think the problem shown there is relevant to this example (though I do suspect that this is a false positive made by making default associated types completely opaque).
Side note. In fact, I think that their problem can be solved by separating their trait
Exampleinto two separate traits as shown here. Then,fn generatewill have a blanket implementation only for types for whichOutput=Box<Self>which will makefn troublefail to compile unless it specifies that this is a typeTfor whichOutput=Box<T>. But thenfn weaponizewill fail to compile sincebool's associated type isSelfand notBox<Self>.Glar at 2025-03-24 12:33:25
Now the code does not compile since the value of x is supposedly <i32 as ConvertOutput>::Output which the compiler fails to see that is Self (which in this case is i32).
It is
<{int} as ConvertOutput>::Outputwhere{int}is an inference variable for an integer type. There could be another integer type that you can convert to i32. The fallback from{int}to i32 likely happens after it already needs to know which impl to pick forConvert::into.bjorn3 at 2025-03-24 12:46:28
<{int} as ConvertOutput>::Output
Thanks for the reply, yea I didn't write explicitly the Output type since I thought it dosn't matter. Consider this modified code (this still don't compile)
fn accept_only_i32(_value: i32){} fn example() { let x = 42_i32; // modification: 42 -> 42_i32 let x = Convert::into(x); accept_only_i32(x) }The error is "type mismatch resolving
<i32 as ConvertOutput>::Output == i32" which is kind of wierd since ifi32implementConvert, then it must have been that...::OutputisSelf.Glar at 2025-03-24 13:03:35
Please discuss this on Zulip or users.rust-lang.org
This issue already has 600+ comments and is unmanageable while having lots of people subscribed for updates on the implementation
Oli Scherer at 2025-03-24 13:10:37