Read from a Bytes


#1

I have some code which uses Read implementations with signature:

pub fn expand<IN>(read: IN, ...) -> ...
    where IN: Read
{ ... }

(I have omitted irrelevant bits.)

I was invoking this in two ways: once with files (using a file reader) and once with a byte iterator (Bytes). [Actually I was going from a string, so there is a simpler solution in my case. But the problem still remains.]

In the byte-iterator case I couldn’t find a converter to a Read implementation from the Bytes type (which is an iterator) so I did this:

expand(some_byte_iterator.collect::<Vec<u8>>().as_slice(), ...)

which worked because there is a Read implementation for &[u8]. However this seems very “non-streamy” to me, so I tried to create a simple Read implementation for Bytes (which is a byte iterator from strings). I can’t do this directly (because of the “coherence” rules) so I invented an intermediate type to hang the Read implementation on.

Here is what I defined (in a sub-module):

use std::str::Bytes;
use std::io::{Result, Read};

pub struct ByteReader<'a>(Bytes<'a>);

impl<'a> Into<ByteReader<'a>> for Bytes<'a>
{
    fn into(self) -> ByteReader<'a>
    {
        ByteReader(self)
    }
}

impl<'a> Read for ByteReader<'a>
{
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>
    {
        let len = buf.len();
        for i in 0..len {
            match self.0.next() {
                Some(byte) => buf[i] = byte,
                None => return Ok(i)
            }
        }
        Ok(len)
    }
}

(Please forgive the slightly idiosyncratic layout; I prefer this line-spacing and separation of generic constraints for (my) readability.)

The I tried to use this as follows:

expand(some_string.bytes().into(), ...)

(I now know I could write some_string.as_bytes(), but peace.)

The compiler rejects this with this message:

error[E0282]: type annotations needed
  --> src/generate/mod.rs:65:5
   |
65 |     expand(some_string.bytes().into(), ...)
   |     ^^^^^^ cannot infer type for `IN`

Fair enough, I’m presuming that I need annotations on into(). But I cannot convince the compiler with any annotations I try.

expand(some_string.bytes().into::<ByteReader>(), ...)

gives this error:

error[E0087]: too many type parameters provided: expected at most 0 type parameters, found 1 type parameter
  --> src/generate/mod.rs:65:40
   |
65 |     expand(some_string.bytes().into::<ByteReader>(), ...)
   |                                       ^^^^^^^^^^ expected 0 type parameters

Which I don’t understand at all.

What annotations would convince the compiler of what I want?

Note added after comment

It has been pointed out that my explicit problem can be solved by going directly from my string to a slice &[u8], which would then be known as implementing Read; et voila. But I still think there is a question to answer.


#2

You can use as_bytes()

https://doc.rust-lang.org/std/primitive.str.html#method.as_bytes


#3

Of course; thank you.

That solves my problem; but doesn’t answer my question about the compiler’s message.

I think I’ll tweak it a bit.


#4

into is not a generic method - it returns a generic type that’s defined on the Into trait. So the way to express this with annotations here is expand::<ByteReader>(some_string.bytes().into())


#5

.into::<ByteReader>()

This syntax is for into<T>() method (one type has many versions of this method), but the actual into method is T::into() with no parameters (many types have one version of this method).


#6

Thank you; that explains it. I got the wrong implication from the compiler message.

I think the compiler message could be a little more helpful here — like telling me exactly what part of the expression could be annotated — what do you think?


#7

To be fair, it did put the caret underline right where the annotation goes :slight_smile:. Since expand is the generic function that needs to resolve the type, I think it’s as good as it gets, short of suggesting the exact code.


#8

Just a small addition: Because the From and the Into traits are dual to each other, one could technically also write:

expand(ByteReader::from(some_string.bytes()), ...)

Which of these many ways to annotate the type one actually uses, seems to be a matter of preference, as far as I can tell.


#9

@vitalyd Yeah, you’re right. My misdirection. Thanks again.
@troiganto I wanted to avoid wrapping the entire expression so I could put the conversion in a chain.
The syntax:

expand(some_bytes_object.Into::<ByteReader>::into(), ...);

is not accepted; though expand(Into::<ByteReader>::into(some_bytes_object), ...) is.

I still don’t know why the compiler couldn’t infer the into() I need to use here to match the Read parameter. I guess ByteReader is too far away from Read.

Not accepting the qualified Into::<ByteReader>::into() syntax in the chain invocation is a bit annoying, especially when let to = Into::<ByteReader>::into; is fine (but I cannot use to with the chain notation either).

Even some_bytes_object.(Into::<ByteReader>::into)() doesn’t work.

Still, although there isn’t a built-in transformer of byte iterators to Read implementations (and I can’t seem to fashion one that I can use in a chain) I’m reasonably content to leave it at that.

Thank you all for your patience.