如何使用Rust的Peekable?

tro*_*ine 9 rust

我有兴趣在角色流中偷看.根据我的理解,Peekable将是最佳选择.我不能弄清楚如何使用它.

第一次尝试:

fn trawl<I, E>(pk: &mut I) where I: std::iter::Peekable<Result<char, E>> {
    loop {
        let cur = pk.next();
        let nxt = pk.peek();
        match (cur, nxt) {
            (Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
            _ => (),
        }
    }
}

fn main() {
    trawl(&mut std::io::stdio::stdin().chars());
}
Run Code Online (Sandbox Code Playgroud)

这无法编译

> rustc /tmp/main.rs
/tmp/main.rs:1:37: 1:73 error: `std::iter::Peekable` is not a trait
/tmp/main.rs:1 fn trawl<I, E>(pk: &mut I) where I: std::iter::Peekable<Result<char, E>> {
                                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Run Code Online (Sandbox Code Playgroud)

好的,公平的.我还没有完全理解特征所以我尝试传递一个迭代器,然后创建一个可窥探的版本:

fn trawl<I, E>(it: &mut I) where I: Iterator<Result<char, E>> {
    let mut pk = it.peekable();
    loop {
        let cur = pk.next();
        let nxt = pk.peek();
        match (cur, nxt) {
            (Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
            _ => (),
        }
    }
}

fn main() {
    trawl(&mut std::io::stdio::stdin().chars().peekable());
}
Run Code Online (Sandbox Code Playgroud)

这失败了

> rustc /tmp/main.rs
/tmp/main.rs:2:18: 2:20 error: cannot move out of dereference of `&mut`-pointer
/tmp/main.rs:2     let mut pk = it.peekable();
                                ^~
/tmp/main.rs:7:65: 7:70 error: cannot move out of dereference of `&`-pointer
/tmp/main.rs:7             (Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
                                                                               ^~~~~
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
/tmp/main.rs:7:39: 7:77 note: expansion site
error: aborting due to 2 previous errors
Run Code Online (Sandbox Code Playgroud)

有人能解释一下:

  • 为什么Peekable因为缺乏特性而无法出现在函数类型中,
  • 什么编译器的意思是什么时候它说'退出'取消引用'和
  • 我怎么解决其中一个或两个?

第三个版本

fn trawl<I, E>(mut it: I) where I: Iterator<Result<char, E>> {
    let mut pk = it.peekable();
    loop {
        let cur = pk.next();
        let nxt = pk.peek();
        match (cur, nxt) {
            (Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
            // (Some(i), ) => println!("{}", i.ok()),
            _ => (),
        }
    }
}

fn main() {
    trawl(std::io::stdio::stdin().chars().peekable());
}
Run Code Online (Sandbox Code Playgroud)

这失败了:

> rustc /tmp/main.rs
/tmp/main.rs:7:65: 7:70 error: cannot move out of dereference of `&`-pointer
/tmp/main.rs:7             (Some(i), Some(nxt_i)) => println!("{} {}", i.ok(), nxt_i.ok()),
                                                                               ^~~~~
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
/tmp/main.rs:7:39: 7:77 note: expansion site
error: aborting due to previous error
Run Code Online (Sandbox Code Playgroud)

我不明白生锈在这里对我说什么,Iterator.next将如何从Peekable.peek获得不同的返回类型.

Chr*_*gan 7

Peekable不是一个特征,因此不能用作约束,这表明它可能意味着许多类型中的一种.它是一种单一的,具体的,具体的类型struct Peekable<A, T>.正如您所观察到的,它是通过peekable()在迭代器上调用该方法来构造的,该迭代器将其更改为可窥探的内容.

如果您只是想使用迭代器,那么您可以使用它:

fn trawl<I, E>(iter: I) where I: Iterator<Result<char, E>> {
    let pk = pk.peekable();
    …
}
Run Code Online (Sandbox Code Playgroud)

另请注意,该peekable()方法采用自值; 你不能在那里对一个迭代器进行可变引用.

你所瞄准的另一种选择,但我通常不太倾向于这种选择,就是要求论证是可以窥视的,把负担放在打电话上,就像你有:

fn trawl<I, E>(pk: Peekable<E, I>) where I: Iterator<Result<char, E>> {
    …
}
Run Code Online (Sandbox Code Playgroud)

  • 注意未来的读者:当这个答案写完时,[`stdin()`很难安全使用](https://github.com/rust-lang/rust/issues/14434).然而,情况已不再如此,因为该问题已于2014年12月修复. (2认同)

小智 6

Peekable实际上是一个结构,而不是一个特征.如果你想要a Peekable,你可以像这样定义你的函数:

fn trawl<E, I>(it: Peekable<I>) where I: Iterator<Result<char, E>> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

你的第二个实施无法编译,因为peek需要self通过值(即它消耗的迭代器,返回一个新的),这样你就可以不通过调用它&mut的参考.大多数代码只是通过值而不是引用来获取迭代器:

fn trawl<E, I>(it: I) where I: Iterator<Result<char, E>> {
    let it = it.peekable();
    ...
}
Run Code Online (Sandbox Code Playgroud)

如果您不想将迭代器移动到类似函数中trawl,则可以使用该by_ref()方法创建一个保留在&mut引用上的新迭代器:

let mut my_iterator = /* whatever */;
trawl(my_iterator.by_ref());
// my_iterator is still usable here
Run Code Online (Sandbox Code Playgroud)

就风格而言,我会说第二种形式是更好的方式,因为第一种方式泄漏的基本上是一个实现细节.


Jer*_*emy 5

自之前的答案以来,Rust 发生了一些变化。现在的做法是:

\n\n
fn trawl<I, E>(pk: Peekable<I>) \nwhere I: Iterator<Item = Result<char, E>> {\n    \xe2\x80\xa6\n}\n
Run Code Online (Sandbox Code Playgroud)\n