Skip to content

RFC: Boxes vs Containers - Naming schemes for element access #7887

Closed
@Kimundi

Description

@Kimundi

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 empty
  • fn peek(&'a self) -> Option<&'a E> - REF or None
  • fn top_mut(&'a mut self) -> &'a mut E - REF_MUT, fail if empty
  • fn peek_mut(&'a mut self) -> Option<&'a mut E> - REF_MUT or None
  • fn pop(&mut self) -> E - TAKE, fail if empty
  • fn pop_opt(&mut self) -> Option<E> - TAKE or None
  • fn pop_consume(self) -> E - UNWRAP, fail if empty
  • fn 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 empty
    • fn get_opt(&'a self) -> Option<&'a E> - REF or None
    • fn get_mut(&'a mut self) -> &'a mut E - REF_MUT, fail if empty
    • fn 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 empty
    • fn 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 empty
    • fn 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 empty
    • fn get_opt(&'a self) -> Option<&'a E> - REF or None
    • fn get_mut(&'a mut self) -> &'a mut E - REF_MUT, fail if empty
    • fn 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() or opt.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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions