Skip to content

Instantly share code, notes, and snippets.

@aturon
Created May 15, 2014 18:18
Show Gist options
  • Save aturon/8e6d72cf5e3779f84e7c to your computer and use it in GitHub Desktop.
Save aturon/8e6d72cf5e3779f84e7c to your computer and use it in GitHub Desktop.
Commentary on return types

Currently, APIs are often forced to either (1) expose a concrete return type or (2) return trait objects, which come with several downsides.

For example, the iter function for Vec<T> currently has the signature

fn iter<'a>(&'a self) -> Items<'a, T>

where Items is a struct defined purely for iterating over a slice. The struct has no public fields; the only interesting thing about it is that it implements various traits, such as Iterator<&'a T>.

This API is revealing more than it might have to: the type Items should not have to be exposed at all. Instead, we should be able to give iter a signature like:

fn iter<'a>(&'a self) -> impl Iterator<&'a T>

where impl Iterator<&'a T> is hypothetical syntax for "some statically-known type that implements Iterator<&'a T>." That's the contract you really want: not the name of a specific type, but just that there is some type that implements Iterator that you are going to return.

Of course, we can use trait objects to get something close to this:

fn iter<'a>(&'a self) -> Box<Iterator<&'a T>>

but this has several disadvantages. It means that we're returning a fat pointer (with a v-table). It also precludes using generic traits for return types.

The situation for input is much better: you can decide to either write a generic function like

fn <I: Iterator<uint>>sum(it: I) { ... }

and get static dispatch, or use trait objects like

fn sum(it: Box<Iterator<uint>>) { ... }

and get dynamic dispatch.

It would be nice to be able to make the analogous choices for return types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment