I'm designing a library API for which I get the following problem very frequently: There is an expensive
allocation to be made by the function, but I don't want to always require the user to pass a mutable borrow, since this is error-prone (maybe the data has the wrong size), and the operation is not otherwise fallible, so I don't want to return a result only because of this kind of error (this should cause a panic).
By allocating the data myself, the user doesn't run into the risk of causing a panic, so I have to offer an
allocating version. But the user might need to call the function inside a loop, so allocating every time also
isn't an option, not only because of the cost of calling the system allocator, but because I might need to execute some initialization of the data state.
My question is, if you were using the library, which of the following patterns would you prefer to use?
pub struct ExpensiveData(Vec<u8>);
Pattern 1 Have two functions and let the user choose between them:
fn compute(arg : i32) -> ExpensiveData;
fn compute_to(data : &mut ExpensiveData, arg : i32);
Pattern 2 Have a single function, and let the user decide by moving an Option into the function
(allocate internally if argument is None)
fn compute(data : Option<ExpensiveData>, arg : i32) -> ExpensiveData;
Call site would look like this (no allocation)
data = compute(Some(data), 1);
or like this (internal allocation):
let data = compute(None, 1);
Pattern 3 Have a single function, and let the user decide by moving a mutable borrow
(allocate internally if argument is None; modify argument and return nothing if argument is Some(data)):
fn compute(data : Option<&mut ExpensiveData>, arg : i32) -> Option<ExpensiveData>;
Call site would look like this (no allocation):
let data = compute(None, 1).unwrap();
or like this (no allocation):
compute(Some(&mut data), 1);