如何在片上实现相当于take_while?

时间:2014-12-13 17:58:00

标签: rust slice

Rust slice当前不支持某些迭代器方法,即take_while。实现切片的take_while的最佳方法是什么?

const STRHELLO:&'static[u8] = b"HHHello";

fn main() {
    let subslice:&[u8] = STRHELLO.iter().take_while(|c|(**c=='H' as u8)).collect();
    println!("Expecting: {}, Got {}",STRHELLO.slice_to(3),subslice);
    assert!(subslice==STRHELLO.slice_to(3));
}

导致错误:

<anon>:6:74: 6:83 error: the trait `core::iter::FromIterator<&u8>` is not implemented for the type `&[u8]`

围栏中的此代码: http://is.gd/1xkcUa

2 个答案:

答案 0 :(得分:4)

首先,您遇到的问题是collect是关于创建集合,而切片是关于引用现有数组中连续范围的项目(无论是是否动态分配。

我担心由于特征的性质,原始容器(STRHELLO)是一个连续范围的事实已经丢失,并且在事后不能重建。我也担心任何使用“通用”迭代器都不能导致所需的输出;类型系统必须以某种方式承载以下事实:

  • 原始容器是一个连续的范围
  • 到目前为止执行的操作链保存了这个属性

这可能是可行的,但我现在没有看到它完成,我不确定它可以优雅地实现。


另一方面,你可以用自己动手的方式解决这个问题:

fn take_while<'a>(initial: &'a [u8], predicate: |&u8| -> bool) -> &'a [u8] { // '
    let mut i = 0u;
    for c in initial.iter() {
        if predicate(c) { i += 1; } else { break; }
    }
    initial.slice_to(i)
}

然后:

fn main() {
    let subslice: &[u8] = take_while(STRHELLO, |c|(*c==b'H'));
    println!("Expecting: {}, Got {}",STRHELLO.slice_to(3), subslice);
    assert!(subslice == STRHELLO.slice_to(3));
}

注意:'H' as u8可以重写为b'H',如此处所示,与字符串对称。

答案 1 :(得分:0)

通过一些重型体操可以使用股票迭代器来实现这个功能:

use std::raw::Slice;
use std::mem::transmute;

/// Splice together to slices of the same type that are contiguous in memory.
/// Panics if the slices aren't contiguous with "a" coming first.
/// i.e. slice b must follow slice a immediately in memory.
fn splice<'a>(a:&'a[u8], b:&'a[u8]) -> &'a[u8] {
    unsafe {
        let aa:Slice<u8> = transmute(a);
        let bb:Slice<u8> = transmute(b);
        let pa = aa.data as *const u8;
        let pb = bb.data as *const u8;
        let off = aa.len as int; // Risks overflow into negative!!!
        assert!(pa.offset(off) == pb, "Slices were not contiguous!");
        let cc = Slice{data:aa.data,len:aa.len+bb.len};
        transmute(cc)
    }
}

/// Wrapper around splice that lets you use None as a base case for fold
/// Will panic if the slices cannot be spliced!  See splice.
fn splice_for_fold<'a>(oa:Option<&'a[u8]>, b:&'a[u8]) -> Option<&'a[u8]> {
   match oa {
       Some(a) => Some(splice(a,b)),
       None => Some(b),
   }
}

/// Implementaton using pure iterators
fn take_while<'a>(initial: &'a [u8], 
                   predicate: |&u8| -> bool) -> Option<&'a [u8]> {
    initial
        .chunks(1)
        .take_while(|x|(predicate(&x[0])))
        .fold(None, splice_for_fold)
}

用法:

const STRHELLO:&'static[u8] = b"HHHello";
let subslice: &[u8] = super::take_while(STRHELLO, |c|(*c==b'H')).unwrap();
println!("Expecting: {}, Got {}",STRHELLO.slice_to(3), subslice);
assert!(subslice == STRHELLO.slice_to(3));
如果你只需要take_while,那么Matthieu的实现就更清晰了。无论如何我发布这个,因为它可能是解决在切片上使用迭代器函数的更一般问题的一条道路。