Trait tuplez::fold::Folder

source ·
pub trait Folder<T, Acc> {
    type Output;
    type NextFolder;

    // Required method
    fn fold(self, acc: Acc, value: T) -> (Self::Output, Self::NextFolder);
}
Expand description

Define folders that fold an element of the tuple and specify another folder to be used to fold the next element of the tuple.

To fold a tuple with type Tuple<T0, T1, ... Tn>, you need to construct a custom folder type, which implements Folder<T0, Acc>, and its NextFolder implements Folder<T1, <F as Folder<T0, Acc>>::Output>, and so on. Pass your folder to tuple’s fold() method, then the tuple will call folder’s fold() method and move its first element in, and then the tuple move its second element in its NextFolder’s fold() method, and so on.

NOTE: Folding a tuple will consume it. If this is not what you want, call as_ref() or as_mut() to create a new tuple that references its all members before folding.

§Quickly build a folder by macros

Here are two ways you can quickly build a folder.

§Fold tuples by element types

The folder! macro helps you build a folder that folds tuples according to their element types.

For example:

use tuplez::{folder, tuple, TupleLike};

let tup = tuple!(Some(1), "2", Some(3.0));
let result = tup.fold(
    folder! { String; // Type of `acc` of all closures must be the same and annotated at the front
        |acc, x: &str| { acc + &x.to_string() }
        <T: ToString> |acc, x: Option<T>| { acc + &x.unwrap().to_string() }
    },
    String::new(),
);
assert_eq!(result, "123".to_string());

§Fold tuples in order of their elements

You can create a new tuple with the same number of elements, whose elements are all callable objects that accepts the accumulation value and an element and returns new accumulation value (FnOnce(Acc, T) -> Acc), then, you can use that tuple as a folder.

For example:

use tuplez::{tuple, TupleLike};

let tup = tuple!(1, "2", 3.0);
let result = tup.fold(
    tuple!(
        |acc, x| (acc + x) as f64,
        |acc: f64, x: &str| acc.to_string() + x,
        |acc: String, x| acc.parse::<i32>().unwrap() + x as i32,
    ),  // Type of `acc` of each closure is the return type of the previous closure.
    0,
);
assert_eq!(result, 15);

§Custom folder

If you are not satisfied with the above three methods, you can customize a folder.

Here is an example, very simple but sufficient to show how to use:

use std::collections::VecDeque;
use tuplez::{fold::Folder, tuple, TupleLike};

struct MyFolder(VecDeque<i32>);

impl<T: std::fmt::Display> Folder<T, String> for MyFolder {
    type Output = String;
    type NextFolder = Self;

    fn fold(mut self, acc: String, value: T) -> (Self::Output, Self::NextFolder) {
        (
            if self.0.len() == 1 {
                acc + &format!("{}[{}]", value, self.0.pop_front().unwrap())
            } else {
                acc + &format!("{}[{}],", value, self.0.pop_front().unwrap())
            },
            self,
        )
    }
}

let tup = tuple!(3.14, "hello", 25);
let result = tup.fold(MyFolder(VecDeque::from(vec![1, 2, 3])), String::new());
assert_eq!(result, "3.14[1],hello[2],25[3]");

Required Associated Types§

source

type Output

Output type of folding.

source

type NextFolder

Type of next folder to be use.

Required Methods§

source

fn fold(self, acc: Acc, value: T) -> (Self::Output, Self::NextFolder)

Fold a value, return the output value and next folder.

Implementors§

source§

impl<First, F, Out, FOthers, Acc> Folder<First, Acc> for Tuple<F, FOthers>
where F: FnOnce(Acc, First) -> Out,

§

type Output = Out

§

type NextFolder = FOthers