Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return a map iterator which is using a closure in Rust [duplicate]

Tags:

rust

I'm learning some Rust solving the Matasano Crypto Challenges and am stuck with implementing the Ceasar cipher ("single-byte xor") as an iterator. My function is supposed to look like this:

fn ceaser_cipher_iter(data: &Vec<u8>, key :u8) -> SomeType {
    data.iter().map(move |&p| p^key)
}

Replacing SomeType with (), the compiler tells me what type it expects: core::iter::Map<core::slice::Iter<'_, u8>, [closure src/main.rs:59:21: 59:31]>. After some headbanging I found out I could use std::slice::Iter<u8> for core::slice::Iter<'_, u8>, which leaves the closure. As far as I understand, my naive experiment to use

fn ceaser_cipher_iter(data: &Vec<u8>, key :u8) -> Map<std::slice::Iter<u8>, fn(&u8)->u8 > {
    data.iter().map(move |&p| p^key)
}

cannot work, because Rust needs to know the exact closure type to allocate the memory for the closure (key has to be stored by being moved into the closure). I've tried to follow the advice to use a Box instead:

fn ceaser_cipher_iter(data: &Vec<u8>, key :u8) -> Map<std::slice::Iter<u8>, Box<Fn(&u8)->u8> > {
    data.iter().map(Box::new(move |&p| p^key))
}

But afaict map does not support it:

src/main.rs:59:17: 59:47 error: the trait `core::ops::FnMut<(&u8,)>` is not implemented for the type `Box<for<'r> core::ops::Fn(&'r u8) -> u8>` [E0277]
src/main.rs:59     data.iter().map(Box::new(move |&p| p^key))
                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:59:17: 59:47 error: the trait `core::ops::FnOnce<(&u8,)>` is not implemented for the type `Box<for<'r> core::ops::Fn(&'r u8) -> u8>` [E0277]
src/main.rs:59     data.iter().map(Box::new(move |&p| p^key))
                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Is there a way to return a map iterator with a closure?

like image 906
Perseids Avatar asked Aug 09 '15 13:08

Perseids


1 Answers

The trick is not to box the closure, but the iterator as a whole.

fn ceaser_cipher_iter<'a>(data: &'a Vec<u8>, key: u8) -> Box<dyn Iterator<Item=u8> + 'a> {
    Box::new(data.iter().map(move |&p| p.key))
}

Note that because the iterator uses a borrow, I had to add lifetime annotations so that the code would pass borrow checking.

like image 166
DK. Avatar answered Oct 23 '22 07:10

DK.