Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

swift

I need to convert an Integer value to String. I made a variable with an Integer value and then I have printed that using print. What is the different between the below approaches?

var word = "Count is "
var count = 100

print(word+String(describing: count)); // Count is 100

print(word+String(count)); // Count is 100
like image 498
Senura Dissanayake Avatar asked Jun 09 '18 12:06

Senura Dissanayake


2 Answers

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

That's because print takes a variadic parameter and inserts space as the default separator.

However, let's answer the question anyway.


Answer

It's 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 can't 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).

like image 103
matt Avatar answered Sep 30 '22 21:09

matt


Reading the docs might help!

Here's 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 let's 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
like image 36
Sweeper Avatar answered Sep 30 '22 23:09

Sweeper