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.