When designing an API, it's easy to use public types that denote resource identity to allow actions on a resource. As an example in the snippet below, we use to denote the identifier of the type that the user of the interface wants to use to retrieve the value. Int Storable Option 1 { : -> } protocol StoredCollection associatedtype Storable Codable func fetch (id: Int) Storable It allows the caller to ask for any random id: , , , . At first, this does not appear to be a problem, all we need to do is to make an edit to the interface to indicate that we will return an optional value in case the identifier does not exist. 5 6 99 105 { : -> ? } protocol StoredCollection associatedtype Storable Codable func fetch (id: Int) Storable Now, let's add creation semantics into this collection, which could lead to the possibilities mentioned below. Option 2 { : -> ? } { : -> -> ? } //Option 1: Interface user specifies the id protocol StoredCollection associatedtype Storable Codable func create (id: Int, value: Storable) func fetch (id: Int) Storable //Option 2: Interface user just provides the value protocol StoredCollection associatedtype Storable Codable func create (value: Storable) Int func fetch (id: Int) Storable Option 2, in my opinion, is more preferable. It indicates that the responsibility of creating the identifier for the storable type belongs to the implementation, whereas Option 1 puts that responsibility on the user of StoredCollection. StoredCollection And here is how might look like when we update the interface to introduce and semantics. Option 2 update remove { : -> -> ? } protocol StoredCollection associatedtype Storable Codable func create (value: Storable) Int func update (id: Int, value: Storable) func remove (id: Int) func fetch (id: Int) Storable As was the case earlier with fetch, we are allowing to be passed in to update and remove and fetch. But the user may accidentally end up using a random variable of type, which has got nothing to do with the StoredCollection when invoking the method. Int Int One way to circumvent this problem is by introducing an ID Type that is public to the outside world with respect to it's existence, but whose creation is private to the framework that's implementing . StoredCollection { : -> < > -> ? } { value: (value: ) { .value = value } } protocol StoredCollection associatedtype Storable Codable func create (value: Storable) StoreIdentifier Storable func update (id: StoreIdentifier<Storable>, value: Storable) func remove (id: StoreIdentifier<Storable>) func fetch (id: StoreIdentifier<Storable>) Storable public < : > struct StoreIdentifier Storable Codable private let Int fileprivate init Int self In the example above, we replaced with . StoreIdentifier could be a public type with being internal to the system implementing StoredCollection. By making this change, we can safely send the type across system boundaries, but are also able to avoid usage confusion related to the user passing in an unrelated type as the identifier when using , and . Int StoreIdentifier init update remove, fetch As an example if we had retained identifier type as Int, the user could create a sum by adding two identifiers, which does not offer any meaning from the StoredCollection's perspective, but the user of the StoredCollection may try to assign a special meaning to the record identified as sum of identifiers of two other values in the collection. Having opaque identifiers like the ones mentioend above introduces additional types, but it offers the benefit that no meaning can be derived by the users of StoredCollection outside of using the type for invoking the stored collection API's Another benefit is that the internal identifiers can be changed to String from Int, without having any impact on the users of the interface.