Git Product home page Git Product logo

swift-structs-reading-nyc-web-111819's Introduction

Structs

VeronicaRoth

Becoming fearless isn't the point. That's impossible. It's learning how to control your fear, and how to be free from it. -Veronica Roth

Overview

In this lesson. we'll cover the basics about Swift's structs.

Learning Objectives

  • Create and describe the syntax of structs
  • Distinguish structs from classes
  • Explain the difference between reference and value types when assigning each to variables and constants
  • Change a struct's properties

Structs

In the last few lessons, you've learned how you can use classes to make your own data types in Swift, complete with their own properties and behaviors (known formally as methods). As a reminder, take a look at how you might create a class to represent a Person. It would probably look something like this:

class Person {
    let firstName: String
    let lastName: String
    var fullName: String {
        return "\(firstName) \(lastName)"
    }

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }

    func goForARun() {
        print("I love running!")
    }
}

You can create instances of that class pretty easily:

let jim = Person(firstName: "Jimbo", lastName: "Guiseppe")
print(jim.fullName)
// prints "Jimbo Guiseppe"
jim.goForARun()
// prints "I love running!"

In Swift, structs are very similar. They are data types that have properties, methods, and initializers. In fact, you declare them almost the same as you do a class, except you use the struct keyword instead of class. Here's how you could define a Person struct:

struct Person {
    let firstName: String
    let lastName: String
    var fullName: String {
        return "\(firstName) \(lastName)"
    }

    func goForARun() {
        print("I love running!")
    }
}

And here's how you could create an instance of your Person struct:

let jim2 = Person(firstName: "Jimbo", lastName: "Guiseppe")
print(jim2.fullName)
// prints "Jimbo Guiseppe"
jim.goForARun()
// prints "I love running!"

Not much different from a class, huh? The biggest difference you may have noticed is that the Person struct does not have an initializer. Unlike classes, you don't have to define an initializer for a struct. If you don't define one, a default one will be created that simply takes all stored (not computed) properties as parameters, and assigns those parameters to the appropriate property. You can define your own initializer, though, if you want it to do more than that—just like a class.

So why use a struct instead of a class? Are default initializers the only benefit structs have?

Nope! Structs behave a bit differently than classes. Classes are reference types, whereas structs are value types.

Wait, reference types? Value types? What does that gobbledygook mean? Well, it's probably best to illustrate with an example.

First, let's go back to our example of a Person class. This time, though, make the properties variables, so that you can change them after you have initialized the class:

class Person {
    var firstName: String
    var lastName: String
    var fullName: String {
        return "\(firstName) \(lastName)"
    }

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
}

Now, create an instance of this Person called person1, then create a variable called person2 and assign person1 to it, like this:

var person1 = Person(firstName: "Luke", lastName: "Skywalker")
var person2 = person1

Now try printing out their full names:

print(person1.fullName)
// prints "Luke Skywalker"
print(person2.fullName)
// prints "Luke Skywalker"

"Luke Skywalker" is printed twice. Pretty much what you expected, right? Now, try changing person1's firstName and lastName, and then printing the fullNames of both person1 and person2 again:

person1.firstName = "Han"
person1.lastName = "Solo"
print(person1.fullName)
// prints "Han Solo"
print(person2.fullName)
// prints "Han Solo"

"Han Solo" is printed twice! Is this what you expected?

When you instantiate an object, that instance is stored once in memory. person1 points to that instance in memory. When you assign person1 to person2, person2 points to that same instance! Which means that if you change a property in person1, that change will be reflected in person2—because they are pointing to the same object!

Think of it like a house. If I tell you where I live, I can say, "I live at 292 West 10th Street." I can also tell you, "I live in the blue house on the corner of West 10th Street and 13th Avenue." It's the same house! I'm just describing the location in two ways. If I paint my house red, then both places will be a red house.

Structs are a bit different. Structs are value types, so when you assign the value of one struct to another variable (or constant), it gets copied in memory, and each variable (or constant) point to different instances of that struct. Let's see this in action by first creating a Person struct again. This one will also have variable properties.

struct Person {
    var firstName: String
    var lastName: String
    var fullName: String {
        return "\(firstName) \(lastName)"
    }
}

Now, repeat the previous exercise by creating a new instance of this Person struct, then assign it to another variable, then print out the full names of each variable:

var hero1 = Person(firstName: "Luke", lastName: "Skywalker")
var hero2 = hero1
print(hero1.fullName)
// prints "Luke Skywalker"
print(hero2.fullName)
// prints "Luke Skywalker"

Once again, you'll see "Luke Skywalker" printed to the console twice.

