Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upTracking issue for RFC 2532, "Associated type defaults" #29661
Comments
aturon
added
T-lang
B-unstable
labels
Nov 6, 2015
housleyjk
referenced this issue
Dec 15, 2015
Closed
expose eventloop to allow timeouts to be set #14
Stebalien
referenced this issue
Jan 19, 2016
Closed
Default the associated return type of unop/binop traits to `Self` #20399
This comment was marked as outdated.
This comment was marked as outdated.
|
One use case I’ve had for this is a default method whose return type is an associated type: /// "Callbacks" for a push-based parser
trait Sink {
fn handle_foo(&mut self, ...);
// ...
type Output = Self;
fn finish(self) -> Self::Output { self }
}This means that
This seems very restrictive (and in particular would prevent my use case). What’s the reason for this? |
This comment was marked as outdated.
This comment was marked as outdated.
There is a basic tradeoff here, having to do with soundness. Basically, there are two options at the extreme:
This is described in slightly more detail in the RFC. Originally we proposed to go with the second route, but it has tended to feel pretty hokey, and we've been leaning more toward the first route. (Incidentally, much the same question comes up for specialization.) I'm happy to elaborate further, but wanted to jot off a quick response for now. |
This comment was marked as outdated.
This comment was marked as outdated.
|
Would it be possible to track which default methods rely of which associated types being the default, so that only those need to be overridden? |
This comment was marked as outdated.
This comment was marked as outdated.
|
@SimonSapin Possibly, but we have a pretty strong bias toward being explicit in signatures about that sort of thing, rather than trying to infer it from the code. It's always a bad surprise when changing the body of a function in one place can cause a downstream crate to fail typechecking. You could consider having some explicit "grouping" of items that get to see a given default associated type. I'm not sure if you've been following the specialization RFC, but part of the proposal is a generalization of default implementations in traits. Imagine something like the following alternative design for trait Add<Rhs=Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
fn add_assign(&mut self, Rhs);
}
// the `default` qualifier here means (1) not all items are impled
// and (2) those that are can be further specialized
default impl<T: Clone, Rhs> Add<Rhs> for T {
fn add_assign(&mut self, rhs: R) {
let tmp = self.clone() + rhs;
*self = tmp;
}
}This feature lets you refine default implementations given additional type information. You can have many such The fact that you can have multiple such blocks might give us a natural way to delimit the scopes in which associated types are visible (and hence in which the methods have to be overridden when those types are). |
This comment was marked as outdated.
This comment was marked as outdated.
|
(cc @nikomatsakis on that last comment) |
This comment was marked as outdated.
This comment was marked as outdated.
|
@aturon I assume you mean specifically this last paragraph:
I find this idea appealing, I just think we have to square it away carefully, especially with the precise fn types we ought to be using. :) |
steveklabnik
referenced this issue
Jul 25, 2016
Closed
Error E0399 regression on nightly from beta. #27377
nrc
added
the
B-RFC-implemented
label
Aug 29, 2016
This comment was marked as outdated.
This comment was marked as outdated.
|
Is this feature still being worked on? What do you think of something like this as a use case for this feature? trait Bar {
type Foo;
// We want to default to just using Foo as its DetailedVariant
type DetailedFoo = Self::Foo;
// ...other trait methods...
fn detailed(&self) -> DetailedFoo;
// ...other trait methods...
} |
This comment was marked as resolved.
This comment was marked as resolved.
|
@sunjay nobody is working on this, I think we are still unsure what semantics we would want to have. |
This comment was marked as resolved.
This comment was marked as resolved.
Kixunil
commented
May 7, 2017
What about a compromise: if default method mentions the associated type in it's signature, then this type is allowed to be default and then, if you override such type, only those methods that use the type in signature would see the type being default. Would this still be unsound/surprising? |
This comment was marked as outdated.
This comment was marked as outdated.
dobkeratops
commented
Jun 20, 2017
•
|
I hope this feature can be stabilised, it would help me out; For my use case specific use case: I think it was most helpful for the trait to compute types that implementations must conform to; it was about letting any code that uses the trait also assume the whole set of bounds it defines. (whereas if I specify a 'where clause' in the trait, that merely tells users what else they need to manually specify) it might look like overkill for the example in the thread, but the rest of my experiment is a lot messier than that |
This comment was marked as outdated.
This comment was marked as outdated.
dobkeratops
commented
Jun 20, 2017
•
|
this is the sort of thing I'd want to be able to do:-
maybe there's a nice way to say 'two functions are supposed to be inverses', e.g.
(any advice on how parts of this may already be possible is welcome, until 1 day ago I didn't realise you can do parts of that by placing bounds on the associated types. Part of the problem is you are essentially re-writing code in a sort of LISP using the awkward angle-bracket type syntax; .. it's like we're going to end up with 'trait metaprogramming..' Are there any RFCs for a C++ like 'decltype' ? I also wish you had the old syntax for traits back as an option (just as you've got the option with 'where' to swap the order of writing bounds; part of the awkwardness is flipping the order in your head ... "Sum<Y,Output=Z> for X { fn sub(&self/* X / , Y)->Z {} } |
Mark-Simulacrum
added
the
C-tracking-issue
label
Jul 22, 2017
This comment was marked as outdated.
This comment was marked as outdated.
real-or-random
commented
Aug 23, 2017
|
I have a issue similar to the one of @dobkeratops, where I'd like to provide a type alias, which is not just a default but should not be changed. The code is
It should not be possible to override Also, I ran into the restriction that @SimonSapin had with associated methods. I think it would be useful to have a reasonable solution to that (if one exists). |
This comment was marked as resolved.
This comment was marked as resolved.
Osspial
commented
Sep 19, 2017
|
Would it be out of the question to stabilize a conservative implementation of this feature that doesn't let default functions assume the details of associated types, and lift restrictions later as allowed? Doing that should allow backwards-compatible changes in the future, and from what I've seen there don't seem to be any outstanding bugs preventing this from being stabilized. Those are also the semantics generic parameters on traits follow right now, so the behavior would be consistent with other parts of the language. |
This comment was marked as resolved.
This comment was marked as resolved.
WaDelma
commented
Nov 15, 2017
|
I would also prefer getting a conservative version of this stabilised as currently there is no way to backwards compatibly add new associated types. |
nikomatsakis
referenced this issue
Nov 16, 2017
Closed
associated_type_defaults not working correctly #35986
This comment was marked as outdated.
This comment was marked as outdated.
jtremback
commented
Dec 20, 2017
•
|
I'm trying to make a trait with a function that returns an iterator and this is tripping me up:
https://play.rust-lang.org/?gist=1197424c5b9c3f16c6cdfc80fe744d5c&version=stable |
This comment was marked as resolved.
This comment was marked as resolved.
|
I may have run into a couple of bugs with this associated type defaults, as I believe https://play.rust-lang.org/?gist=ffc94a727888539a99ccfe6d9965a217&version=nightly should compile. It looks like the default is not actually being "applied" in the trait impl. I assume this is not intended? Fixing that by explicitly applying (l.31 from working version link, below) runs into a second error, requiring an ?Sized bound (l.6 below). Here is the working version: https://play.rust-lang.org/?gist=3e2f2f39e38815956aaefaf511add0c1&version=nightly Credit to @durka for the workarounds. (Thank you!) |
This comment was marked as resolved.
This comment was marked as resolved.
Kixunil
commented
Feb 10, 2018
•
|
I just had one idea that would help ergonomics a lot. I'll call it "final associated types" for the purpose of this explanation. The idea is that some traits define associated types and sometimes, they use them as type parameters of other types. A notable example is the trait Future {
type Item;
type Error;
// | This is long and annoying to write and read
// V
fn poll(&mut self) -> Poll<Self::Item, Self::Error>;
}What would be nice is being able to write this: trait Future {
type Item;
type Error;
// This is practically type alias.
// Can't be changed in trait impl, only used.
final type Poll = Poll<Self::Item, Self::Error>;
fn poll(&mut self) -> Self::Poll;
}It would also help various cases when one has associated type called trait Foo {
type Error;
// It can be generic
final type Result<T> = Result<T, Self::Error>;
fn foo(&mut self) -> Self::Result<u32>;
}What do you think? Should I write an RFC, or could this be part of something else? |
This comment was marked as outdated.
This comment was marked as outdated.
bgeron
commented
Feb 21, 2018
|
@Kixunil For the first use case you can consider putting the type definition outside the trait: trait Future {
type Item;
type Error;
fn poll(&mut self) -> FPoll<Self>;
}
type FPoll<F: Future> = Poll<F::Item, F::Error>; |
This comment was marked as outdated.
This comment was marked as outdated.
Kixunil
commented
Feb 24, 2018
|
@bgeron good idea. I still like |
This comment was marked as resolved.
This comment was marked as resolved.
|
@Osspial as much as that'd be good, changing from a conservative to non-conservative assumption is a breaking change, as it would mean default methods which were previously available are now not available. I'm for stabilizing this as well, but we need good semantics first. |
hawkw
referenced this issue
Apr 23, 2018
Merged
proxy: Make control::discovery::Bind generic over its Endpoint type #796
dtolnay
referenced this issue
May 23, 2018
Open
Internal buffering disrupts format-specific deserialization features #1183
This comment has been minimized.
This comment has been minimized.
|
Here is a workaround I developed for serde-rs/serde#1354 and worked successfully for my use case. It doesn't support all possible uses for associated type defaults but it was sufficient for mine. Suppose we have an existing trait // Placeholder for whatever bounds you would want to write on the
// associated type.
trait Bounds: Default + std::fmt::Debug {}
impl<T> Bounds for T where T: Default + std::fmt::Debug {}
trait MyTrait {
type Associated: Bounds = ();
//~~~~~~~~~~~~~~~~~~~~~~^^^^ unstable
}
struct T1;
impl MyTrait for T1 {}
struct T2;
impl MyTrait for T2 {
type Associated = String;
}
fn main() {
println!("{:?}", T1::Associated::default());
println!("{:?}", T2::Associated::default());
}Instead we can write: trait Bounds: Default + std::fmt::Debug {}
impl<T> Bounds for T where T: Default + std::fmt::Debug {}
trait MyTrait {
//type Associated: Bounds = ();
fn call_with_associated<C: WithAssociated>(callback: C) -> C::Value {
type DefaultAssociated = ();
callback.run::<DefaultAssociated>()
}
}
trait WithAssociated {
type Value;
fn run<A: Bounds>(self) -> Self::Value;
}
struct T1;
impl MyTrait for T1 {
// type Associated = ();
}
struct T2;
impl MyTrait for T2 {
// type Associated = String;
fn call_with_associated<C: WithAssociated>(callback: C) -> C::Value {
callback.run::<String>()
}
}
fn main() {
struct Callback;
impl WithAssociated for Callback {
type Value = ();
fn run<A: Bounds>(self) -> Self::Value {
// code uses the "associated type" A
println!("Associated = {:?}", A::default());
}
}
T1::call_with_associated(Callback);
T2::call_with_associated(Callback);
} |
This comment has been minimized.
This comment has been minimized.
AGausmann
referenced this issue
Oct 6, 2018
Closed
Adding a serializer method to complement deserialize_any #1405
This comment has been minimized.
This comment has been minimized.
|
I believe I have ran into a bug with this feature. Here is a minimal example to reprodue it: trait Foo {
type Inner = ();
type Outer: Into<Self::Inner>;
}
impl Foo for () {
// With this, it compiles:
// type Inner = ();
type Outer = ();
}Although Edit: more precisely, it gives this error:
It seems like the associated type default is only applied after the trait bound is evaluated. Is this expected? |
PieterPenninckx
referenced this issue
Dec 31, 2018
Open
Split plugin API to make it thread safe. #49 #65
This comment has been minimized.
This comment has been minimized.
|
Dumping for future reference: #![feature(associated_type_defaults)]
trait A {
type B = Self::C;
type C = Self::B;
}
impl A for () {}
fn main() {
let x: <() as A>::B;
}==> error[E0275]: overflow evaluating the requirement `<() as A>::B`
thread '<unnamed>' panicked at 'Metadata module not compiled?', src/libcore/option.rs:1038:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: aborting due to previous error |
pnkfelix
referenced this issue
Feb 7, 2019
Open
nightly ICE: panicked at 'Box<Any>', /checkout/src/librustc_errors/lib.rs:434:8 #43924
This comment has been minimized.
This comment has been minimized.
|
RFC rust-lang/rfcs#2532 is now merged and specifies how |
Centril
added
B-RFC-approved
and removed
B-RFC-implemented
labels
Feb 25, 2019
Centril
changed the title
Tracking issue for `associated_type_defaults`
Tracking issue for RFC 2532, "Associated type defaults"
Feb 25, 2019
This comment has been minimized.
This comment has been minimized.
Visic
commented
Mar 7, 2019
|
In the following example, I was trying to use associated types to clean up programming against a generic trait. It seems to work fine for the "Self::Input" but doesn't work for "Self::Output". I don't know if this is a bug, or if my expectation isn't intended, but I at least wanted to share this in case something needs to be changed.
========
|
aturon commentedNov 6, 2015
•
edited by Centril
This is a tracking issue for the RFC "Associated type defaults" (rust-lang/rfcs#2532) under the feature gate
#![feature(associated_type_defaults)].The associated item RFC included the ability to provide defaults for associated types, with some tricky rules about how that would influence defaulted methods.
The early implementation of this feature was gated, because there is a widespread feeling that we want a different semantics from the RFC -- namely, that default methods should not be able to assume anything about associated types. This is especially true given the specialization RFC, which provides a much cleaner way of tailoring default implementations.
The new RFC, rust-lang/rfcs#2532, specifies that this should be the new semantics but has not been implemented yet. The existing behavior under
#![feature(associated_type_defaults)]is buggy and does not conform to the new RFC. Consult it for a discussion on changes that will be made.Steps:
Unresolved questions: