Advice for crate API design

I have a public struct that contains a private map as one of its fields. I have a method to add an entry to this map but I would like to extend it to communicate whether or not the entry already existed. Notably the value type of the map is private cannot be returned. Also calling this function again overwrites any old value.

For example lets say I have a function like this

impl Container {
    fn add_thing(&mut self, name: String, thing: String) -> ?;
}

I want to adjust the return type, I have thought of the following options
a) Return bool, where true means the value already existed.
b) Return bool, where false means the value already existed.
c) Return Option<()> where Some(()) means the value already existed.
d) Define a custom enum with two variants and return that.
e) Define a separate method has_thing() that the user would have to use first.

I hope this question makes sense. Currently I am leaning to c) but I would like to see if anyone else has any opinions or has seen this pattern before.

Similar APIs

  • BTreeMap::insert returns Some(V) if the map contained the key and None if the map did not contain the key.
  • BtreeSet::insert returns true if the set previously did not contain the value.
1 Like

What is a user of the API likely to do with the information? Its possible there's not much value in returning anything at all.

Option<()> is effectively a bool. In this case I don't think it really semantically communicates the difference between the possible outcomes any more clearly than bool does.

Another option might be to define a unit struct like struct Replaced;[1] and make the return type Option<Replaced>. Then the type is at least communicating what the Some variant indicates.

You could also take inspiration from the HashMap Entry API which would give the user a way to check the state of the value at that key and insert if they need to.


  1. or a tuple struct with a private field if you want to reserve the right to store something in the struct in a later version. For example to let a user put the old value back into the collection later ↩ī¸Ž

3 Likes

If you are mimicking a map, then I would follow the pattern that both HashMap and BTreeMap follow.

2 Likes

I like to use Result if one outcome is clearly an error. I don't think Option<()> has any useful meaning or makes much sense (unless it's an Option<T> where T happens to be () in some cases). (But I acknowledge this could mimick what HashMap/HashSet do.)

If you don't deal with a clear "error vs success" case, I would most certainly use a bool or an enum. I think either of those has advantages/disadvantages and may be a matter of taste.