In this case I want to read integers from standard input such that they are separated by spaces and newline. My first attempt was similar to the following code:
fn splitter(x: String) -> impl Iterator<Item=&'static str> {
x.as_str().split_whitespace()
}
fn valuereader<A: std::str::FromStr>() -> impl Iterator<Item=A>
where <A as std::str::FromStr>::Err: std::fmt::Debug
{
let a = std::io::stdin().lines();
let b = a.map(Result::unwrap);
let c = b.flat_map(splitter);
c.map(|x|x.parse().expect("Not an integer!"))
}
fn main() {
let temp: Vec<usize> = valuereader().collect();
println!("{:?}", temp);
}
The problem is that split_whitespace wants a &str, but std::io::stdin().lines() returns an owned String. I don't want to use x.as_str().split_whitespace().collect(), because I don't want to allocate a temporary vector.
The best solution I could come up with was to use a wrapper that contains the owned String and the iterator that depends on the String, using unsafe code. The wrapper's implementation of Iterator is simply a wrapper for the iterator that depends on the String. This was the result:
mod move_wrapper {
use std::pin::Pin;
pub fn to_wrapper<'b, A: 'b, F, B: 'b> (a: A, f: F) -> Wrapper<A,B>
where
F: FnOnce (&'b A) -> B
{
let contained_a = Box::pin(a);
// Here is the use of unsafe. It is necessary to create a reference to a that can live as long as long as needed.
// This should not be dangerous as no-one outside this module will be able to copy this reference, and a will live exactly as long as b inside Wrapper.
let b = f(unsafe{&*core::ptr::addr_of!(*contained_a)});
Wrapper::<A,B> {_do_not_use:contained_a, dependent:b}
}
pub struct Wrapper<A,B> {
_do_not_use: Pin<Box<A>>,
dependent: B
}
impl<A,B: Iterator> Iterator for Wrapper<A,B>
{
type Item = B::Item;
fn next(&mut self) -> Option<Self::Item> {
self.dependent.next()
}
}
}
fn splitter(x: String) -> impl Iterator<Item=&'static str> {
move_wrapper::to_wrapper(x, |a|a.as_str().split_whitespace())
}
fn valuereader<A: std::str::FromStr>() -> impl Iterator<Item=A>
where <A as std::str::FromStr>::Err: std::fmt::Debug
{
let a = std::io::stdin().lines();
let b = a.map(Result::unwrap);
let c = b.flat_map(splitter);
c.map(|x|x.parse().expect("Not an integer!"))
}
fn main() {
let temp: Vec<usize> = valuereader().collect();
println!("{:?}", temp);
}
Now to the actual question. How would you do this as idiomatic as possible, if possible without using any unsafe code (does the function here called to_wrapper exist)? Have I written safe unsafe code? Is there any way to make my Wrapper work for all traits, not just Iterator?
EDIT
To be clearer, this question is about creating a method you can apply anytime you have to give ownership to something that wants a reference, not about how to read from standard input and parse to integers.
You can't create self-referential types in safe rust, as explained here. Unfortunately, fixing this issue still leaves the next one.
Iterators that return items that can't outlive the iterator are impossible, explained here. Yours is even more restrictive: it's trying to create items that can't exist by the time the next item is fetched, which means you need a lending iterator to make this in its current state.
It would be quite easy to create your
Vecwith some nested for loops:However, this is not very instructive, and creates many temporary allocations, especially if you only need an iterator and not a
Vec.In order to make your original idea work idiomatically, you need an iterator that produces owned items. Fortunately, your final item type is
usize(or anything that comes out ofparse, which has to be owned), so you can create an iterator that creates those. This only allocates oneString, which will grow to the length of the longest line. (playground)This could be made more efficient by using
fill_bufandconsumeto only read one word at a time, shortening the max length of theString. It would also be sensible to report errors instead of unwrapping.