`'a` is a "lifetime parameter"; syntactically, it's like a generic parameter, only it's use to denote how long a given reference stays alive. This is one of the important mechanisms that Rust uses to ensure memory safety without a GC. Every reference in Rust has a lifetime, but many of them can be elided.

`fn add<'a>` just means `define a function with a lifetime parameter called `'a`. This is analogous to if you defined a function as `fn add<T>` with a generic parameter T.

`Env<'a>` means that the type `Env` is parametrized by a lifetime. As before, this is similar to a type being parametrized by a generic parameter. This is usually done when a type contains a reference; Rust requires that references stored in a type must have explicit lifetimes specified. In this case, we're specifying that the lifetime that Env is parametrized over is the lifetime parameter that the function defines. This doesn't mean anything in particular on it's own, but when we use `'a` elsewhere in the signature, it means that the two places use the same lifetime, which is necessary to specify sometimes to clarify to the compiler that something is safe.

`&[Term<'a>]` is a slice of `Term<'a>` values; any type can go between the `[]`, e.g. `&[i32]` for a slice of 32-bit integers. A slice is a view into a sequence of values in memory. One of the things Rust does to aid the programmer in low-level optimizations is define separate types for sequences, namely slices, arrays, and vectors. A slice is a reference to another sequence type (which could be another slice); they're very cheap to make, but cannot be resized. An array is a value type (i.e. not a reference) and must be constant-sized. Finally, a `Vec` is resizable and heap-allocated, so it's expensive to create and copy (relative to slices).

Finally, `Result<Term<'a>, Error>` means that the function returns a `Result` (i.e. either a success or an error); if no error occurs, it will return a `Term<'a>; otherwise, it will return the type `Error` (which is generally custom-defined in a given package, but could be an instance of a standard library error like `std::io::Error`. This is roughly analogous to returning a `Term<'a>` and marking that an exception of type `Error` may be thrown.

One thing to note is that Rust's return values with lifetimes tend be tied to one of the parameters. In this case, the compiler's rules aren't sufficient to infer which of `Env` and `Term` to tie it to, so the programmer needs to disambiguate. The programmer chose to tie them all together, which presumably indicates that the output will reference both parameters in some way (although it's impossible to figure out more precisely how it will from the signature alone).

Lifetimes are definitely the hardest part of Rust in my opinion; the important thing to realize is that changing the lifetime parameters in a function will never change the user-visible semantics of the code. If the lifetimes are invalid, the compiler will error, and if they are valid but not optimal, then the worst case is that that memory might be kept around longer than needed. Unless you're programming something that is super performance critical or will be long running, you generally won't have huge issues if you fix the lifetime compiler errors with trial and error when they come up (which is much less often than you might expect due to the compiler being able to infer them most of the time).