Macro taken::take [] [src]

macro_rules! take {
    [$var:ident, $($rest:tt)*] => { ... };
    [$var:ident as $v:ident, $($rest:tt)*] => { ... };
    [mut $var:ident, $($rest:tt)*] => { ... };
    [mut $var:ident as $v:ident, $($rest:tt)*] => { ... };
    [&$var:ident, $($rest:tt)*] => { ... };
    [&$var:ident as $v:ident, $($rest:tt)*] => { ... };
    [&mut $var:ident, $($rest:tt)*] => { ... };
    [&mut $var:ident as $v:ident, $($rest:tt)*] => { ... };
    [=$var:ident, $($rest:tt)*] => { ... };
    [=$var:ident as $v:ident, $($rest:tt)*] => { ... };
    [=mut $var:ident, $($rest:tt)*] => { ... };
    [=mut $var:ident as $v:ident, $($rest:tt)*] => { ... };
    [$var:ident] => { ... };
    [$var:ident as $v:ident] => { ... };
    [mut $var:ident] => { ... };
    [mut $var:ident as $v:ident] => { ... };
    [&$var:ident] => { ... };
    [&$var:ident as $v:ident] => { ... };
    [&mut $var:ident] => { ... };
    [&mut $var:ident as $v:ident] => { ... };
    [=$var:ident] => { ... };
    [=$var:ident as $v:ident] => { ... };
    [=mut $var:ident] => { ... };
    [=mut $var:ident as $v:ident] => { ... };
    [] => { ... };
}

Take ownership of specific variables.

One of the main use cases is closures. Closures try to be "smart" about how much scope they capture. If you don't mutate a variable they take &var, if you do mutate they take &mut var. However, if you require ownership you use move, i.e. move || {... do stuff with var...}... right?

The problem with move is that it moves every variable that is referenced. If you only need to move a few variables it can be a pain. Interestingly, you can tell the compiler to only move specific variables like so:

let x = x;
let y = y;
// ... etc

But this is quite silly and not obvious to someone who doesn't know about it. Instead, use the take! macro and your code will be self documenting.

You can also use take! to perform a clone or assert to the compiler that you are only taking a certain kind of reference. See the examples for more.

Downsides

If you use this macro (or let x = x) then the closure becomes FnOnce.

Unfortunately the best explanation of the trade offs is currently a reddit thread. Please help flush out these docs more!

Examples

Changing Ownership

It is easy to change the mutability and take references or clones.

let (a, mut b, c, d, e, f) = (1, 2, 3, 4, 5, 6);
take!(
    &a,     // let a = &a;
    &mut b, // let b = &mut b;
    c,      // let c = c;
    mut d,  // let mut d = d;
    =e,     // let e = e.clone();
    =mut f, // let mut e = e.clone();
);

Changing Ownership and Renaming

You can also rename one or more of the variables using as:

let (var_a, mut var_b, var_c, var_d, var_e, var_f) = (1, 2, 3, 4, 5, 6);
take!(
    &var_a as a,     // let a = &var_a;
    &mut var_b as b, // let b = &mut var_b;
    var_c as c,      // let c = var_c;
    mut var_d as d,  // let mut d = var_d;
    =var_e as e,     // let e = var_e.clone();
    =mut var_f as f, // let mut e = var_e.clone();
);

Usecase: Closures

Closures are one of the main use cases.

#[macro_use] extern crate taken;

// make them all mutable to demo that we can control
// mutability using `take!`
let mut w = vec![1, 2, 3];
let mut x = vec![1, 2, 3];
let mut y = vec![1, 2, 3];
let mut z = vec![10];

{
    let closure = || {
        take!(&w, &mut x, y, mut z);
        // w.push(5); // ERROR: cannot borrow as mutable
        x.push(4);    // mutate reference to x
        z.push(10);   // we own `mut z`

        println!("&x: {:?}", &x);
        println!("moved y: {:?}", y);
        println!("moved mut z: {:?}", z);
        // y and z are dropped
    };

    closure();
}

println!("&w after: {:?}", w);    // We can still print w!
println!("&x after: {:?}", x);    // We can still print x!
// println!("y after: {:?}", y);  // ERROR: use of moved value
// println!("z after: {:?}", z);  // ERROR: use of moved value

Usecase: Threads

Threads are another primary use case, as threads use closures. Threads in particular are always FnOnce and often find themselves cloning and moving specific variables.

use std::thread::spawn;
use std::sync::mpsc::channel;
#[macro_use] extern crate taken;

let (send, recv) = channel();
{
    let th = spawn(|| {
        take!(send); // let send = send;
        send.send("foo").unwrap();
    });
    th.join().unwrap();
}
println!("received: {:?}", recv.into_iter().collect::<Vec<_>>());