1

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.

dawid
  • 663
  • 6
  • 12
  • what is `reduce`? I don't recognize that from the standard library or the itertools crate. – kmdreko Mar 08 '21 at 04:19
  • @kmdreko I edited to add reference. "`reduce` gives Iterators a reduce function that is similar to fold but without an initial value." – dawid Mar 08 '21 at 05:00
  • 2
    `reduce` expects that both parameters of the closure and its return value all have the same type. But `chunks` returns slices and `sandwich` returns an array, so you will need a conversion at some point. Possibly: `lst.chunks (3).cloned().reduce (|x, y| [x[0], y[1], x[0]])`. – Jmb Mar 08 '21 at 07:58

1 Answers1

1

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

Netwave
  • 40,134
  • 6
  • 50
  • 93
  • That works. However, how can I take ownership when the chunk size is large, say 200? – dawid Mar 08 '21 at 13:33
  • @olyk, for that you can map another function that copies the content of the referenced slice into another owned one. You can find how to do it around SO too :) – Netwave Mar 08 '21 at 13:43
  • The ` -> [u8; 3] ` part is novelty to me and solve basically all my problems. Probably I need to finish the book reading every single paragraph before attempting to effectively program anything real ... – dawid Mar 08 '21 at 16:49
  • [Another option from the answers from your link](https://stackoverflow.com/a/38253365/9681577). Will be worth a try in terms of performance. – dawid Mar 08 '21 at 17:06
  • @olyk, yes, array_ref may be useful. I tried to stick to the std though – Netwave Mar 08 '21 at 17:16