Tuple VS Struct in Swift

Tuple vs Struct in Swift

This question of a slightly "discussion" nature, but I'll add two points in favour of sometimes preferring tuples over structures.


Native Equatable conformance for limited sized tuples

In Swift 2.2, tuples of up to size 6 will be natively equatable, given that it's members are equatable

  • Proposal SE-0015: Tuple comparison operators

This means tuples will sometimes be the natural choice over using smaller constructs in a limited scope.

E.g. consider the following example, using (1): a structure

struct Foo {
var a : Int = 1
var b : Double = 2.0
var c : String = "3"
}

var a = Foo()
var b = Foo()

// a == b // error, Foo not Equatable

/* we can naturally fix this by conforming Foo to Equatable,
but this needs a custom fix and is not as versatile as just
using a tuple instead. For some situations, the latter will
suffice, and is to prefer. */
func == (lhs: Foo, rhs: Foo) -> Bool {
return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c
}

and (2): a tuple

/* This will be native in Swift 2.2 */
@warn_unused_result
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2
}
/* end of native part ... */

var aa = (1, 2.0, "3")
var bb = (1, 2.0, "3")

aa == bb // true
aa.0 = 2
aa == bb // false

Generic access to different type tuples: more versatile than for different type structures

From the above (compare the == functions) it's also apparent that tuples are easily to work with in the context of generics, as we can access their anonymous member properties using the .0, .1 ... suffixes; whereas for a struct, the easiest way to mimic this behaviour quickly becomes quite complex, needing tools such as runtime introspection and so on, see e.g. this.

Swift Tuples - Different from struct and from each other?

I find it's easiest to conceptualize Swift Tuples as "Anonymous Structs" with a few critical differences. They behave similarly, but a struct has a formal definition and allows more control over mutability, while tuples allow for pattern matching.

Similarities Between Tuples and Structs

  • Both may have any number of members of any type, including closures
  • Both can be constructed inline (see typealias in the code below)
  • Both prevent mutation of any members if declared as constants
  • If a tuple has labeled members, both structs and tuples allow member access by label

Differences Between Tuples and Structs

  • Structs require a definition before use
  • Structs do not allow pattern matching against their members
  • Structs allow mutability of members declared as variables if the instance is a variable
  • Tuples do not allow mutating functions or functions that refer to any of its members
  • Tuples may not implement Protocols
  • If a tuple has anonymous members, its members can be accessed by index, unlike structs

Some code for a playground illustrating these differences and similarities

// All commented code causes a compilation error. Uncomment to view error messages.

struct StructureX {
let a: Int = 0
var b: String = "string"
}

//
// Struct member variability
//
var structureA: StructureX = StructureX()
let structureB: StructureX = StructureX()
//structureA.a = 2 // declared as a constant, instance is variable
structureA.b = "allowed" // declared as a variable, instance is variable
//structureB.a = 2 // declared as constant, instance is constant
//structureB.b = "not allowed" // declared as constant, instance is constant
structureA = structureB // these are the same type
structureA


//
// A tuple can't be used as a literal to construct a struct.
//
//let StructureC: StructureX = (a: 17, b: "nope")


//
// Typealias a labeled tuple and it can be constructed similarly to a struct
//
typealias StructureT = (a: Int, b: String)
var structureD: StructureT = StructureT(a: 0, b: "asdf")
structureD
//structureD = structureA // but they are distinct types



let emptyTuple: () = () // philosophically, isn't this the definition of Void?
print(emptyTuple) // prints as ()
let single: (Int) = (23)
//let namedSingle: (a: Int) = (a: 42)


//
// Tuple Labeled Member Access
//
var labeledTupleA: (a: Int, b: String) = (a: 0, b: "string")
labeledTupleA.0 = 5
labeledTupleA.a
labeledTupleA

var check: (a: Int, b: String)
check = labeledTupleA // same type
check

//
// Tuples can have functions/closures
//
let labeledTupleB: (Int, String, fun: () -> Void) = (0, "string", { () -> Void in
print("hi")
})
labeledTupleB.1
labeledTupleB.fun()
//labeledTupleB.0 = 10 // this tuple is a constant, so all of its members are constant


