【问题标题】:How to use Rust's Peekable?如何使用 Rust 的 Peekable?
【发布时间】:2014-10-13 05:30:06
【问题描述】:

我有兴趣在字符流中窥视。据我了解,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());
}

编译失败

> 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

好的,很公平。我还没有完全理解特征,所以我尝试传入一个迭代器,然后创建一个可查看的版本:

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());
}

这失败了

> 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

谁能解释一下:

  • 为什么 Peekable 不能出现在函数类型中,因为缺少 trait,
  • 当编译器说“移出取消引用”和时是什么意思
  • 我如何解决其中一个或两个?

第三版

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());
}

这失败了:

> 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

我无法理解 rust 在这里对我说什么,Iterator.next 将如何具有与 Peekable.peek 不同的返回类型。

【问题讨论】:

    标签: rust


    【解决方案1】:

    Peekable 不是特征,因此不能用作界限,这表明它可能意味着多种类型中的一种。它是一个单一的、特定的、具体的类型,struct Peekable&lt;A, T&gt;。正如您所观察到的,它是通过在迭代器上调用 peekable() 方法来构造的,这会将其更改为可窥视的东西。

    如果你只是想使用一个迭代器,你会如何使用它:

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

    还要注意peekable() 方法按值获取self;你不能在那里对迭代器进行可变引用。

    替代方案是您的目标,但我通常不太倾向于,将要求参数是可窥视的,将负担放在调用者身上,就像您所做的那样:

    fn trawl<I, E>(pk: Peekable<E, I>) where I: Iterator<Result<char, E>> {
        …
    }
    

    【讨论】:

    【解决方案2】:

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

    fn trawl<E, I>(it: Peekable<I>) where I: Iterator<Result<char, E>> {
        ...
    }
    

    您的第二个实现无法编译,因为 peek 采用 self 的值(即它消耗迭代器,返回一个新的),所以你不能通过 @ 调用它987654326@ 参考。大多数代码只是通过值而不是引用来获取迭代器:

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

    如果您不想将迭代器移动到像 trawl 这样的函数中,可以使用 by_ref() 方法创建一个新的迭代器,该迭代器保留 &amp;mut 引用:

    let mut my_iterator = /* whatever */;
    trawl(my_iterator.by_ref());
    // my_iterator is still usable here
    

    就风格而言,我会说第二种形式是更好的选择,因为第一种形式基本上泄露了实现细节。

    【讨论】:

    • 如果迭代器是按值传递给“拖网”的,那么如何迭代它的内容,因为这会改变迭代器?我对在这里做的正确事情是实现 - 在精神上 - 我在第二个实现中的目标感到困惑。
    • 如果迭代器按值传递给trawl,则函数拥有它,并且可以对它做任何想做的事情。您可以执行 fn trawl(it: I) { let mut it = it; ... } 以允许在迭代器上进行迭代。还有一个在语义上完全相同的简写:fn trawl(mut it: I) { ... }
    【解决方案3】:

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

    fn trawl<I, E>(pk: Peekable<I>) 
    where I: Iterator<Item = Result<char, E>> {
        …
    }
    

    【讨论】:

      猜你喜欢
      • 2022-06-28
      • 1970-01-01
      • 2015-04-30
      • 1970-01-01
      • 2023-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-13
      相关资源
      最近更新 更多