DEV Community

BC
BC

Posted on

Day6:Closure - 100DayOfRust

Closure

This is the 6th day and I almost gave up because this "closure" thing is so complicated.

You can see this article here: Why Rust Closures are (Somewhat) Hard

But finally I think I (somewhat) got it (or I hope so).

General use case

fn main() {
    let num_vec = vec![1,2,3,4,5,6];
    let num_arr = [1,2,3,4,5,6];

    let finder = |x: &&i32| **x==3;

    let found_in_vec = num_vec.iter().find(finder)
                                     .unwrap();
    let found_in_arr = num_arr.iter().find(finder)
                                     .unwrap();

    println!("found in vec: {}", found_in_vec);
    println!("found in arr: {}", found_in_arr);

    // Pass closure to filter
    let filtered: Vec<&i32> = num_vec.iter()
                                     .filter(|&&x| x % 3 == 0)
                                     .collect();
    println!("Filtered: {:?}", filtered);

    // FizzBuzz
    println!("{}", "=".repeat(20));
    let numbers: Vec<i32> = (1..31).collect();
    numbers.iter().for_each(|x| {
        if x % 15 == 0 {
            println!("Fizz Buzz");
        } else if x % 3 == 0 {
            println!("Fizz");
        } else if x % 5 == 0 {
            println!("Buzz");
        } else {
            println!("{}", x);
        }
    });

    // Found all numbers divisible by 3
    let div3: Vec<&i32> = numbers.iter()
                                 .filter(|&&x| x % 3 == 0)
                                 .collect();
    println!("Div3: {:?}", div3);

    // map a function to range
    let doubled:Vec<i32> = (1..10).map(|x| x*2).collect();
    println!("Doubled: {:?}", doubled);

    // sum all even numbers b/t 1 to 10 (10 is not included)
    let sum:i32 = (1..10).filter(|x| x % 2 == 0).sum();
    println!("Sum is {}", sum);
}
Enter fullscreen mode Exit fullscreen mode

Result

found in vec: 3
found in arr: 3
Filtered: [3, 6]
====================
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz Buzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
Fizz Buzz
Div3: [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
Doubled: [2, 4, 6, 8, 10, 12, 14, 16, 18]
Sum is 20
Enter fullscreen mode Exit fullscreen mode

Closure as input: Fn, FnMut, FnOnce

How variables are captured (by &, by &mut or by value) won't determine which trait (Fn, FnMut, FnOnce) will be used by compiler.

For example, when you use move, the variable is captured by value, but if that value is only being used for read, compiler will still use Fn attribute, not FnOnce, but if that variable is dropped, then compiler will use FnOnce

fn apply<F>(f: F) where F: FnOnce() {
    f();
}

fn sayhi() {
    println!("hi");
}


fn main() {
    let greeting = "hello";
    let mut copied = greeting.to_owned();

    let say = || {
        // If just this one line,
        // F could be Fn, FnMut and FnOnce
        println!("{}", greeting);

        // With this line, F cannot be Fn anymore
        // needs to be FnMut or FnOnce
        copied.push_str(" world");

        // with this line, F needs to be FnOnce
        drop(copied);
    };

    apply(say);
    apply(sayhi);
}
Enter fullscreen mode Exit fullscreen mode

Result:

hello
hi
Enter fullscreen mode Exit fullscreen mode

Closure as output (returned)

fn create_fn() -> impl Fn() {
    let text = "Fn".to_owned();

    move || println!("This is a: {}", text)
}

fn create_fnmut() -> impl FnMut() {
    let text = "FnMut".to_owned();

    move || println!("This is a: {}", text)
}

fn main() {
    let fn_plain = create_fn();
    let mut fn_mut = create_fnmut();

    fn_plain();
    fn_mut();
}
Enter fullscreen mode Exit fullscreen mode

Result:

This is a: Fn
This is a: FnMut
Enter fullscreen mode Exit fullscreen mode

Top comments (0)