Reputation: 2094
I defined a trait Foo
, implemented this trait for [u32]
, and wrote a function bar
taking this trait as argument (playground):
trait Foo {
fn foo(&self) -> u32;
}
impl Foo for [u32] {
fn foo(&self) -> u32 {
self[0]
}
}
fn bar<T>(f: &T) -> u32
where
T: Foo,
{
f.foo() + 1
}
fn main() {
let f: &[u32] = &[42];
bar(f);
}
This does not compile because bar
implicitly expects its arguments to be Sized
:
error[E0277]: the trait bound `[u32]: std::marker::Sized` is not satisfied
--> src/main.rs:20:5
|
20 | bar(f);
| ^^^ `[u32]` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[u32]`
note: required by `bar`
--> src/main.rs:11:1
|
11 | / fn bar<T>(f: &T) -> u32
12 | | where
13 | | T: Foo,
14 | | {
15 | | f.foo() + 1
16 | | }
| |_^
I can fix it with T: Foo + ?Sized
, but then I would have to do this for every function expecting a Foo
, which is a pain...
Can I declare once and for all that implementations of Foo
should not be expected to be Sized
? I tried trait Foo: ?Sized
in line 1, but the compiler complains about it.
This question is not the same as Trait implementing Sized. In that question, the Foo
parameter is moved, so it is normal that the compiler wants to know its size at compile time. In my case, the parameter is a reference, so it does not need to be sized -- but still the compiler implicitly assumes it is, unless explicitly told (using + ?Sized
). What I would like to change is this implicit assumption, for this particular trait.
Upvotes: 2
Views: 142
Reputation: 8486
trait Foo: ?Sized
?When you say that every
Foo
isSized
, you're kind of hiding the truth to yourself. Yes, everyFoo
isSized
but actually every type has a given size at some point. The real important information is that you're not saying how much this size is.
In this case you asking for a sized [u32]
:
error[E0277]: the trait bound `[u32]: std::marker::Sized` is not satisfied
|
| impl Foo for [u32] {
| ^^^ `[u32]` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[u32]`
For further information consult this answer of Trait implementing Sized.
T
be unsized, since we only have a reference?Let me quote again from this answer from Why does a reference to a trait in a generic function have to implement Sized
?
By default, all generic types on functions implicitly have the
Sized
bound, regardless of how they are used. You need to explicitly opt-out of that requirement using?Sized
This will solve your problem:
fn bar<T>(f: &T) -> u32
where
T: Foo + ?Sized,
{
f.foo() + 1
}
It would also be possible to implement Foo
for &[u32]
:
impl<'a> Foo for &'a [u32] {
fn foo(&self) -> u32 {
self[0]
}
}
fn bar<T>(f: T) -> u32
where
T: Foo,
{
f.foo() + 1
}
In this particular case, you could even generalize your implementation for Foo
and it would work on Vec
s and arrays as well as references to those types:
impl<T: AsRef<[u32]>> Foo for T {
fn foo(&self) -> u32 {
self.as_ref()[0]
}
}
With the new impl Trait syntax, bar
can be shortened in the last two cases to
fn bar(f: impl Foo) -> u32 {
f.foo() + 1
}
Upvotes: 2