support default impl for specialization
rust-lang/rfcs#1210 included support for default impl, which declared a set of defaults that other impls can inherit (if they correspond to a subset of the default impl's types). This was intended as a replacement/improvement of the existing default methods that appear in trait bodies (it was also originally called partial impl). Unfortunately, this feature is not yet implemented!
Some things to be sure of:
- [ ] a
default implneed not include all items from the trait - [ ] a
default implalone does not mean that a type implements the trait - [x] all items in a
default implare (implicitly)defaultand hence specializable
cc https://github.com/rust-lang/rust/issues/31844
cc @giannicic @aturon
Niko Matsakis at 2016-11-08 20:47:21
@nikomatsakis the PR addressing the 3rd task list item has been merged. Now i'm working on the first two points.
Gianni Ciccarelli at 2017-04-27 12:59:36
@giannicic awesome!
Niko Matsakis at 2017-04-28 19:16:47
Any progress on this?
Phlosioneer at 2017-08-01 14:40:56
@Phlosioneer Yes, the first point is done. Currently I'm working on the second one. I've looked at the trait resolution and identified where to place the
default impltrait implementation check, now I'm figuring out how to properly implement this check because i need to differentiate it based on the cause of the obligation. For example, if i have adefault implthat doesn't implement all the methods of the traitFoo, like the followingtrait Foo { fn foo_one(&self) -> &'static str; fn foo_two(&self) -> &'static str; } default impl<T> Foo for T { fn foo_one(&self) -> &'static str { "generic" } } struct MyStruct;a call like
MyStruct.foo_one()should pass the trait resolution because of the first point. Instead if i callfoo(MyStruct)with foo likefn foo<T: Foo>(x: T) -> &'static strthe trait resolution should fail because of the second point. It could pass if thedefault implimplements all the methods of theTraitGianni Ciccarelli at 2017-08-02 21:01:16
Can inherent impls be default?
This currently compiles successfully, but the compiler has zero tests for it, so it's probably unintended.
#![feature(specialization)] struct S; default impl S {} fn main() {}The general idea is reasonable though, e.g.
struct S<T>(T); default impl<T> S<T> { fn f() { println!("Hello world!") } } impl S<u8> { fn f() { println!("Goodbye world!") } } // This currently errorsVadim Petrochenkov at 2017-12-02 12:06:48
Can auto trait impls (including negative ones) be default?
This is currently accepted:
struct S; struct Z; default unsafe impl Send for S {} default impl !Send for Z {}Vadim Petrochenkov at 2017-12-02 12:26:28
https://github.com/rust-lang/rust/pull/46455 conservatively makes both https://github.com/rust-lang/rust/issues/37653#issuecomment-348687794 and https://github.com/rust-lang/rust/issues/37653#issuecomment-348688785 errors.
Vadim Petrochenkov at 2017-12-03 00:30:22
Really excited for full support of specialization (esp. point 2)! +1
Restioson at 2017-12-03 18:16:41
@petrochenkov as per RFC, there is no mentions about auto traits and a possible extension that covers inherent impls. But I suppose that it will not be implemented in the near future so, thank you for including an error about that in your PR :)
Gianni Ciccarelli at 2017-12-04 23:04:29
As seen in the PR #45404, many people are confused about the syntax. The author of #45404, the author of #44790, the author of
parquet-rs, and the poster of #48515 were at least confused about it.Perhaps it is a good idea to propose a change to the syntax? For example resurrecting the
partial implkeyword from rust-lang/rfcs#1210 or introduce an attribute like#[partial] default impl.Masaki Hara at 2018-02-25 03:21:44
I don't think it's confusing, I think it just needs to be documented in the error messages of default impl. For example, in the parquet-rs error, the confusion would go away with a help message:
= help: there is a default impl for the trait bound: `{{the default impl here}}` A default impl does not count as satisfying the trait bounds; try removing `default`.Which would make the full error:
error[E0599]: no method named `set_data` found for type `encodings::decoding::PlainDecoder<T>` in the current scope --> src/column/reader.rs:443:18 | 443 | dictionary.set_data(page.buffer().clone(), num_values as usize)?; | ^^^^^^^^ | ::: src/encodings/decoding.rs:83:1 | 83 | pub struct PlainDecoder<T: DataType> { | ------------------------------------ method `set_data` not found for this | = note: the method `set_data` exists but the following trait bounds were not satisfied: `encodings::decoding::PlainDecoder<T> : encodings::decoding::Decoder<_>` = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `set_data`, perhaps you need to implement it: candidate #1: `encodings::decoding::Decoder` = help: there is a default impl for the trait bound: `default impl<T: DataType> Decoder<T> for PlainDecoder<T>` A default impl does not count as satisfying the trait bounds; try removing `default`.I think that does a really good job of not only pointing out the mistake with relevant info, but also teaching the user why it's an error and how it can be fixed.
@nikomatsakis @giannicic Pinging because of your work on #45404
Edit: formatting.
Phlosioneer at 2018-02-25 18:51:43
I am torn. On the one hand, I liked the "economy" of using
default. I feel like havingpartial implis its own sort of complexity. On the other hand, I think there is a kind of assumption that adefault impl { .. }ought to be equivalent toimpl { default ... }. I'm not sure though if that means that this is a natural rule that we ought to respect, or a matter of needing more education. Perhaps -- even -- it is thedefaultkeyword on functions and items that should be changed (e.g., tospecializableor something).Niko Matsakis at 2018-02-27 17:38:16
Personally, I was slightly confused as to why
partial implwas scrapped in favour ofdefault impl-default impldoesn't really sound like it can be partial...Restioson at 2018-02-27 18:22:16
all items in a default impl are (implicitly) default and hence specializable
I think this is a serious limitation, because it means there is no way to have a partial impl where some items are not specializable. I do not see that limitation discussed in the RFC, nor a motivation for why it might be required. It's a big RFC though, so maybe I missed it?
Here is a short discussion with a use-case for a partial unspecializable impl. Basically the idea is to implement some associated types of the trait, and then provide some default implementations that require this particular choice of associated type. I think the current approach makes that kind of reuse fundamentally impossible.
Ralf Jung at 2020-04-19 12:01:57
@RalfJung it was a deliberate simplification, but one that I'm still not 100% sure was the right call. The initial version of the RFC had
partial implanddefault fnas two distinct concepts. They were merged because it seemed pretty clear that it was going to be confusing somehow, particularly if we hadpartial default impl, but that did imply giving up on the ability to have some things fixed and some things not. You're right that the use case you cited then becomes impossible.Niko Matsakis at 2020-04-20 22:28:45
@nikomatsakis I see. IMO that limitation should be lifted before stabilization, it's rather surprising and frustrating that what seems like a purely syntactic choice makes some interesting specialization use-cases fundamentally impossible. Having
default implhave both of these effects (enable partial impl and make every item overwritable) violates orthogonality and compositionality, values that otherwise Rust considers very important.Could we get this listed as a concern to revisit in some tracking issue, maybe here?
Ralf Jung at 2020-04-21 07:52:09
@RalfJung I'd be willing to revisit this prior to stabilization, yes.
Niko Matsakis at 2020-04-22 14:54:39
Actually, @RalfJung, it's already listed:
- Should we have
default impl(where all items aredefault) orpartial impl(wheredefaultis opt-in)
Niko Matsakis at 2020-04-22 14:55:18
- Should we have
Oh, I was checking the items in this issue but not the main one. oops Thanks for adding a pointer to this sub-discussion here. :)
Ralf Jung at 2020-04-22 14:57:57
Will it be possible to define different default impls for differently trait bounded type parameters? e.g.
default impl<A: ToRoute> Routable for A { type Route = <A as ToRoute>::Route; fn route(&self) -> Self::Route { self.to_route() } } default impl<B: ToString> Routable for B { type Route = DefaultRoute; fn route(&self) -> Self::Route { DefaultRoute::from(self.to_string()) } }In case of
A: ToRouteandB: ToRoute + Debug,Ashould only match types not implementingDebug, i.e. the more specific trait bound has precedence.Linus Behrbohm at 2020-12-21 20:20:51
I posted a suggestion on
default implin https://github.com/rust-lang/rust/issues/31844#issuecomment-995627907 and just realized it would have been more appropriate here. I don't know if my suggestion makes sense at all and if it isn't too late, since this issue is already well on its way. But I would love to hear your thoughts.Jan Kleinert at 2021-12-16 10:37:46
@joergbrech I think you would be interested in the future possibilities section of RFC 2532 (associated type defaults).
Jonas Platte at 2021-12-16 10:56:22
@jplatte, thanks for the link. Yes default groups would be much more flexible!
Jan Kleinert at 2021-12-16 11:20:42