//
// Tuples with members of the same type, but differet labels are not of the same type
//
var labeledTupleC: (c: Int, d: String) = (c: -1, d: "fail")
//labeledTupleC = labeledTupleA
//labeledTupleC = labeledTupleB


//
// Tuples with anonymous members matching the type pattern of a labeled member tuple are of equivalent type
//
var unlabeledTuple: (Int, String) = (0, "good")
unlabeledTuple = labeledTupleA
unlabeledTuple = labeledTupleC


//
// Tuples with closures may not refer to sibling members
//
var labeledTupleD: (de: Int, df: (Int) -> Void) = (de: 0, df: { (num: Int) -> Void in
//de += num
//self.de += num
print(num)
})

labeledTupleD.de
labeledTupleD.df(1)


//
// Tuples allow pattern matching, Structs do not
//
//switch structureA {
//case (let i, let s):
// print(i, s)
//default:
// break
//}

switch labeledTupleD {
case (_, let closure):
closure(123)
default:
break
}

Tuple vs. Object in Swift

As vadian notes, Apple's advice is that tuples only be used for temporary values. this plays out. If you need to do almost anything non-trivial with a data structure, including store it in a property, you probably do not want a tuple. They're very limited.

I'd avoid the term "object" in this discussion. That's a vague, descriptive term that doesn't cleanly map to any particular data structure. The correct way to think of a tuple is as being in contrast to a struct. In principle, a tuple is just an anonymous struct, but in Swift a tuple is dramatically less flexible than a struct. Most significantly, you cannot add extensions to a tuple, and adding extensions is a core part of Swift programming.

Basically, about the time you're thinking that you need to label the fields of the tuple, you probably should be using a struct instead. Types as simple as "a point" are modeled as structs, not tuples.

So when would you ever use a tuple? Consider the follow (non-existent) method on Collection:

extension Collection {
func headTail() -> (Element?, SubSequence) {
return (first, dropFirst())
}
}

This is a good use of a tuple. It would be unhelpful to invent a special struct just to return this value, and callers will almost always want to destructure this anyway like:

let (head, tail) = list.headTail()

This is one thing that tuples can do that structs cannot (at least today; there is ongoing discussion of adding struct destructuring and pattern matching to Swift).

What's the difference between struct and typealias in swift?

You are comparing a struct with the typealias of a tuple.

A struct is much more powerful than a tuple, here's some advantages only a struct offers

  1. a struct can conform to a protocol, a tuple can't
  2. a struct can have methods, a tuple can't
  3. a struct can have computed properties, a tuple can't
  4. a struct can have initializers, a tuple can't

Considerations

I suggest you to use a struct for your model. It's a better fit for a model value.

Someday you'll need to add a computed property (like birthYear) to your type and you don't want to be constrained by the limitations of simple tuple.

Why use a tuple rather than an array or dictionary in Swift?

A tuple can contain elements of different types. So for example you could declare a tuple containing a String and an Int, while all elements of an array have to be the same type, unless you use AnyObject and type casting.

Single-value-tuple as last member of struct in swift

MusicPlayer is no longer exported as above. As of Xcode 6.3b1

typedef struct MusicEventUserData
{
UInt32 length;
UInt8 data[1];
} MusicEventUserData;

This is much closer to the C declaration. It still does not completely explain how to deal with the API in swift but that is another question.

When should tuples be used instead of objects and vice versa?

According to the book on Swift,

Tuples are useful for temporary groups of related values. They are not suited to the creation of complex data structures.

They define "temporary" through the scope of the data: if a piece of data never leaves the scope of a single method, or a group of methods of the same object, it can be considered temporary, even though it might very well persist through the lifetime of an application.

If your data structure is useful outside a temporary scope - for example, because it needs to be returned from a public method of your class, model it as a class or structure, rather than as a tuple.

Another important consideration is associating behavior with your data: if your object needs to have methods, use classes for them. Use tuples only for data devoid of behavior.



Related Topics



Leave a reply



Submit