Integral implemented using a trapezoidal Riemann sum
Embrace even more static analysis tools, such as clippy. It provides warnings like:

warning: `if _ { .. } else { .. }` is an expression
  --&gt; src/main.rs:12:5
   |
12 |       let mut sign = 1.0;
   |  _____^ starting here...
13 | |
14 | |     if a &gt; b {
15 | |         mem::swap(&amp;mut a, &amp;mut b);
16 | |         sign = -1.0;
17 | |     }
   | |_____^ ...ending here
   |

You don't need to rebind a variable to make it mutable. you can just add mut in the argument list.
When a where clause is used, the { moves to the next line.
Why is abs used?  Isn't it guaranteed that b > a?
"side" vs "size" seems likely to cause confusion. Either make them the same or distinct.
Using map and sum avoids needing to make result mutable.
Consider removing all mutability; either by reassigning all the variables at once:

let (a, b, sign) = if a &gt; b {
    (b, a, -1.0)
} else {
    (a, b, 1.0)
};


Or by adding a small shim that reverses the order and applies the negation; I like the latter solution  because it ties the argument reversal and final negation closer together.


/// calculates the signed area between the function f and the x axis from
/// x = a to b using a trapezoidal Riemann sum. precision is the number of
/// trapezoids to calculate
pub fn integral&lt;F&gt;(a: f64, b: f64, f: F, precision: u32) -&gt; f64
    where F: Fn(f64) -&gt; f64
{
    fn core&lt;F&gt;(a: f64, b: f64, f: F, precision: u32) -&gt; f64
        where F: Fn(f64) -&gt; f64
    {
        let delta = (b - a) / precision as f64;

        (0..precision).map(|trapezoid| {
            let left_side = a + (delta * trapezoid as f64);
            let right_size = left_side + delta;

            0.5 * (f(left_side) + f(right_size)) * delta
        }).sum()
    }

    if a &gt; b {
        -core(b, a, f, precision)
    } else {
        core(a, b, f, precision)
    }
}