How to use non-'static trait objects with associated types? - rust

I have this type:
struct Wrap<T>(Vec<T>);
I want to implement std::ops::Index and return references to trait objects. This was my first attempt (Playground):
use std::ops::Index;
use std::fmt::Display;
impl<T> Index<usize> for Wrap<T>
where
T: Display
{
type Output = Display;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
This doesn't work and leads to this error:
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:13:9
|
7 | impl<T> Index<usize> for Wrap<T>
| - help: consider adding an explicit lifetime bound `T: 'static`...
...
13 | &self.0[index]
| ^^^^^^^^^^^^^^
|
note: ...so that the type `T` will meet its required lifetime bounds
--> src/main.rs:13:9
|
13 | &self.0[index]
| ^^^^^^^^^^^^^^
I think I know why this happens: type Output = Display is equivalent to type Output = Display + 'static as every trait object carries a lifetime bound which defaults to 'static.
So now I can just add the 'static bound to my parameter T, but this is over-constrained I think. I can easily implement such a method when not using an associated type:
impl<T> Wrap<T>
where
T: Display,
{
fn my_index(&self, index: usize) -> &Display {
&self.0[index]
}
}
No 'static bound needed, because now the signature desugars to:
fn my_index<'a>(&'a self, index: usize) -> &'a Display + 'a
Which makes sense: the trait object has to live for at least 'a. (Playground with all the code).
But can I make this work using associated types (like with the Index trait)? I have the feeling that this might work with generic associated types, but (a) I'm not sure and (b) they are not implemented yet.

One attempt is to attach a lifetime to the impl:
// Note: won't work.
impl<'a, T> Index<usize> for Wrap<T>
where
T: Display + 'a,
{
type Output = Display + 'a;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
However, the compiler will not accept it because 'a is not used.
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> src/main.rs:7:6
|
7 | impl<'a, T> Index<usize> for Wrap<T>
| ^^ unconstrained lifetime parameter
There are several solutions suggested by the error code E0207, but since we cannot change the Index trait, the only acceptable solution is to make Wrap capture that unconstrained lifetime parameter:
use std::ops::Index;
use std::fmt::Display;
use std::marker::PhantomData;
struct Wrap<'a, T>(Vec<T>, PhantomData<&'a ()>);
// ^~ ^~~~~~~~~~~~~~~~~~~
impl<'a, T> Index<usize> for Wrap<'a, T>
where
T: Display + 'a,
{
type Output = Display + 'a;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
fn main() {
let w = Wrap(vec!['a', 'b'], PhantomData);
println!("{}", &w[0]); // prints "a"
let s = "hi".to_string();
let w = Wrap(vec![&s], PhantomData);
println!("{}", &w[0]); // prints "hi"
}
(Playground)
For sure, this will change your API and that extra lifetime will infect everywhere... If this is not acceptable, you could either
Not use the Index trait, introduce your own lifetime-sensitive trait instead (thus users will need to use w.my_index(i) instead of &w[i]); or
Set Output = Display + 'static, and exclude all transient types. Users will need to clone or use Rc.

Return a reference to T instead of returning a reference to a trait object and let the user cast to a trait object, or just let Rust implicitly infer from the context when to perform the cast:
use std::fmt::Display;
use std::ops::Index;
fn main() {
let w1 = Wrap(vec!['I', 'b']);
let s = "am".to_string();
let w2 = Wrap(vec![&s]);
let w3 = Wrap(vec![1, 2]);
let mut trait_store: Vec<Box<Display>> = Vec::new();
trait_store.push(Box::new(w1.index(0)));
trait_store.push(Box::new(w2.index(0)));
trait_store.push(Box::new(w3.index(0)));
for el in trait_store {
println!("{}", el);
}
}
struct Wrap<T>(Vec<T>);
impl<T> Index<usize> for Wrap<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}

Hi I met the same problem as you. "Like &Index<usize, Output = Display>. This doesn't work with Index directly, but using Index in my question made it a bit easier."
I didn't find out whether the Rust releases some related features or not. But I figure out a rather foolish way to fulfill my demands.
This method only works when the structs which implement the trait are enumerable. Suppose that you have three structs Index1, Index2, Index3, they all implement the trait Index<usize, Output = Display>
Then we can simply wrap these structs by
pub enum Indices{
Index1(Index1),
Index2(Index2),
Index3(Index3),
}
And then implement the trait for the enum and all of its variants, there is an example for this:
rust - How do I implement a trait for an enum and its respective variants? - Stack Overflow

