Confused about forwarding a reference through multiple functions - rust

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);
}

Related

Rust expected dyn ToString, found f64

I am struggling with code to write containers to file. I have specified a trait bound of std::fmt::Display because any type implementing this has to_string(). f64 implements this trait, so I don't understand how this error could happen. I checked the rust issues on github and nothing appears to be there but I wanted to check my understanding before raising an issue.
type mismatch resolving `<Vec<f64> as IntoIterator>::Item == (dyn std::fmt::Display + 'static)`
expected trait object `(dyn std::fmt::Display + 'static)`
found type `f64` rustc (E0271)
Edit:
The ToString trait is what I actually tried first, and it gives the same error
type mismatch resolving `<Vec<f64> as IntoIterator>::Item == (dyn ToString + 'static)`
expected trait object `(dyn ToString + 'static)`
found type `f64` rustc (E0271)
Edit:
Added relevant code. I also have an error where the compiler complains about x not being a reference, which I haven't figured out and is why I didn't initially have the code in this question.
use std::error::Error;
// Write to a file
pub fn write_to_file<T>(data_line: T) -> Result<(), Box<dyn Error>>
where
T: IntoIterator<Item = dyn ToString> + len_trait::Len,
{
let file = std::fs::OpenOptions::new()
.write(true)
.create(true)
.append(true)
.open("test_data/data.tsv")
.unwrap();
let mut wtr = csv::WriterBuilder::new()
.delimiter(b'\t')
.quote_style(csv::QuoteStyle::NonNumeric)
.from_writer(file);
let mut data_line_str: Vec<String> = Vec::with_capacity(data_line.len());
// into_iter() is used instead of iter() because iter() doesn't have a trait
// and into_iter() consumes the reference, not the original object
data_line
.into_iter()
.for_each(|x| data_line_str.push(x.to_string()));
wtr.write_record(&data_line_str)?;
wtr.flush()?;
Ok(())
}
// Use assertions to check for problems
#[test]
fn test_write() -> Result<(), Box<dyn Error>>
{
let test: Vec<f64> = vec![1.423, 0.61324, 123.865];
write_to_file(test)?;
// Could use assert_eq! and open file and check matching
let _remove_success = std::fs::remove_file("test_data/data.tsv");
Ok(())
}
Edit:
I've tried changing the function definition, haven't changed to use collect yet because I'm trying to pick apart how this works, and I get this error... I thought I understood but now I'm confused again.
error[E0599]: no method named `len` found for type parameter `T` in the current scope
--> src/io.rs:22:71
|
4 | pub fn write_to_file<T, I>(data_line: T) -> Result<(), Box<dyn Error>>
| - method `len` not found for this type parameter
...
22 | let mut data_line_str: Vec<String> = Vec::with_capacity(data_line.len());
| ^^^ method not found in `T`
|
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `len`, perhaps you need to restrict type parameter `T` with one of them:
|
6 | T: IntoIterator<IntoIter = I> + ExactSizeIterator,
| +++++++++++++++++++
6 | T: IntoIterator<IntoIter = I> + FixedInitializer,
| ++++++++++++++++++
For more information about this error, try `rustc --explain E0599`.
Final edit:
I believe that I understand the issue. When I bound IntoIterator::IntoIter to ExactSizeIterator, that is specifying what the object that can be turned into an iterator (that is, implements IntoIterator) is turned into an ExactSizeIterator which at that point has a len(). So data_line may not have a len() method, but data_line.into_iter() would when I do as PitaJ suggests and use a type signature like
pub fn write_to_file<T, I>(data_line: T) -> Result<(), Box<dyn Error>>
where
T: IntoIterator<IntoIter = I>,
I: ExactSizeIterator,
<I as Iterator>::Item: ToString,
T: IntoIterator<Item = dyn ToString> + len_trait::Len,
What this says is "I expect T to be able to be converted into an iterator containing some unknown type of trait objects for ToString. That's actually a pretty complicated iterator type, and since dyn ToString is unsized, I'm not entirely confident you can ever have any valid instance of this type.
In most reasonable use cases for this function, we know the type of the iterator at compile time, so you're looking for generics, not trait objects. Consider
pub fn write_to_file<T, I>(data_line: T) -> Result<(), Box<dyn Error>>
where
T: IntoIterator<Item = I> + len_trait::Len,
I: ToString

Why do I get the error "the trait `Iterator` is not implemented" for a reference to a generic type even though it implements IntoIterator?

