Open lilyball opened 9 years ago
You should be able to get around this to some extent by using associated types in combination with the unit () type. Implement a trait with an associated type of () for types which don't need the field, and the correct type otherwise.
@Diggsey associated types won't add storage for that type to a struct. The example is that you would normall want Take<R> { inner: R, limit: usize }
, but might want Take<R> where R: Seek { inner: R, limit: usize, orig_limit: usize }
.
You misunderstand me, I was describing something like this (not sure of syntax):
trait TakeHelper {
type OrigLimit = ()
}
impl<R> TakeHelper for R where R: Seek {
type OrigLimit = usize
}
struct Take<R> {
inner: R,
limit: usize,
orig_limit: <R as TakeHelper>::OrigLimit
}
Oh I see. This requires default values for associated items.
That requires either being able to specialize trait impls, or being able to impl traits with negative trait bounds, as in
trait TakeHelper {
type OrigLimit = ()
}
impl<R> TakeHelper for R where R: Seek {
type OrigLimit = usize
}
// default impl, specialized by the above
impl<R> TakeHelper for R {
type OrigLimit = ()
}
// alternatively, negative trait bound
impl<R> TakeHelper for R where R: !Seek {
type OrigLimit = ()
}
I implemented something like this in a patch to lru-cache
, but it's not terribly nice. I made the LRUCache
type generic over a CountableMeter
trait, which has an associated type that can be ()
for the default case where it doesn't need to store an additional count. It definitely required jumping through more hoops than I had hoped.
Right now generic types can implement traits if its type parameters match certain constraints. But sometimes implementing these additional traits requires additional struct fields. Since the struct field list is currently fixed, that means that the programmer has to decide between adding additional fields that are unused in most instances of the struct, or not implementing traits that it would be nice to have.
For example, I think it would be nice if
std::io::Take<T>
implementedstd::io::Seek
ifT: std::io::Seek
. But in order to do this,std::io::Take
would need an additional field to keep track of the original limit (in order to supportSeekFrom::Start
). It seems very wasteful to put this extra field on all implementations ofTake
when most of them would never touch the field.I'm not sure what the syntax of this should look like.