Related

Issues setting lifetimes in functions of Traits

I have a trait Atom that has many associated types, of which one is an owned version OP and the other is a borrow version O of essentially the same data. I have a function to_pow_view that creates a view from an owned version and I have an equality operator.
Below is an attempt:
pub trait Atom: PartialEq {
// variants truncated for this example
type P<'a>: Pow<'a, R = Self>;
type OP: OwnedPow<R = Self>;
}
pub trait Pow<'a>: Clone + PartialEq {
type R: Atom;
}
#[derive(Debug, Copy, Clone)]
pub enum AtomView<'a, R: Atom> {
Pow(R::P<'a>),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum OwnedAtom<R: Atom> {
Pow(R::OP),
}
pub trait OwnedPow {
type R: Atom;
fn some_mutable_fn(&mut self);
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a>;
// compiler said I should add 'a: 'b
fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
if self.to_pow_view().eq(&other) {
self.some_mutable_fn();
}
}
}
impl<R: Atom> OwnedAtom<R> {
// compiler said I should add 'a: 'b, why?
pub fn eq<'a: 'b, 'b>(&'a self, other: AtomView<'b, R>) -> bool {
let a: AtomView<'_, R> = match self {
OwnedAtom::Pow(p) => {
let pp = p.to_pow_view();
AtomView::Pow(pp)
}
};
match (&a, &other) {
(AtomView::Pow(l0), AtomView::Pow(r0)) => l0 == r0,
}
}
}
// implementation
#[derive(Debug, Copy, Clone, PartialEq)]
struct Rep {}
impl Atom for Rep {
type P<'a> = PowD<'a>;
type OP = OwnedPowD;
}
#[derive(Debug, Copy, Clone, PartialEq)]
struct PowD<'a> {
data: &'a [u8],
}
impl<'a> Pow<'a> for PowD<'a> {
type R = Rep;
}
struct OwnedPowD {
data: Vec<u8>,
}
impl OwnedPow for OwnedPowD {
type R = Rep;
fn some_mutable_fn(&mut self) {
todo!()
}
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a> {
PowD { data: &self.data }
}
}
fn main() {}
This code gives the error:
27 | fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
| -- lifetime `'b` defined here
28 | if self.to_pow_view().eq(&other) {
| ------------------
| |
| immutable borrow occurs here
| argument requires that `*self` is borrowed for `'b`
29 | self.some_mutable_fn();
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
I expect this to work, since the immutable borrow should be dropped right after the eq function evaluates.
Something in the setting of the lifetimes is wrong in this code, already in the equality function eq: I would expect that there is no relation between 'a and 'b; they should just live long enough to do the comparison. However, the compiler tells me that I should add 'a: 'b and I do not understand why. The same thing happened for the function test.
These problems lead me to believe that the lifetimes in to_pow_view are wrong, but no modification I tried made it work (except for removing the 'a lifetime on &'a self, but then the OwnedPowD does not compile anymore).
Link to playground
Can someone help understand what is going on?
Here's the point: you constrained Pow to be PartialEq. However, PartialEq is PartialEq<Self>. In other words, Pow<'a> only implements PartialEq<Pow<'a>> for the same 'a.
This is usually the case for any type with lifetime and PartialEq, so why does it always work but not here?
It usually works because if we compare T<'a> == T<'b>, the compiler can shrink the lifetimes to the shortest of the two and compare that.
However, Pow is a trait. Traits are invariant over their lifetime, in other words, it must stay exactly as is, not longer nor shorter. This is because they may be used with invariant types, for example Cell<&'a i32>. Here's an example of how it could be exploited if this was allowed:
use std::cell::Cell;
struct Evil<'a> {
v: Cell<&'a i32>,
}
impl PartialEq for Evil<'_> {
fn eq(&self, other: &Self) -> bool {
// We asserted the lifetimes are the same, so we can do that.
self.v.set(other.v.get());
false
}
}
fn main() {
let foo = Evil { v: Cell::new(&123) };
{
let has_short_lifetime = 456;
_ = foo == Evil { v: Cell::new(&has_short_lifetime) };
}
// Now `foo` contains a dangling reference to `has_short_lifetime`!
dbg!(foo.v.get());
}
The above code does not compile, because Evil is invariant over 'a, but if it would, it would contain UB in safe code. For that reason, traits, that may contain types such as Evil, are also invariant over their lifetimes.
Because of that, the compiler cannot shrink the lifetime of other. It can shrink the lifetime of self.to_pow_view() (in test(), eq() is similar), because it doesn't really shrink it, it just picks a shorter lifetime for to_pow_view()'s self. But because PartialEq is only implemented for types with the same lifetime, it means that the Pow resulting from self.to_pow_view() must have the same lifetime of other. Because of that, (a) 'a must be greater than or equal to 'b, so we can pick 'b out of it, and (b) by comparing, we borrow self for potentially whole 'a, because it may be that 'a == 'b and therefore the comparison borrows self for 'a, so it is still borrowed immutably while we borrow it mutably for some_mutable_fn().
Once we understood the problem, we can think about the solution. Either we require that Pow is covariant over 'a (can be shrinked), or we require that it implements PartialEq<Pow<'b>> for any lifetime 'b. The first is impossible in Rust, but the second is possible:
pub trait Pow<'a>: Clone + for<'b> PartialEq<<Self::R as Atom>::P<'b>> {
type R: Atom;
}
This triggers an error, because the automatically-derived PartialEq does not satisfy this requirement:
error: implementation of `PartialEq` is not general enough
--> src/main.rs:73:10
|
73 | impl<'a> Pow<'a> for PowD<'a> {
| ^^^^^^^ implementation of `PartialEq` is not general enough
|
= note: `PartialEq<PowD<'0>>` would have to be implemented for the type `PowD<'a>`, for any lifetime `'0`...
= note: ...but `PartialEq` is actually implemented for the type `PowD<'1>`, for some specific lifetime `'1`
So we need to implement PartialEq manually:
impl<'a, 'b> PartialEq<PowD<'b>> for PowD<'a> {
fn eq(&self, other: &PowD<'b>) -> bool {
self.data == other.data
}
}
And now it works.

