dawid
dawid

Reputation: 788

How to apply reduce to chunks (coming from a borrowed reference to an array)?

How can I make use of functional patterns without incurring in borrowing problems? The solution proposed by the compiler leads to another error (expected array [u8; 3], found '&[u8]') and it goes on from one error to another different error, indefinitely. Some related code seem overly complicated for such a simple task like in this other question.

use reduce::Reduce;

/// Take an array representing a sequence of 3-tuples and fold it through an arbitrary sandwich logic.
fn sandwich(lst: &[u8])->[u8; 3]{
    lst.chunks(3).reduce(|x, y| [x[0], y[1], x[0]]).unwrap()
}
/*
3  |     lst.chunks(3).reduce(|x, y| [x[0], y[1], x[0]]).unwrap()
   |                                 ^^^^^^^^^^^^^^^^^^
   |                                 |
   |                                 expected `&[u8]`, found array `[u8; 3]`
   |                                 help: consider borrowing here: `&[x[0], y[1], x[0]]`
*/

The best compilable code I could write was this convoluted one, giving up reduce at all:

fn sandwich2(lst: &[u8])->[u8; 3]{
    let mut r: [u8; 3] = lst[..].try_into().unwrap();
    for i in (3..lst.len()).step_by(3) {
        let y = &lst[i..i + 3];
        r = [r[0], y[1], r[0]];
    }
    r
}

Please note that sandwich is just an example to illustrate the problem (that does nothing actually smart). I expect an external much more complex function instead of that lambda.

Upvotes: 1

Views: 389

Answers (1)

Netwave
Netwave

Reputation: 42708

You have to somehow take ownership of the values into a required [u8; 3].

Maybe as this example using the iterator_fold_self feature (as per today nightly):

#![feature(iterator_fold_self)]

/// Take an array representing a sequence of 3-tuples and reduce it through an arbitrary sandwich logic.
fn sandwich(lst: &[u8]) -> [u8; 3] {
    lst.chunks(3)
        .map(|x| [x[0], x[1], x[2]])
        .reduce(|x, y| [x[0], y[1], x[0]])
        .unwrap()
}

fn main() {
    let test_set = [1, 2, 3, 1, 2, 3];
    println!("{:?}", sandwich(&test_set));
}

Playground

You can use try_into(from this famous answer) to get an owned slice:

fn sandwich(lst: &[u8]) -> [u8; 3] {
    lst.chunks(3)
        .map(|x| -> [u8; 3] { x.try_into().unwrap() } )
        .reduce(|x, y| [x[0], y[1], x[0]])
        .unwrap()
}

Playground

Upvotes: 1

Related Questions