In the following example, MyTrait extends IntoIterator but the compiler doesn't recognize it when used in a loop.
pub trait MyTrait: IntoIterator<Item = i32> {
fn foo(&self);
}
pub fn run<M: MyTrait>(my: &M) {
for a in my {
println!("{}", a);
}
}
I get the error:
error[E0277]: `&M` is not an iterator
--> src/lib.rs:6:14
|
6 | for a in my {
| ^^ `&M` is not an iterator
|
= help: the trait `Iterator` is not implemented for `&M`
= note: required because of the requirements on the impl of `IntoIterator` for `&M`
= note: required by `into_iter`
Only M implements IntoIterator, but you're trying to iterate over a &M, which doesn't have to.
It's not clear what you hope to achieve with run, but removing the reference might be a start:
pub fn run<M: MyTrait>(my: M) {
for a in my {
println!("{}", a);
}
}
Note that M itself may be (or contain) a reference, so writing it in this way doesn't mean you can't use it with borrowed data. Here's one way to use run to iterate over a &Vec (playground):
impl<I> MyTrait for I
where
I: IntoIterator<Item = i32>,
{
fn foo(&self) {}
}
fn main() {
let v = vec![10, 12, 20];
run(v.iter().copied());
}
This uses .copied() to turn an Iterator<Item = &i32> to an Iterator<Item = i32>.
Related questions
Why is a borrowed range not an iterator, but the range is?
Am I incorrectly implementing IntoIterator for a reference or is this a Rust bug that should be reported?
How to implement Iterator and IntoIterator for a simple struct?
What is an idiomatic way to collect an iterator of &T into a collection of Ts?
How to properly pass Iterators to a function in Rust

implementing traits for dyn Fns

Today I was playing around with function traits. Though the example I show below might not practically be very useful, I do wonder why it doesn't compile.
pub fn do_something(o: &(dyn Other + 'static)) {
}
trait Other {
fn do_something_other(&self);
}
impl<A> Other for dyn Fn(A) {
fn do_something_other(&self) {
do_something(self);
}
}
Here I implement a trait for a function type. This function type is generic over it's parameter. This means that if you were to do it like this:
pub fn do_something(o: &(dyn Other + 'static)) {
}
trait Other {
fn do_something_other(&self);
}
impl<F, A> Other for F where F: (Fn(A)) + 'static {
fn do_something_other(&self) {
do_something(self);
}
}
you get an error stating a type parameter is unconstrained.
I get this and don't believe it's possible to do it with generics. But the dynamic approach, why doesn't it work? It gives the following error:
I don't understand this error. It states I pass a Fn(A) -> (), which doesn't implement Other. However, this error occurs literally in the implementation of Other. How can it not be implemented here?
My first thought was because each closure is its own type. If it has to do with this, I find the error very weird.
The first construction fails because you cannot convert a &dyn A into a &dyn B, even when implementing B for dyn A.
trait A {}
trait B {
fn do_thing(&self);
}
impl B for dyn A {
fn do_thing(&self) {
let b: &dyn B = self;
}
}
error[E0308]: mismatched types
--> src/lib.rs:9:25
|
9 | let b: &dyn B = self;
| ------ ^^^^ expected trait `B`, found trait `A`
| |
| expected due to this
|
= note: expected reference `&dyn B`
found reference `&(dyn A + 'static)`
Well, you can convert traits but only with help from the source trait. But since in this case the source is Fn, that's not a route.
The second construction fails because Rust won't let you implement traits that can conflict. Trying to implement B for a type that implements A<_> will automatically be rejected because types can have multiple implementations of A<_>.
trait A<T> {}
trait B {
fn do_thing(&self);
}
impl<T, U> B for T where T: A<U> {
fn do_thing(&self) {}
}
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:7:9
|
7 | impl<T, U> B for T where T: A<U> {
| ^ unconstrained type parameter
Regarding Fns in particular, its somewhat hard to tell since usually function objects only implement a single Fn trait. However, the keyword is usually since you can enable a feature on nightly to do just that. And the trait system usually doesn't play favorites.
So what can you do? Well the first method is still functional, just you have to keep the implementation within the trait. You can use the second method if you use a concrete types for the function arguments.
You can conceivably implement Other for &dyn Fn(_) (implementing it on the reference and not the object itself). But that's not particularly convenient with how Fn objects are usually used.
pub fn do_something(o: &dyn Other) {}
trait Other {
fn do_something_other(&self);
}
impl<A> Other for &dyn Fn(A) {
fn do_something_other(&self) {
do_something(self);
}
}
fn main() {
// THIS WORKS
let closure: &dyn Fn(_) = &|x: i32| println!("x: {}", x);
closure.do_something_other();
// THIS DOESN'T WORK
// let closure = |x: i32| println!("x: {}", x);
// closure.do_something_other();
}
Another option would be to make the Other trait generic in order to constrain A, but that of course depends on how its designed to be used.

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

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

Rust generic AddAssign with references

A while ago I looked into writing a generic iterator for the Fibonacci sequence
that could accept both primitive numbers as well as custom types (such as
bignums). After failing to get a version working for both the primitive types
and bignums, I stumbled upon this question:
How to write a trait bound for adding two references of a generic type?
Which used so called Higher Ranked Trait Bounds to solve the problem with this
particular issue.
Now however, I'm trying to use a similar strategy to use the *_assign
operators instead. In particular, I'm trying to get something similar to this
working:
use std::ops::{Add, AddAssign};
fn add_test<'a, T>(x: &'a T, y: &'a T) -> T
where
for<'b> &'b T: Add<Output = T>,
{
x + y
}
fn add_assign_test<'a, T>(x: &'a mut T, y: &'a T) -> T
where
for<'b> &'b mut T: AddAssign<&'b T>,
T: Clone,
{
x += y;
x.clone()
}
fn main() {
println!("add_test()={}", add_test(&1, &2));
println!("add_assign_test()={}", add_assign_test(&mut 2, &2));
}
add_test() works as expected but I'm unable to get add_assign_test() to work in a similar way. The errors I'm getting suggest that there might not actually exist an implementation for this kind of behaviour on the primitive types:
error[E0277]: the trait bound `for<'b> &'b mut _: std::ops::AddAssign<&'b _>` is not satisfied
--> src/main.rs:21:38
|
21 | println!("add_assign_test()={}", add_assign_test(&mut 2, &2));
| ^^^^^^^^^^^^^^^ no implementation for `&'b mut _ += &'b _`
|
= help: the trait `for<'b> std::ops::AddAssign<&'b _>` is not implemented for `&'b mut _`
= note: required by `add_assign_test`
I could create a macro that creates implementations for these operators that actually takes references to the primitive types, but that seems a little wasteful. Is there any other way to achieve the same effect?
Just a tiny oversight in your code. Let's look at the trait:
pub trait AddAssign<Rhs = Self> {
fn add_assign(&mut self, rhs: Rhs);
}
The receiver of the method is already &mut self and not self. The reason that you had to do the extra work with Add is because it accepts self as receiver. For AddAssign this means: if a type T implements AddAssign, you can call the method add_assign() on a &mut T!
Thus, instead of writing:
where for <'b> &'b mut T: AddAssign<&'b T>,
... you would write:
where for <'b> T: AddAssign<&'b T>,
(No other line changed so far)
However, you notice that the code still won't compile:
error[E0277]: the trait bound `for<'b> {integer}: std::ops::AddAssign<&'b {integer}>` is not satisfied
--> src/main.rs:13:38
|
13 | println!("add_assign_test()={}", add_assign_test(&mut 2, &2));
| ^^^^^^^^^^^^^^^ no implementation for `{integer} += &'b {integer}`
|
= help: the trait `for<'b> std::ops::AddAssign<&'b {integer}>` is not implemented for `{integer}`
= note: required by `add_assign_test`
The reason is simple: there is simply no implementation of AddAssign for primitive types which takes an immutable reference as rhs (Docs). I don't know if this is an oversight -- it could be worth opening an issue on the Rust repo.
To verify the above code works, I write my own type and implemented AddAssign appropriately: Playground.
Corrected the code snippet based on Lukas' reply:
use std::ops::{Add, AddAssign};
fn add_test<'a, T>(x: &'a T, y: &'a T) -> T
where
for<'b> &'b T: Add<Output = T>,
{
x + y
}
fn add_assign_test<'a, T>(x: &'a mut T, y: &'a T) -> T
where
for<'b> T: AddAssign<&'b T>,
T: Clone,
{
*x += y;
x.clone()
}
fn main() {
println!("add_test()={}", add_test(&1, &2));
println!("add_assign_test()={}", add_assign_test(&mut 2, &2));
}
It appears that this is an issue in Rust itself. There is currently a tracker for this issue.
Until this is fixed, there's two possible workarounds:
Create named-tuples for each primitives and implement OpAssign for those types instead. This does however force you to 'cast' all primitives to your custom type.
Duplicate the 'generic' code with specializations for the primitives.

Resources