Why is a static lifetime needed when using move? [duplicate]

I'm trying to implement something that looks like this minimal example:
trait Bar<T> {}
struct Foo<T> {
data: Vec<Box<Bar<T>>>,
}
impl<T> Foo<T> {
fn add<U: Bar<T>>(&mut self, x: U) {
self.data.push(Box::new(x));
}
}
Since Rust defaults to (as far as I can tell) pass-by-ownership, my mental model thinks this should work. The add method takes ownership of object x and is able to move this object into a Box because it knows the full type U (and not just trait Bar<T>). Once moved into a Box, the lifetime of the item inside the box should be tied to the actual lifetime of the box (e.g., when pop()ed off the vector the object will be destroyed).
Clearly, however, the compiler disagrees (and I'm sure knows a bit more than I...), asking me to consider adding a 'static lifetime qualifier (E0310). I am 99% sure that's not what I want, but I'm not exactly sure what I'm supposed to do.
To clarify what I'm thinking and help identify misconceptions, my mental model, coming from a C++ background, is:
Box<T> is essentially std::unique_ptr<T>
Without any annotations, variables are passed by value if Copy and rvalue-reference otherwise
With a reference annotation, & is roughly const& and &mut is roughly &
The default lifetime is lexical scope
Check out the entire error:
error[E0310]: the parameter type `U` may not live long enough
--> src/main.rs:9:24
|
8 | fn add<U: Bar<T>>(&mut self, x: U) {
| -- help: consider adding an explicit lifetime bound `U: 'static`...
9 | self.data.push(Box::new(x));
| ^^^^^^^^^^^
|
note: ...so that the type `U` will meet its required lifetime bounds
--> src/main.rs:9:24
|
9 | self.data.push(Box::new(x));
| ^^^^^^^^^^^
Specifically, the compiler is letting you know that it's possible that some arbitrary type U might contain a reference, and that reference could then become invalid:
impl<'a, T> Bar<T> for &'a str {}
fn main() {
let mut foo = Foo { data: vec![] };
{
let s = "oh no".to_string();
foo.add(s.as_ref());
}
}
That would be Bad News.
Whether you want a 'static lifetime or a parameterized lifetime is up to your needs. The 'static lifetime is easier to use, but has more restrictions. Because of this, it's the default when you declare a trait object in a struct or a type alias:
struct Foo<T> {
data: Vec<Box<dyn Bar<T>>>,
// same as
// data: Vec<Box<dyn Bar<T> + 'static>>,
}
However, when used as an argument, a trait object uses lifetime elision and gets a unique lifetime:
fn foo(&self, x: Box<dyn Bar<T>>)
// same as
// fn foo<'a, 'b>(&'a self, x: Box<dyn Bar<T> + 'b>)
These two things need to match up.
struct Foo<'a, T> {
data: Vec<Box<dyn Bar<T> + 'a>>,
}
impl<'a, T> Foo<'a, T> {
fn add<U>(&mut self, x: U)
where
U: Bar<T> + 'a,
{
self.data.push(Box::new(x));
}
}
or
struct Foo<T> {
data: Vec<Box<dyn Bar<T>>>,
}
impl<T> Foo<T> {
fn add<U>(&mut self, x: U)
where
U: Bar<T> + 'static,
{
self.data.push(Box::new(x));
}
}
asking me to consider adding a 'static lifetime qualifier (E0310). I am 99% sure that's not what I want, but I'm not exactly sure what I'm supposed to do.
Yes it is. The compiler does not want a &'static reference, it wants U: 'static.
Having U: 'static means that U contains no references with a lifetime less than 'static. This is required because you want to put a U instance in a structure without lifetimes.
trait Bar<T> {}
struct Foo<T> {
data: Vec<Box<dyn Bar<T>>>,
}
impl<T> Foo<T> {
fn add<U: Bar<T> + 'static>(&mut self, x: U) {
self.data.push(Box::new(x));
}
}

How to return an iterator over the keys of a HashMap from a trait implementation?

I'm trying to build a simple graph library in Rust. There is a trait Graph that any graph must implement. This trait has only one function at the moment, nodes, which allows iteration of the graph's nodes using a for-in loop.
An implementation of Graph, MapGraph, is a lightweight wrapper around a HashMap. MapGraph must implement the Graph trait method nodes. I'm having problems getting this to work.
Here's the code for Graph:
pub trait Graph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item = &N>>;
}
And here's the code for MapGraph:
use std::collections::HashMap;
use crate::rep::Graph;
pub struct MapGraph<N> {
map: HashMap<N, HashMap<N, ()>>
}
impl<N> MapGraph<N> {
pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
MapGraph { map }
}
}
impl<N> Graph<N> for MapGraph<N> {
fn nodes(&self) -> Box<dyn Iterator<Item=&N>> {
let keys = self.map.keys();
Box::new(keys)
}
}
The compiler gives this error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/lib.rs:19:29
|
19 | let keys = self.map.keys();
| ^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 18:5...
--> src/lib.rs:18:5
|
18 | / fn nodes(&self) -> Box<dyn Iterator<Item = &N>> {
19 | | let keys = self.map.keys();
20 | |
21 | | Box::new(keys)
22 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/lib.rs:19:20
|
19 | let keys = self.map.keys();
| ^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
I've found other references to this error, but those cases don't seem to look like the one I have here.
I'm using Box because the Graph trait has a function that itself returns a trait. What is the correct way to return an Iterator (or any other trait)? gives this approach as one option, and I haven't been able to implement any of the the others. If there's another way to do this, that would be fine.
What are my options for resolving this specific problem?
It works if you explicitly specify that the trait object (dyn Iterator) that you are returning contains references that are tied to the lifetime of self.
Without adding this bound, the compiler cannot infer from the function signature that the iterator cannot be used after self is moved or destroyed. Because the compiler cannot infer this, it cannot safely use self.map.keys() in the function's output.
Working example with this bound added:
pub trait Graph<N> {
fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a>;
}
use std::collections::HashMap;
pub struct MapGraph<N> {
map: HashMap<N, HashMap<N, ()>>,
}
impl<N> MapGraph<N> {
pub fn new(map: HashMap<N, HashMap<N, ()>>) -> Self {
MapGraph { map }
}
}
impl<N> Graph<N> for MapGraph<N> {
fn nodes<'a>(&'a self) -> Box<dyn Iterator<Item = &N> + 'a> {
let keys = self.map.keys();
Box::new(keys)
}
}
Playground
I had thought that a bound of Item = &'a N would also be required, but I guess that's already covered by the "+ 'a"...
N.B. that to make sense of an error like:
expected std::boxed::Box<(dyn std::iter::Iterator<Item = &N> + 'static)>
found std::boxed::Box<dyn std::iter::Iterator<Item = &N>>
you have to understand that the compiler, for ergonomic reasons, automatically adds a + 'static lifetime qualifier to any unqualified trait object. This means that an unqualified Box<dyn MyTrait> is transformed by the compiler into a Box<(dyn MyTrait + 'static)>, which in turn means that the object cannot contain any references except those that last for the lifetime of the entire program.
With this in mind you can see why self.map.keys() does not fit this strict bound, and a more specific explicit bound is required.

