Is `String(describing: variable)` defined to always return the same, reliable?

Hi,

I have some older code that expected to get a "nil" for String(describing: variable) , in case the variable is a String. And "Optional(1)" if the variable is an int holding 1.

The usage is string interpolation, like:

print("The String is \(String(describing: str_variable)), number is \(String(describing:int_variable))")

This returned: "The String is nil, number is Optional(1)"

Now, after updating some dependencies and Swift versions updates, this does not hold anymore, and the debug string that was printed returns.

Now this returns: "The String is , number is 1"

Can this happen, or has some oddity been introduced due to the updates?

In general, you should not rely on the format of any print description. However, if you have a value of type String with the value "nil" and a value of type Int? with the value .some(1), then the result of interpolation has not changed.

Thanks, I should probably have added the variables and write the fill code.

var int_variable: Int32?
var str_variable: String?
int_variable = 1
print("The String is \(String(describing: str_variable)), number is \(String(describing:int_variable))")

I double-checked that the string is nil;

Still, the output changed. It was

The String is nil, number is Optional(1)

now I get

The String is , number is 1

I suspect some dependencies changed something, there is a StringView class new, and some other classes, and that might have side effects. Also find a lot of extend String in the code ...

With no dependencies, just a simple script, I'm still seeing The String is nil, number is Optional(1) as of Swift 6.0.1. If you suspect it's your dependencies muddling with String, that is unfortunate, but also a likely candidate it would seem.

1 Like

As both Int32 and String conform to CustomStringConvertible, wouldn't

print("The string is \(str_variable as Any), number is \(int_variable as Any)")

Give the same output?

1 Like

Yes, it does give the same. Thanks!
Have to check now if that changes something on the output I see no, will come back one I did it

Edit: It works even better, as it did before! Thanks !!

its nil, but String(describing:) is ""

(lldb) po dbName
nil
(lldb) expr -l Swift -- String(describing: dbName)
(String) $R0 = ""
(lldb) fr v dbName
(String?) dbName = nil
(lldb) expr -l Swift -- print(dbName)
nil
() $R1 = {}
(lldb) expr dbName == nil
(Bool) $R2 = true
(lldb) expr -l Swift -- String(describing: dbName)
(String) $R3 = ""
(lldb) 

That should not be possible, but it happens.

(lldb) expr -l Swift -- "\(dbName as Any)"
(String) $R5 = "nil"
(lldb) expr -l Swift -- "\(String(describing: dbName))"
(String) $R6 = ""

No idea how to debug into what is going on in the String(describing: dbName) ,

but I guess I use as Any to have the behavior I had

See if you can figure out which overload of String(describing:) that resolves to. It's possible someone put something like in your code or one of you dependencies:

extension String {
  init<T>(describing value: T?) {
    if let value {
      self = String(describing: value)
    } else {
      self = ""
    }
  }
}

Yes, it would be nice if I could debug the constructor to see what is actually called. I tried it with single steps, but that was not helpful in the time budget I could reserve for investigation on this problem.