2014-12-13 2 views
1

Rust slices в настоящее время не поддерживают некоторые методы итератора, то есть take_while. Каков наилучший способ реализовать 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

ответ

3

Прежде всего, этот вопрос у вас есть, что 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' как показано здесь, которое симметрично со строками.

+0

Человек, отличный ответ! В то время, когда мне потребовалось, чтобы я сидел здесь и подозревал, что ответ может быть чем-то вроде этого, вы фактически внедрили его. Возможно, iter() на срезах может вместо этого возвращать подклассы, содержащие один элемент за раз, и тогда у вас может быть что-то вроде сбора, который повторно объединяет избыточные подклассы? –

+0

@AndrewWagner: Честно говоря, я просто не знаю, что здесь может быть API. –

+0

Все еще просто spitballing, но если моя общая идея работает, возможно, все, что вам нужно, это каждый()? сгенерировать итератор, а может быть, splice() объединить все вместе? возможно, сращивание возвращает итератор срезов для несмежных результатов ... –

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. Я все равно размещаю это, так как это может быть путь к решению более общей проблемы использования функций итератора на срезах чисто.

Смежные вопросы