- Proposal: SE-0089
- Author: Austin Zheng
- Status: Awaiting Review
- Review manager: Chris Lattner
- Revision: 2
- Previous Revisions: 1
Swift's String
type ships with a large number of initializers that take one unlabeled argument. One of these initializers, defined as init<T>(_: T)
, is used to create a string containing the textual representation of an object. It is very easy to write code which accidentally invokes this initializer by accident, when one of the other synonymous initializers was desired. Such code will compile without warnings and can be very difficult to detect.
Discussion threads: pre-proposal part 1, pre-proposal part 2, review thread, post-review thread
String
ships with a number of initializers which take a single unlabeled argument. These include non-failable initializers which create a String
out of a Character
, NSString
, CharacterView
, or UnicodeScalarView
, initializers which build a string out of a number, and failable initializers which take a UTF8View
or a UTF16View
.
There are at least two possible situations in which a user may write incorrect code which nevertheless compiles successfully:
- The user means to call one of the non-failable initializers besides the
init<T>(_: T)
initializer, but passes in an argument of incorrect type. - The user means to call one of the failable initializers, but accidentally assigns the created object to a value of non-nullable type.
In both cases the compiler silently infers the use of the init<T>(_: T)
initializer in lieu of the desired initializer. This may result in code which inadvertently utilizes the expensive reflection machinery and/or produces an unintentionally lossy representation of the value.
A proposed solution to this problem follows.
-
The current reflection-based
String.init<T>(_: T)
initializer will be renamed toString.init<T>(describing: T)
. This initializer will rarely be invoked directly by user code. -
A new protocol will be introduced:
ValuePreservingStringConvertible
. This protocol will be defined as follows:protocol ValuePreservingStringConvertible { /// A lossless, unambiguous representation of the conforming type as a string. var stringRepresentation : String { get } /// Instantiate an instance of the conforming type from a string representation. init?(stringRepresentation: String) }
Values of types that conform to
ValuePreservingStringConvertible
are capable of being represented in a lossless, unambiguous manner as a string. For example, the integer value1050
can be represented in its entirety as the string"1050"
. As such, it should be possible to attempt to create an instance of aValuePreservingStringConvertible
conforming type from a string representation. -
A new initializer will be introduced:
init<T: ValuePreservingStringConvertible>(_ v: T) { return v.stringRepresentation }
. This allows theString(x)
syntax to continue to be used on all values of types that can be converted to a string in a value-preserving way. -
The standard library will be audited. Any type which can be reasonably represented as a string in a value-preserving way will be modified to conform to
ValuePreservingStringConvertible
. If they conform toCustomStringConvertible
and their existingdescription
is value-preserving,stringRepresentation
will simply returndescription
. -
The Foundation SDK overlay will be audited in the same manner.
-
As a performance optimization, the implementation of the string literal interpolation syntax will be changed to prefer the unlabeled initializer when interpolating a type that is
ValuePreservingStringConvertible
or that otherwise has an unlabeledString
initializer, but use theString.init<T>(describing: T)
initializer if not.
With the introduction of the ValuePreservingStringConvertible
protocol, the intended semantics of the three string convertible protocols can thus be clarified:
-
CustomStringConvertible
will provide a human-readable description of an instance. It may provide as little or as much detail as deemed appropriate. -
CustomDebugStringConvertible
will provide a human-readable description of an instance. It can provide additional information relative toCustomStringConvertible
, information that would not be pertinent for consumers ofdescription
s (such as human readers or other APIs), but would be useful for development or diagnostic purposes. -
ValuePreservingStringConvertible
will provide an exact, value-preserving representation of an instance, one that is sufficent to reconstruct an instance of that type.
This API change may impact existing code.
Code which intends to invoke init<T>(_: T)
will need to be modified so that the proper initializer is called. In addition, it is possible that this change may uncover instances of the erroneous behavior described previously.
One alternative solution might be to make ValuePreservingStringConvertible
an empty protocol narrowing CustomStringConvertible
, and require conforming types to vend a description
providing a value-preserving representation. There are several potential pitfalls to this approach:
-
The term
description
does not unambigiously imply "a value-preserving string representation" to readers. It can be reasonably construed as referring to a human-readable abridged summary of the value in question, which is how it is used today for many complex types (such asUIView
). Overloadingdescription
to mean two different things would require types to choose between conforming toValuePreservingStringConvertible
and providing abridged descriptions of their instances. -
It would make conforming certain Foundation types (which would otherwise be a natural fit for this functionality) impossible without introducing changes in functionality to existing APIs. Code that depends on
description
returning a string with a particular format might break as a result.
Another alternative solution might be to avoid introducing any new protocols, and instead allow CustomStringConvertible
or CustomDebugStringConvertible
to provide the means by which a String could be initialized from another type. This solution would prevent any sort of formal distinction between types that can be turned into value-preserving strings, and those that cannot.
On [Date], the core team decided to (TBD) this proposal. When the core team makes a decision regarding this proposal, their rationale for the decision will be written here.