Parameter T might not live long enough [duplicate]

I'm trying to implement something that looks like this minimal example:
trait Bar<T> {}
struct Foo<T> {
data: Vec<Box<Bar<T>>>,
}
impl<T> Foo<T> {
fn add<U: Bar<T>>(&mut self, x: U) {
self.data.push(Box::new(x));
}
}
Since Rust defaults to (as far as I can tell) pass-by-ownership, my mental model thinks this should work. The add method takes ownership of object x and is able to move this object into a Box because it knows the full type U (and not just trait Bar<T>). Once moved into a Box, the lifetime of the item inside the box should be tied to the actual lifetime of the box (e.g., when pop()ed off the vector the object will be destroyed).
Clearly, however, the compiler disagrees (and I'm sure knows a bit more than I...), asking me to consider adding a 'static lifetime qualifier (E0310). I am 99% sure that's not what I want, but I'm not exactly sure what I'm supposed to do.
To clarify what I'm thinking and help identify misconceptions, my mental model, coming from a C++ background, is:
Box<T> is essentially std::unique_ptr<T>
Without any annotations, variables are passed by value if Copy and rvalue-reference otherwise
With a reference annotation, & is roughly const& and &mut is roughly &
The default lifetime is lexical scope
Check out the entire error:
error[E0310]: the parameter type `U` may not live long enough
--> src/main.rs:9:24
|
8 | fn add<U: Bar<T>>(&mut self, x: U) {
| -- help: consider adding an explicit lifetime bound `U: 'static`...
9 | self.data.push(Box::new(x));
| ^^^^^^^^^^^
|
note: ...so that the type `U` will meet its required lifetime bounds
--> src/main.rs:9:24
|
9 | self.data.push(Box::new(x));
| ^^^^^^^^^^^
Specifically, the compiler is letting you know that it's possible that some arbitrary type U might contain a reference, and that reference could then become invalid:
impl<'a, T> Bar<T> for &'a str {}
fn main() {
let mut foo = Foo { data: vec![] };
{
let s = "oh no".to_string();
foo.add(s.as_ref());
}
}
That would be Bad News.
Whether you want a 'static lifetime or a parameterized lifetime is up to your needs. The 'static lifetime is easier to use, but has more restrictions. Because of this, it's the default when you declare a trait object in a struct or a type alias:
struct Foo<T> {
data: Vec<Box<dyn Bar<T>>>,
// same as
// data: Vec<Box<dyn Bar<T> + 'static>>,
}
However, when used as an argument, a trait object uses lifetime elision and gets a unique lifetime:
fn foo(&self, x: Box<dyn Bar<T>>)
// same as
// fn foo<'a, 'b>(&'a self, x: Box<dyn Bar<T> + 'b>)
These two things need to match up.
struct Foo<'a, T> {
data: Vec<Box<dyn Bar<T> + 'a>>,
}
impl<'a, T> Foo<'a, T> {
fn add<U>(&mut self, x: U)
where
U: Bar<T> + 'a,
{
self.data.push(Box::new(x));
}
}
or
struct Foo<T> {
data: Vec<Box<dyn Bar<T>>>,
}
impl<T> Foo<T> {
fn add<U>(&mut self, x: U)
where
U: Bar<T> + 'static,
{
self.data.push(Box::new(x));
}
}
asking me to consider adding a 'static lifetime qualifier (E0310). I am 99% sure that's not what I want, but I'm not exactly sure what I'm supposed to do.
Yes it is. The compiler does not want a &'static reference, it wants U: 'static.
Having U: 'static means that U contains no references with a lifetime less than 'static. This is required because you want to put a U instance in a structure without lifetimes.
trait Bar<T> {}
struct Foo<T> {
data: Vec<Box<dyn Bar<T>>>,
}
impl<T> Foo<T> {
fn add<U: Bar<T> + 'static>(&mut self, x: U) {
self.data.push(Box::new(x));
}
}

Confused about forwarding a reference through multiple functions

