swift – What is the Different between String(describing: Int) vs String(Int)?

swift – What is the Different between String(describing: Int) vs String(Int)?

Your question is actually unnecessary because if all you want to do here is print, you can just do it directly:

print(Count is, count) // Count is 100

Thats because print takes a variadic parameter and inserts space as the default separator.

However, lets answer the question anyway.


Answer

Its the difference between coercion and representation.

Coercion. Certain types can be changed into certain other types. You can change a Double to an Int and an Int to a Double. You can change an Int to a String and a String (maybe) to an Int. This is possible because the second type has an initializer whose parameter is the first type. This is something you would do in your actual program, like this:

let sum : Int = x + y
self.myLabel.text = Your total is (String(sum))

Representation. For debugging purposes, all types can benefit from being representable as a string. Suppose you have a Person type. You cant change a Person to a String and vice versa — that makes no sense — but you would surely like to be able to print a Person to the console to see if you are getting the right results. This is possible because the Person type itself supplies a printable description. This is something you would do for debugging purposes:

let p = Person(first:Matt, last:Neuburg)
print(p is (String(describing:p)))

Comments

Comment 1. This distinction is fairly new in Swift. It used to be that String(...) was used to express both notions. But the powers that be realized that that was a confusing conflation of the two distinct mechanisms. So nowadays, an attempt to write String(p) will fail, whereas earlier it would have succeeded. String has no Person initializer, so String(p) is forbidden; you now have to say explicitly that you are describing, not coercing.

Comment 2. The need to print a description is so obviously common that you do not have to pass thru String(describing:) if all you want to do is log the object itself. You can write print(p), because this is a shorthand for print(String(describing:p)). Similarly, string interpolation calls String(describing:) for you, so you can write print(p is (p)). And, as I said at the outset, print takes a variadic parameter and inserts space as the default separator, so you can write print(p is, p).

Reading the docs might help!

Heres an excerpt from String.init(describing:)

Use this initializer to convert an instance of any type to its
preferred representation as a String instance. The initializer creates
the string representation of instance in one of the following ways,
depending on its protocol conformance:

  • If instance conforms to the TextOutputStreamable protocol, the result is obtained by calling instance.write(to: s) on an empty string
    s.
  • If instance conforms to the CustomStringConvertible protocol, the result is instance.description.
  • If instance conforms to the CustomDebugStringConvertible protocol, the result is instance.debugDescription.
  • An unspecified result is supplied automatically by the Swift standard library.

Int conforms to CustomStringConvertible, so String(describing: someInt) is the same as someInt.description.

Now lets look at String(someInt), this calls this initializer:

public init<T>(_ value: T) where T : LosslessStringConvertible

Int also conforms to LosslessStringConvertible. In fact, LosslessStringConvertible inherits from CustomStringConvertible. LosslessStringConvertible specifies only one requirement – an initializer that takes a string.

The docs for init<T>(_:) says this:

Creates an instance from the description of a given LosslessStringConvertible instance.

Even though description is not in a monospace font, I am quite sure (though not 100%) that this refers to the description property of CustomStringConvertible.

Assuming that init<T>(_:) indeed returns the description, String(describing: someInt) always returns the same value as String(someInt).

Note that this does not hold true for all types. It only holds true for types that conforms to LosslessStringConvertible but not TextOutputStreamable. If you have a type that conforms to both, results can be different:

struct A: TextOutputStreamable, LosslessStringConvertible {
    init?(_ description: String) { }

    init() { }

    var description: String {
        return foo
    }

    func write<Target>(to target: inout Target) where Target : TextOutputStream {
        target.write(bar)
    }
}

String(describing: A()) // bar
String(A()) // foo

swift – What is the Different between String(describing: Int) vs String(Int)?

Leave a Reply

Your email address will not be published. Required fields are marked *