Now, change the first name of hero1 to "Han" and the last name of hero1 to "Solo", and print each variable's full name again. What do you expect to see in the console this time?

hero1.firstName = "Han"
hero1.lastName = "Solo"
print(hero1.fullName)
// prints "Han Solo"
print(hero2.fullName)
// prints "Luke Skywalker"

This time, you see both "Han Solo" and "Luke Skywalker" printed to the console. Why? Well, hero1 and hero2 both point to different copies of the Person struct. Changing hero1's properties does not affect the properties of hero2, because they are different instances!

Coffee Talk

Let's talk about coffee for a second. Here's a pretty simple class for representing a mug of coffee:

class Mug {
    var amountOfCoffee: Double = 0.0
}

An empty mug of coffee is pretty useless, so here's a function to fill it up:

func fillMug(mug: Mug) {
    mug.amountOfCoffee = 10.0
}

And now some code to fill 'er up:

let myMug = Mug()
print(myMug.amountOfCoffee)
// prints "0.0"
fillMug(myMug)
print(myMug.amountOfCoffee)
// prints "10.0"

As you probably expected, the code above first prints "0.0" to the console, then "10.0".

But wait... myMug is a constant, so how can you change the property? When it comes to classes, myMug being constant simply means you can't assign myMug to another instance—but you can still change its properties, because the Mug class is a reference type. In other words, you can't change what myMug points to, but you can change its contents.

Let's try to do the same thing with a struct. First, a simple Mug struct, and its accompanying fillMug() function:

struct Mug {
    var amountOfCoffee: Double = 0.0
}

func fillMug(mug: Mug) {
    mug.amountOfCoffee = 10.0
}

Wait...that doesn't even compile! If you try to write that code, you'll get an error stating that "'mug' is a 'let' constant."

Mug is a let constant

What's going on? Remember how structs are value types, so changing the value of a property doesn't affect other copies of that struct? When you pass a struct into a function, that function receives a copy of the struct. Changing that copy's properties won't affect anything outside of that function (it's a copy, after all), so Swift won't even bother to let you do that.

In order to change the properties of a struct, you need to make fillMug() a method. You could modify the Mug struct to look like this:

struct Mug {
    var amountOfCoffee: Double = 0.0

    func fillMug() {
        amountOfCoffee = 10.0
    }
}

Oy...we're still getting an error here, though.

self is immutable

"Self is immutable"? What does that mean?

In case you missed it before, structs are value types. Changes in properties are not reflected on copies of that struct, so by default Swift doesn't even allow instances of a struct to modify its own properties.

So what, you're just hosed? Doomed to live a caffeine-free life?

Not exactly. You can change properties within a struct's methods if you mark them with the mutating keyword:

struct Mug {
    var amountOfCoffee: Double = 0.0

    mutating func fillMug() {
        amountOfCoffee = 10.0
    }
}

Now you're free to fill up your mug:

var myMug1 = Mug()
print(myMug1.amountOfCoffee)
// prints "0.0"
myMug1.fillMug()
print(myMug1.amountOfCoffee)
// prints "10.0"

What if you create a Mug constant?

let myMug2 = Mug()
myMug2.fillMug()

Nope! You can't do that.

Constant Mug

Unlike classes, a constant struct's properties cannot be changed—not from outside the struct, not even from within the struct's own methods, even if they're marked as mutating. Once a struct is constant, it is constant. It can't change.

Structs aren't the only value types in Swift: Enums are value types, too. You'll actually encounter value types quite a bit. Many of Swift's core data structures, including strings, arrays, and dictionaries, are structs, too.

As you become more accustomed to Swift, you'll start to figure out when you should use classes and when you should use structs. For right now, it's generally easier to favor structs over classes. As you grow as a Swift programmer, though, you'll find many uses for both.

View this lesson on Learn.co

View Swift Structs Reading on Learn.co and start learning to code for free.

swift-structs-reading-nyc-web-111819's People

Contributors

jimcampagno avatar mdippery avatar annjohn avatar ianrahman avatar jthnyc avatar mimicatcodes avatar

Watchers

 avatar Mohawk Greene avatar Victoria Thevenot avatar Bernard Mordan avatar Otha avatar raza jafri avatar  avatar Joe Cardarelli avatar The Learn Team avatar  avatar  avatar Ben Oren avatar Matt avatar Antoin avatar Alex Griffith avatar  avatar Amanda D'Avria avatar  avatar Ahmed avatar Nicole Kroese  avatar Kaeland Chatman avatar  avatar Lisa Jiang avatar Vicki Aubin avatar Maxwell Benton avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.