Description
Right now there is much confusion and inconsistency about the naming of functions that deal with elements in types that wrap one or more of them. We need to find a consistent way to deal with that.
First, some terminology:
- Container - Can contain at most N elements (with N effectively infinite).
Examples: Vector, Hash Map, Linked List. - Box - Can contain at most 1 element.
Examples: Option, Cell, ~T, ARC.
I'm also defining a elementary set of operations you can perform on containers and boxes in regard to an arbitrary chosen element E
(named in all-caps to differentiate from actual function names below):
- REF: Return a reference to the element -
(&'a self) -> &'a E
- REF_MUT: Return a mutable reference to the element -
(&'a mut self) -> &'a mut E
- TAKE: Return a element, removing it from the type -
(&mut self) -> E
- UNWRAP: Return a element, destroying the type in the process -
(self) -> E
In regard to naming schemes, I see a number of possibilities:
Unify boxes and containers
This would be the globally most consistent and reusable way, but also the most ugly one. We define a set of functions for working with an arbitrary 'next' element on an container. For example in stack-terminology:
fn top(&'a self) -> &'a E
- REF, fail if emptyfn peek(&'a self) -> Option<&'a E>
- REF or Nonefn top_mut(&'a mut self) -> &'a mut E
- REF_MUT, fail if emptyfn peek_mut(&'a mut self) -> Option<&'a mut E>
- REF_MUT or Nonefn pop(&mut self) -> E
- TAKE, fail if emptyfn pop_opt(&mut self) -> Option<E>
- TAKE or Nonefn pop_consume(self) -> E
- UNWRAP, fail if emptyfn pop_consume_opt(self) -> Option<E>
- UNWRAP or None.
Separate boxes and containers, consistent box naming scheme
Containers and Boxes are separate concepts. Containers are build around a terminology and operations that make most sense to them. They probably wouldn't support UNWRAP and might not support the others either, depending on their specific semantics.
Boxes gain their own more concise and strictly consistent terminology, based around the box metaphor:
- "Get a reference to the content, but keep it in the box"
fn get(&'a self) -> &'a E
- REF, fail if emptyfn get_opt(&'a self) -> Option<&'a E>
- REF or Nonefn get_mut(&'a mut self) -> &'a mut E
- REF_MUT, fail if emptyfn get_mut_opt(&'a mut self) -> Option<&'a mut E>
- REF_MUT or None
- "Take the content out of the box, keeping the box intact but empty"
fn take(&mut self) -> E
- TAKE, fail if emptyfn take_opt(&mut self) -> Option<E>
- TAKE or None
- "Destructively unwrap the box to get to the content"
fn unwrap(self) -> E
- UNWRAP, fail if emptyfn unwrap_opt(self) -> Option<E>
- UNWRAP or None.
Separate boxes and containers, convenient box naming scheme
This one is quite similar to the prior one, but shuffles names and definitions around to provide a nicer set of function names for the common operations:
- "Get a reference to the content, but keep it in the box"
fn get(&'a self) -> &'a E
- REF, fail if emptyfn get_opt(&'a self) -> Option<&'a E>
- REF or Nonefn get_mut(&'a mut self) -> &'a mut E
- REF_MUT, fail if emptyfn get_mut_opt(&'a mut self) -> Option<&'a mut E>
- REF_MUT or None
- "Take the content out of the box if any, keeping the box intact but empty"
fn take(&mut self) -> Option<E>
- TAKE or None
- "Destructively unwrap the box to get to the content"
fn unwrap(self) -> E
- UNWRAP, fail if empty
The last one is probably the most convenient as it's optimized for common "I know it is not None
" Option
usecases:
opt.get()
- " Do something with the content of a Option, but keep it in place."opt.unwrap()
- "Move the content out of a Option, throw the Option away"opt.clone().unwrap()
oropt.get().clone()
- "Get a copy of the content"opt.take().unwrap()
- "Move the content out of a Option, replace the Option with None"
Hm, this writeup is only half as comprehensive and cohesive as I wanted it to be, I hope it's still understandable.