Parameter as a String or a Stream

What should a parameter which can be a string or a stream look like?

What do you want to do with this parameter? I.e. what interface do you need? Have you considered Either? Or converting the string to a stream beforehand?

I want to be able to read it character by character.

You can convert a string to a Stream<Item = char> via futures::stream::iter(string.chars()). Example.

This seems fine, but it is not a part of the standard library.

What Stream trait are you using, if you are not using the futures library? There is no Stream trait in the standard library, only the unstable AsyncIterator.

1 Like

There is std::io::Read, but I do not know what I should ideally do.

You can use String::as_bytes to access the underlying byte slice &[u8], which implements Read. I wouldn't use Read to read a buffer "character by character" though, as this is non-trivial for UTF-8 scalar values, which is what chars in Rust are. I'd use Iterator<Item = char> instead.

1 Like

Can file streams be passed down as std::iter::Iterator before being read?

yes.

in rust, the API to read files is provided by the std::io::Read trait, which has a bytes() method that gives you an iterator. the iterator yields items of type Result<u8, std::io::Error>.

there's an example in the documentation of Read::bytes() you can take a look:

The problem with this is that it is different from std::fmt::Bytes that is what String::bytes returns.

of course it's different, every iterator is a distinct type. what's common is they all implement the Iterator trait,

if you want static dispatch, use a generic type with Iterator as bound. if you want dynamic dispatch, use a trait object.

however, since reading from a file is fallible due to the nature of doing IO, the associated Item type is different. it's up to you to decide how to handle the IO error while reading from the file.

for example, this example uses Result::unwrap so it would panic on error while reading the file:

// this function takes an iterator of bytes as argument
fn foo<I>(bytes: I) where I: Iterator<Item = u8> {
    for b in bytes {
        // do something with `b`
    }
}

fn main() {
    let s = String::from("hello world!");
    foo(s.bytes());

    use std::io::Read;
    let f = std::fs::File::open("/tmp/my_file.txt").expect("open file");
    foo(f.bytes().map(Result::unwrap));
}
1 Like