Macro ergo::take[]

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.

You can instruct the compiler on how you want to own your variables in this way:

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

But this is quite silly and not always completely obvious what you are trying to do. Use the take! macro and your code will be self documenting.

take!(x, y); // I see, you are just taking ownership
// ... etc

It also allows you to change the mutability of variables more concisely.

// v has to be mutable at first
let mut v = vec![1, 2, 3, 4];
v.extend(0..100);
take!(v); // make sure we don't mutate v anymore.

As well as do operations in mass, such as if you want to assert how you own variables in your closure.

let (mut w, x, y, z) = (42, vec![1, 2], vec![3,10], vec![4, 5, 6]);
{
    let mut closure = || {
        // Specify _how_ we are using the outside scope
        take!(&mut w, &x, mut y, =mut z);

        // We took a a mutable reference to `w`
        *w = 777;

        // `x` is an immutable reference
        println!("we can't change x: {:?}", x);

        // `y` is mutable and will be dropped at the end of scope.
        y.push(111);

        // We took a mutable clone of `z`
        z.push(40);
    };
    closure();
}

println!("w has been mutated: {}", w);
println!("x couldn't change: {:?}", x);
// println!("y was moved: {:?}", y); // ERROR: use of moved value
println!("z was cloned, so it didn't change: {:?}", z);

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 using let x = x or take!(x).

Downsides

If you use this macro (or let x = x) inside a closure then it 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 on one or more variables.

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 f = f.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 f = var_f.clone();
);

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<_>>());