提问人:true equals false 提问时间:4/12/2023 最后编辑:true equals false 更新时间:4/12/2023 访问量:77
是否有任何 rust 函数用于包装依赖于引用的迭代器,以便包装器包含引用?
Are there any rust functions for wrapping an iterator that is dependent on a reference so the wrapper contains the referent?
问:
在这种情况下,我想从标准输入中读取整数,以便它们用空格和换行符分隔。我的第一次尝试类似于以下代码:
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);
}
问题是想要一个 ,但返回一个拥有的 .我不想使用 ,因为我不想分配临时向量。split_whitespace
&str
std::io::stdin().lines()
String
x.as_str().split_whitespace().collect()
我能想到的最好的解决方案是使用一个包装器,其中包含拥有的和依赖于 的迭代器,使用不安全的代码。包装器的实现只是依赖于 的迭代器的包装器。结果是这样的:String
String
Iterator
String
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);
}
现在来谈谈实际问题。如果可能的话,您将如何在不使用任何不安全代码的情况下尽可能惯用地做到这一点(这里调用的函数是否存在)?我是否编写了安全不安全的代码?有没有办法让我的工作适合所有特征,而不仅仅是?to_wrapper
Wrapper
Iterator
编辑
更清楚地说,这个问题是关于创建一个方法,你可以随时应用,将所有权授予需要引用的东西,而不是关于如何从标准输入读取并解析为整数。
答:
您无法在安全 rust 中创建自引用类型,如此处所述。不幸的是,解决此问题仍然会留下下一个问题。
返回不能超过迭代器的项的迭代器是不可能的,此处对此进行了解释。你的限制性更强:它试图创建在获取下一个项目时不存在的项目,这意味着你需要一个借用迭代器才能使其处于当前状态。
使用一些嵌套的 for 循环创建你的是非常容易的:Vec
fn valuereader<A: FromStr>() -> Vec<A>
where
A::Err: Debug,
{
let mut v = Vec::new();
for line in std::io::stdin().lines() {
for word in line.unwrap().split_whitespace() {
let a = word.parse().unwrap();
v.push(a);
}
}
v
}
但是,这并不是很有启发性,并且会创建许多临时分配,尤其是当您只需要一个迭代器而不是 .Vec
为了使你的原始想法以惯用的方式工作,你需要一个迭代器来生成拥有的项。幸运的是,你的最终项目类型是(或任何必须拥有的 ,所以你可以创建一个迭代器来创建这些。这只会分配一个 ,它将增长到最长行的长度。(游乐场)usize
parse
String
use std::fmt::Debug;
use std::io::BufRead;
use std::marker::PhantomData;
use std::str::FromStr;
#[derive(Debug, Clone)]
struct ValueReader<B, V> {
// The underlying BufRead
buffer: B,
// The current line being read
line: String,
// The current offset into the current line
index: usize,
// The type being parsed
_value_type: PhantomData<V>,
}
impl<B, V> ValueReader<B, V> {
fn new(b: B) -> Self {
Self {
buffer: b,
line: String::new(),
index: 0,
_value_type: PhantomData,
}
}
fn value(&mut self) -> Option<V>
where
V: FromStr,
V::Err: Debug,
B: BufRead,
{
loop {
// Check if line is consumed, or the iterator just started
if self.line.is_empty() {
let bytes = self.buffer.read_line(&mut self.line).unwrap();
// Buffer is completely consumed
if bytes == 0 {
return None;
}
}
let unconsumed = self.line[self.index..].trim_start();
self.index = self.line.len() - unconsumed.len();
let Some(word) = unconsumed.split_whitespace().next() else {
// Line is consumed, reset to original state
self.index = 0;
self.line.clear();
continue;
};
self.index += word.len();
return Some(word.parse().unwrap());
}
}
}
impl<B, V> Iterator for ValueReader<B, V>
where
V: FromStr,
V::Err: Debug,
B: BufRead,
{
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
self.value()
}
}
通过使用并且一次只读取一个单词,缩短 .报告错误而不是解包也是明智的。fill_buf
consume
String
评论
上一个:标准库函数何时会引发异常?
下一个:Rascal MPL 重载库函数
评论
unsafe
unsafe
// SAFETY
lines()
String
read_line
Iterator
x.as_str().split_whitespace().collect()
Vec
String