I see this error when trying to extend unorthodox 'types' like (Int, Int)
, or Any
:
Non-nominal type 'Any' cannot be extended
So what makes a type non-nominal? What's the difference between a non-nominal type like Any
or (Int)
and a regular nominal type like Int
?
The currently accepted answer is incorrect; the actual answer comes from a deep and esoteric part of Swift's type system.
Most types in Swift are nominal types—they're "named" types declared in a particular module as part of user code. Seemingly primitive types like Int
and Array
are actually structs defined in the Swift
module, which is automatically imported into every Swift source file. Even Optional
itself is an enum in the Swift
module, although the compiler adds a little bit of magic like optional chaining and force-unwrapping.
The few exceptions are called "non-nominal types". There may be others, but the main ones are:
(Int) -> String
(Int, String)
String.Type
(the type of the expression String.self
)CustomStringConvertible & Error
Existentials deserve a little more explanation. An existential contains a value whose exact type has been abstracted away, but which is known to conform to a certain set of protocols. For example:
// You can put anything that conforms to `CustomStringConvertible` in `x` let x: CustomStringConvertible // You can put anything that conforms to both `CustomStringConvertible` // and `Error` in `y` let y: CustomStringConvertible & Error // You can put anything in `z`; `Any` is an existential that doesn't // require you to conform to any particular protocols let z: Any
Non-nominal types all simply combine other types together in some ad-hoc way. (They're sometimes called "structural types" because they define some kind of general structure for other types to slot into.) They don't need to be explicitly defined, and they don't belong to any particular module—FooKit's (Int, String)
is the same as BarKit's (Int, String)
. But all of their behavior is defined by the language—a non-nominal type can't have methods or properties of its own, can't conform to protocols, and therefore can't be extended.
So you can't extend Any
because Any
is a special thing that's built into the language, just like a function type or a tuple type. It just happens to have a name written in letters, not in punctuation.
(So why can you extend CustomStringConvertible
? Because, depending on context, CustomStringConvertible
might mean the protocol or it might mean an existential containing a value conforming to the protocol. When you write extension CustomStringConvertible
, you're extending the protocol, but when you write let x: CustomStringConvertible
, you're declaring a variable whose type is "existential containing a value conforming to the protocol". This is kind of confusing, and some of Swift's maintainers would actually like to require that the existential be written as Any<CustomStringConvertible>
to make it more clear. Not terribly likely to happen now that they're trying to preserve source stability, though.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With