-
Notifications
You must be signed in to change notification settings - Fork 110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reconsider C-STRUCT-BOUNDS #217
Comments
Let me clarify this to make the rules clearer and maybe more obvious. Terminology: A type is considered Trait wrapper if:
A type is multi trait wrapper if
By "nobody would write it" I mean that there's no good reason to write it. I propose that:
Examples: #[Derive(Clone, Debug)]
struct IterFilter<I, F> where I: Iterator, F: FnMut(&I::Item) -> bool {
// ...
}
impl<T, F> Iterator for IterFilter<T, F> // ... This is a combinator analogous to As we can see this type also impls Multi trait wrapper example: // Can be used for Read, Write or both
struct Buffer<T> {
io: T,
read_buf: Vec<u8>,
writ_buf: Vec<u8>,
}
impl<T: Read> Buffer<T> {
fn new_reader(reader: T) -> Self {
// ...
}
}
impl<T: Write> Buffer<T> {
fn new_writer(writer: T) -> Self {
// ...
}
}
impl<T> Read for Buffer<T> where T: Read // ...
impl<T> Write for Buffer<T> where T: Write // ... This type provides useful functionality if However, consider this alternative: // Can be used for Read, Write or both
struct ReadBuffer<T> where T: Read {
io: T,
read_buf: Vec<u8>,
}
impl<T: Read> Buffer<T> {
fn new(reader: T) -> Self {
// ...
}
}
impl<T> Read for Buffer<T> where T: Read // ...
impl<T> Write for Buffer<T> where T: Write // ... This type implements buffering for readers only, not for writers. It additionally delegates writes if the inner type is a writer but does not buffer them. Such type is still useless without I hope this explanation and the examples make the idea and reasons behind it more clear. I'll be happy to clarify if something is still not obvious. |
First let me say that
C-STRUCT-BOUNDS
talks about derived bounds mainly, which is completely fine, just a bit confusing (as the text down below speaks about bounds in general). I'd like to see a more visible distinction between the two, but that's not my main point here.I suggest adding a new execption: "when a struct is supposed to wrap a type implementing a specific trait" - that means specialized wrappers for
Iterator
,Future
,Stream
,Read
,Write
etc. Reason: if the user attempts to instantiate the type without generic implementing the trait, he hits the error somewhere else, which is confusing and causes long error messages.Requiring the trait will cause the error sooner, pointing at the correct location.
As an alternative, one might omit the bounds from struct itself and put them on every constructing function instead. This works until the crate author forgets about one case and exactly that case will be experienced by the user. (Murphy law: Everything that can break will break.)
#6 reasoned that it's annoying, but since generics are just type-level functions, it's equally annoying as having to write types in function signatures, which was very reasonably considered as a good thing.
The text was updated successfully, but these errors were encountered: