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