unborrow/lib.rs
1#![no_std]
2
3/// Explicitly precompute a method's arguments before the call so that borrowck sees them the same
4/// way that trans does.
5///
6/// Examples
7/// =======
8///
9/// ```
10/// # #[macro_use] extern crate unborrow;
11/// # fn main() {
12/// let mut v = vec![1, 2, 3];
13///
14/// // this line would cause an error because borrowck consider `v` borrowed by `reserve`
15/// // during its parameter list
16/// // v.reserve(v.capacity()); //~ERROR cannot borrow `v`
17/// // but wrap the call in unborrow!() and it works!
18/// unborrow!(v.reserve(v.capacity()));
19/// assert!(v.capacity() >= 6);
20/// assert_eq!(v, [1, 2, 3]);
21///
22/// // similar to the above, both v.len()-1 and v[0]+41 require borrowing `v` and we can't
23/// // do that while borrowck thinks is is mutably borrowed by `insert`
24/// // v.insert(v.len() - 1, v[0] + 41); //~ERROR cannot borrow `v`
25/// // but wrap the call in unborrow!() and it works!
26/// unborrow!(v.insert(v.len() - 1, v[0] + 41));
27/// assert_eq!(v, [1, 2, 42, 3]);
28///
29/// // it also works for nested objects!
30/// struct Wrapper { v: Vec<i32> }
31/// let mut w = Wrapper { v: vec![1, 2, 3] };
32/// unborrow!(w.v.reserve(w.v.capacity()));
33///
34/// // ...and with free functions! (the first argument is assumed to be the mutable borrow)
35/// use std::mem;
36/// unborrow!(mem::replace(&mut v, v.clone()));
37///
38/// # }
39/// ```
40#[macro_export]
41macro_rules! unborrow {
42 // =========================================================================================================
43 // PRIVATE RULES
44
45 // This rule fires when we have parsed all the arguments.
46 // It just falls through to output stage.
47 // (FIXME could fold the output rule into this one to reduce recursion)
48 (@parse () -> ($names:tt $lets:tt) $($thru:tt)*) => {
49 unborrow!(@out $names $lets $($thru)*)
50 };
51
52 // Parse an argument and continue parsing
53 // This is the key rule, assigning a name for the argument and generating the let statement.
54 (@parse ($arg:expr, $($rest:tt)*) -> ([$($names:ident),*] [$($lets:stmt);*]) $($thru:tt)*) => {
55 unborrow!(@parse ($($rest)*) -> ([$($names,)* arg] [$($lets;)* let arg = $arg]) $($thru)*)
56 // ^ ^
57 // Right here an ident is created out of thin air using hygiene.
58 // Every time the macro recurses, we get a new syntax context, so "arg" is actually a new identifier!
59 };
60
61 // Output stage for free functions.
62 // Assembles the let statements and variable names into a block which computes the arguments,
63 // calls the method, and returns its result.
64 (@out [$($names:ident),*] [$($lets:stmt);*] ($($meth:ident)::+) $arg1:expr) => {{
65 $($lets;)*
66 $($meth)::+($arg1, $($names),*)
67 }};
68
69 // Output stage for object methods.
70 (@out [$($names:ident),*] [$($lets:stmt);*] $($obj:ident).+) => {{
71 $($lets;)*
72 $($obj).+($($names),*)
73 }};
74
75 // =========================================================================================================
76 // PUBLIC RULES
77
78 // Macro entry point for object methods.
79 ($($obj:ident).+ ($($args:expr),*)) => {
80 unborrow!(@parse ($($args,)*) -> ([] []) $($obj).+)
81 // | | | ^ info about the method call, saved for later
82 // | | ^ generated let statements
83 // | ^ generated argument names
84 // ^ arguments to be parsed
85 };
86
87 // Macro entry point for free functions.
88 ($($meth:ident)::+ ($arg1:expr, $($args:expr),*)) => {
89 unborrow!(@parse ($($args,)*) -> ([] []) ($($meth)::+) $arg1)
90 };
91}
92