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.
unsafecontext, that is, code that has been explicitly marked unsafe. All this to say that there is no "risk" of accidentally writing unsafe code.unsafein your code is hard to spot. You should at the least have// SAFETYcomment explaining why you think it's sound.lines()is slow because it has to allocate a newStringfor each line, it's better to useread_linedirectly. But of course, then you can't useIterator.x.as_str().split_whitespace().collect()is objectionable due to allocating a temporaryVec, presumably the allocation activity for aStringper line is also an issue. Mind you, this is almost certainly premature optimization; even on the fastest modern hard drives, I/O would almost certainly swamp any allocator overhead, so I'd just do the convenient thing until I had evidence it was causing problems.