问题
A feature in Swift has been making me wonder for a while... see the code below:
class Clazz {
var foo: String = "abc";
}
let foo: Int = 1
let bar: Int? = 2
let baz: Clazz? = Clazz()
let qux: Clazz = Clazz()
let quux: (Int, String)? = (1, "abc")
foo.0 //1
foo.0.0 //1
bar.0.0.0 //{Some 2} #optional
baz.0 //{{foo "abc"}} #optional
qux.0.0 //{foo "abc"}
quux.0 //{(.0 1, .1 "abc")} #optional
quux.1 //error: doesn't have a member named '1'
quux!.1 //"abc"
As I understand, it is because a tuple with only one element of type T
will be treated as T
rather than (T)
and vice versa, so T.0
is really (T).0
, which returns the first element.
What I don't understand is, if T
is optional, i.e. in the case of T?
or (T)?
, what's the point of being always possible to access .0
and gets exactly itself? Is it an intended feature with some real usecases, or just an unavoidable by-product?
The question is not how to 'fix' it, but why it is like this.
Or, it's because I'm reading it wrong... T?.0
should actually be (T?).0
?
回答1:
- If
x
is a tuple thenx.i
returns the i-th component. - If
x
is not a tuple thenx.0
is treated as(x).0
and returnsx
(andx.i
is an error for anyi > 0
).
An optional is not a tuple, so x.0
returns x
for any optional type.
This applies to your
let quux: (Int, String)? = (1, "abc")
and therefore
quux, quux.0, quux.0.0
are all identical, and quux.1
is an error. quux!
is a tuple, therefore
quux!.0 == 1
quux!.1 == "abc"
Relevant part of the documentation:
Tuple Type
...
If there is only one element inside the parentheses, the type is simply the type of that element. For example, the type of(Int)
isInt
, not(Int)
.
Update for Swift 2.1/Xcode 7.1.1: Apparently, treating any variable as a "one-element tuple" does not work anymore:
let foo: Int = 1
let bar = foo.0 // error: value of type 'Int' has no member '0'
回答2:
You asked: Why is it possible to access the .0 element of an optional tuple in Swift?
I think the answer is: it is possible to access the .0 element of an optional tuple in Swift because it is possible to access the .0 element of any type in Swift.
So you are not seeing a special behavior related to optional tuple types. You're seeing a general behavior related to tuple accessors.
So then the real question is, why is there this general behavior? Why is it in general possible to apply a .0 tuple accessor to any type in Swift? The documentation states that "the type of (Int) is Int, not (Int)", but that does not explain why this is so or why it should work the other way so that a Int can be treated as a (Int).
I am not sure why, but I suspect the answer has to do with the fact tuples are structs, and structs in general are designed to require as little as possible overhead for memory allocation, memory dereferencing, and other runtime costs. That minimum overhead is in fact zero overhead in many cases, and then in some sense the struct is the same as its contents. So if you create a value of type Foo
as follows:
struct Foo<T> { let foo:T }
let myValue:Foo<Int> = Foo(foo: Int(2))
Then the resulting value myValue
at runtime is in some sense represented exactly the same as a plain Int. The surrounding type information and the fact that the Int
is "contained" in a Foo
does not translate into some kind of runtime structure of one thing containing another or pointing to another.
So if a Foo<Int>
is a Int
in this sense, and a tuple is a kind of struct, then a 1-element tuple type like (Int)
is a Int
in the same way. Or in general, a (T)
is a T
. And if a (T)
is a T
, then for many purposes a T
is also a (T)
. And if a T
is a (T)
, then you should be able to access a T
in the same way you can access a (T)
. That is why you can do x.0
for so many possible types of x
.
In this explanation I have been a bit vague about the "some sense" in which a struct type or tuple type is the same as the values it contains. I've alluded to the idea that they resolve to the same runtime structure in some cases. Is it the same in all cases? I don't know. And this equivalence could also be stated in terms of the type system, rather than in terms of compiled runtime structures. So how would one specify this equivalence more precisely? I don't know. I welcome comments! :)
来源:https://stackoverflow.com/questions/26506919/why-is-it-always-possible-to-access-the-0-element-of-an-optional-tuple-in-swift