I'm having trouble understanding how references get forwarded through functions. The following scenario seems to compile as expected:
trait Trait {}
struct ImplementsTrait {}
impl Trait for ImplementsTrait {}
fn foo(t: &mut Trait) {
// ... use the mutable reference
}
fn forward(t: &mut Trait) {
foo(t); // forward the type '&mut Trait' to foo
}
fn main() {
let mut t = ImplementsTrait{};
forward(&mut t); // need to pass as reference because Trait has no static size
}
However, in using the API for the capnp crate, I get unexpected behavior:
fn parse_capnp(read: &mut BufRead) {
let reader = serialize_packed::read_message(read, message::ReaderOptions::new());
Ok(())
}
fn main() {
// ... ///
let mut br = BufReader::new(f);
parse_capnp(&mut br);
Ok(())
}
error[E0277]: the trait bound `std::io::BufRead: std::marker::Sized` is not satisfied
--> src/main.rs:18:16
|
18 | let reader = serialize_packed::read_message(read, message::ReaderOptions::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::io::BufRead` does not have a constant size known at compile-time
The signature of read_message is:
pub fn read_message<R>(
read: &mut R,
options: ReaderOptions
) -> Result<Reader<OwnedSegments>>
where
R: BufRead,
It appears that read is getting passed by value when it is a &mut BufRead and read_message is expecting a &mut BufRead. The only way to get this snippet to compile for me is changing this to:
fn parse_capnp(mut read: &mut BufRead) {
let reader = serialize_packed::read_message(&mut read, message::ReaderOptions::new());
Ok(())
}
I believe I am missing something simple about the types here. To me, this appears to pass a &mut &mut BufRead, which is not the expected type, but compiles.
Could someone add clarity to the types of read and t for the two examples?
I've looked at the following threads:
Use of mut in function signature
What are Rust's exact auto-dereferencing rules?
For the first thread, I'd say the comparison to C-style pointers is faulty due to the dereferencing rules that Rust applies.
Creating a Minimal, Complete, and Verifiable example that reproduces the problem is a useful step:
use std::io::BufRead;
pub fn read_message<R>(read: &mut R)
where
R: BufRead,
{}
fn parse_capnp(read: &mut BufRead) {
read_message(read);
}
fn main() {}
error[E0277]: the trait bound `std::io::BufRead: std::marker::Sized` is not satisfied
--> src/main.rs:9:5
|
9 | read_message(read);
| ^^^^^^^^^^^^ `std::io::BufRead` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `std::io::BufRead`
note: required by `read_message`
--> src/main.rs:3:1
|
3 | / pub fn read_message<R>(read: &mut R)
4 | | where
5 | | R: BufRead,
6 | | {}
| |__^
The error message is well covered in existing questions:
Why does a generic method inside a trait require trait object to be sized?
What does "Sized is not implemented" mean?
Working with trait objects requiring sized
Why is the `Sized` bound necessary in this trait?
TL;DR: trait objects aren't guaranteed to have a size, but generics have a Sized trait bound by default.
read is getting passed by value
Yes, everything in Rust is always passed by value. Sometimes that value happens to be a reference though.
read_message is expecting a &mut BufRead
It is not. It is expecting a generic type that implements the trait BufRead. These two signatures are different:
// Reference to a concrete type
pub fn read_message<R>(read: &mut R)
where
R: BufRead,
// Trait object
pub fn read_message<R>(read: &mut BufRead)
See also:
What is the difference between <T: Trait> Box<T> and &Trait / Box<Trait>?
What makes something a "trait object"?
a &mut &mut BufRead, which is not the expected type
It's a perfectly cromulent type. BufRead is implemented for any mutable reference to any type that implements BufRead itself:
impl<'a, B: BufRead + ?Sized> BufRead for &'a mut B
Besides, in this case you don't have a &mut &mut BufRead, you have a &mut &mut R. The concrete monomorphization for the types you've shown is actually a &mut &mut Bufreader.
You can fix it by :
changing the read_message function to accept unsized types. This is fine since R is always behind a pointer:
pub fn read_message<R>(read: &mut R)
where
R: ?Sized + BufRead,
changing the parse_capnp function to take a reference to a concrete type instead of a trait object:
fn parse_capnp<R>(read: &mut R)
where
R: BufRead,
{
read_message(read);
}
changing the parse_capnp function to take a concrete type instead of a trait object. You then need to take a reference to it yourself:
fn parse_capnp<R>(mut read: R)
where
R: BufRead,
{
read_message(&mut read);
}

Resources