[][src]Macro cascade::cascade

macro_rules! cascade {
    ($i:ident : $e: expr; $($tail: tt)*) => { ... };
    ($e: expr; $($tail: tt)*) => { ... };
    (@line $i:ident, | $s: stmt; $($tail: tt)*) => { ... };
    (@line $i: ident, .. $v: ident = $e: expr; $($tail: tt)*) => { ... };
    (@line $i:ident, .. $v:ident += $e:expr; $($tail:tt)*) => { ... };
    (@line $i:ident, .. $v:ident -= $e:expr; $($tail:tt)*) => { ... };
    (@line $i:ident, .. $v:ident *= $e:expr; $($tail:tt)*) => { ... };
    (@line $i:ident, .. $($q: ident ($($e: expr),*)).+; $($tail: tt)*) => { ... };
    (@line $i:ident, .. $($q: ident ($($e: expr),*)).+?; $($tail: tt)*) => { ... };
    (@line $i:ident,) => { ... };
    () => { ... };
}

A macro for chaining together statements that act on an initial expression.

Usage

Cascading allows you to run multiple statements on a struct generated by an expression without having to repeatedly type it out. This can be very convinient for modifying multiple properties simultaneously, or running a long series of individual methods on a struct.

When writing a cascade, the first line specifies the expression that will be operated upon. All following lines modify the struct created by the expression in some way. The most common operator is the .. member operator, which is borrowed from Dart's syntax. It's a convinient shorthand for accessing a member or method of the struct. For example:

use cascade::cascade;

let old_vector = vec!(1, 2, 3);
let new_vector = cascade! {
  old_vector;
  ..push(4);
  ..push(5);
  ..push(6);
};

Remember, you can't move the struct, because it gets returned at the end of the cascade. In other words, you can't run a method on a struct with the .. operator if the method takes self or mut self. But &self or &mut self works fine.

If you want to put statements within the macro, you can use the | operator:

use cascade::cascade;
use std::collections::HashMap;

let hash_map = cascade! {
  HashMap::new();
  ..insert("foo", "bar");
  | println!("Look! You can put statements in a cascade!");
  | for i in 0..3 {
    println!("You can put loops in here too! Make sure to put a semicolon at the end!");
  };
};

If you need to reference the expression inside a cascade, you can name it and then use it:

use cascade::cascade;

let vector = cascade! {
  v: Vec::new();
  ..push(1);
  | println!("The vector now has {} element(s).", v.len());
  ..push(2);
};

This will print The vector now has 1 element(s).

Once again, trying to move this value will throw an error.

Finally, you can also use the member operator (..) to set or change members of the cascaded struct:

use cascade::cascade;
struct A {
  pub b: u32,
  pub c: u32
}

let a = cascade! {
  A { b: 0, c: 0 };
  ..b = 5;
  ..c += 1;
};

More examples of the cascade macro can be found in the examples folder on the Github repository.

Common Mistakes

Sometimes, you might get a weird error while using this macro. Most likely it will be some variation of no rules expected the token '@'. Since these errors are notoriously hard to debug, here are some common mistakes one might make that will result in this error being thrown:

  1. Not putting a semicolon at the end of a line. Every single cascade statement must end with a semicolon. Yes, even loops! This is because the parser needs to know when the line ends and the next line begins, and the semicolon is used as the delimiter.
  2. Not putting a operator at the beginning of a line. Even if you are just writing a statement, you need to put the | operator at the beginning. Once again, this is due to the way Rust parses macros. It also means that each line of the cascade macro is consistent, with an <operator> <statement>; syntax.