Problems with API Design


#1

Hi,
I have troubles to make a good API for my small lib. For instance, I provide following function:

pub fn random_choice<'a, T>(samples: &'a Vec<T>, weights: &Vec<f64>, n: usize) -> Vec<&'a T>

But there is also an in place variant which has one argument less. Because rust doesn’t support function overloading, I had to give it a new name:

pub fn random_choice_in_place<T: Copy>(samples: &mut Vec<T>, weights: &Vec<f64>)

But T may be boxed, so I cant copy it. So I have to design a new API:

pub fn random_choice_in_place<T>(samples: &mut Vec<Box<T>>, weights: &Vec<f64>)

The problem is, I can’t overload a function in rust. Even a trait doesn’t seem to work, because there is a difference between Vec<Box<T>> and Vec<T>. So do I have to invent a new function name like this?

fn random_choice_in_place_boxed<T>(samples: &mut Vec<Box<T>>, weights: &Vec<f64>)

I could implement this easily in Java or C++, but I have my problems with rust. Any ideas, how I could define just one function name with different parameters?

My code is here, but the boxed variant is missing so far.


#2

For non in-place version, you can use slices as input parameters:

fn random_choice<'a, T>(samples: &'a [T], weights: &[f64], n: usize) -> Vec<&'a T>

I am not sure what the in-place version does. Loos like it permutes and possibly duplicates some elements without changing the vector size. So you need the T: Clone bound (Copy is severely more restricted) and a &mut [T] slice.

fn random_choice_in_place<T: Clone>(samples: &mut [T], weights: &[f64])

#3

Wow, thx! That helped a lot! :slight_smile:

But function overloading with different arguments won’t work in rust, right?


#4

Are you referring to passing Vec<T> in as &[T]/&mut [T]? If so, that’s handled using deref coercion, which will automagically kick in if you pass &Vec<T> to a function that accepts &[T] (similarly for a mutable reference).

EDIT: And arguably passing slices is much nicer because deref coercion gives you a little bit of free function “overloading” by default.


#5

@dikaiosune: Yes, @matklad already gave me this hint, thx. :slight_smile:

My last problem: I want follwing API:

fn random_choice<'a, T>(samples: &'a [T], weights: &[f64], n: usize) -> Vec<&'a T>

and with one argument less and without return type:

fn random_choice<T: Clone>(samples: &mut [T], weights: &[f64])

And they have to be called the same way:

RandomChoice::random_choice(....);

As far as I know, this is not possible, right?


#6

Yes. I believe that even if you define separate traits for these two functions then you would have to use UFCS form anyway.

But as an API consumer, I would much prefer if these particular functions had different names anyway :slight_smile:


#7

This is by design. If two methods do different things, they should have different names. This is especially important for rust due to type inference.