As far as functional programming goes this looks quite clean and well done.
The thing that jumps out at me is your partition() call. Every time you go through the quick_sort() function you'll be allocating a pair of temporary vectors and populating it with references to the items above/below your pivot. The final .collect::<Vec<E>>() (tip: you don't need turbofish here because it'll be deduced) will drain the contents of your lower and higher buffers, effectively double-handling the data.
I think the problem you're going to encounter is that by using a functional style you'll have loads of unnecessary allocations and copies, which would destroy runtime performance. Functional programming prefers to leave things immutable which means every function will construct and return a new object, so I'm not sure how you'd get around that.
Normally, I'd recommend accepting a &mut [T] as the input parameter and then using something like split_at_mut() to split at your pivot point, but I'm not sure whether that is "functional" enough...
I understand your intension of the functional way here, but most functional languages have sort function implemented with mutable buffer under the hood for the performance reason. Quicksort is quick because we don't need extra allocation for it and it's cache friendly.