Constants, Variables, and Types
In this chapter, you’ll learn how you can store values in the computer’s memory using constants and variables. You’ll also learn how constants and variables have types associated with them, and how these types help you write safer code.
Constants
A constant is a name associated with a value in memory. The following example declares a constant named answer
which holds a value of 42:
let answer = 42
This declaration allocates a piece of memory large enough to hold an integer, and assigns it the value 42. The name answer
now refers to this value in memory.
The diagrams in this course represent constants and variables as labeled boxes. The box represents a piece of memory and shows its contents. The label shows the name that refers to this piece of memory:
![A constant represented as a box](/courses/pws-fundamentals/images/constants-variables-types/constant-light.png)
You can use a constant in an expression by writing its name:
2 * answer + 1
Swift reads the value of the constant from memory and uses that value to evaluate the expression. Remember to use the print
function if you want to see the value of this expression in your terminal:
print(2 * answer + 1)
So why should you use constants? Wouldn’t it be easier just to use their values instead?
Constants are a great way to represent parameters. Suppose you want to calculate the time it takes for an object dropped from a tall building to hit the ground. The following expression calculates this time for a building with a height of 100 meters:
sqrt(2 * 100.0 / 9.81)
Here, 100.0 is the height of the building, and 9.81 is the Earth’s gravity. By storing these values in constants, the code becomes a lot clearer:
let height = 100.0
let gravity = 9.81
sqrt(2 * height / gravity)
You can improve this even more by also storing the result in a constant:
let height = 100.0
let gravity = 9.81
let time = sqrt(2 * height / gravity)
print(time)
When you represent parameters as constants instead of literals, you don’t have to change any calculations when you want to try different parameters. Simply change the parameters and rerun the program:
let height = 120.0
let gravity = 9.81
let time = sqrt(2 * height / gravity)
print(time)
Another great use for constants is storing intermediate results. The next example calculates the distance of a throw, given the angle of the throw and the object’s initial velocity:
let angle = 45.0
let velocity = 2.0
let gravity = 9.81
let radians = (angle / 180) * .pi
let vx = velocity * cos(radians)
let vy = velocity * sin(radians)
let time = (2 * vy) / gravity
let distance = vx * time
print(distance)
This example first declares its parameters as constants. Then, it declares several more constants to store intermediate results. Finally, it uses these constants to calculate the distance of the throw as vx * time
.
Using constants in this way reduces the complexity of your code. For comparison, here’s the calculation of distance
without any intermediate results:
let distance = velocity * cos((angle / 180) * .pi) *
(2 * velocity * sin((angle / 180) * .pi)) / gravity
Use constants whenever they make your code easier to understand. Don’t worry about wasting memory; the compiler removes unnecessary constants when it compiles and optimizes your code.
Variables
Like a constant, a variable is a name associated with a value in memory. The declaration of a variable uses the keyword var
instead of let
:
var unitsInStock = 12
This code declares a variable named unitsInStock
with an initial value of 12:
![A variable represented as a box](/courses/pws-fundamentals/images/constants-variables-types/variable-light.png)
Unlike constants, variables are mutable: their value can change as the program runs. To change the value of a variable, you use the assignment operator (=
). The following example reduces the value of unitsInStock
by one:
unitsInStock = unitsInStock - 1
The assignment operator evaluates the expression on its right-hand side and stores the result in the variable on its left-hand side:
![unitsInStock has been changed to 11](/courses/pws-fundamentals/images/constants-variables-types/assignment-light.png)
Note
You only use the keyword var
to declare a new variable. Don’t repeat it when you assign a value to an existing variable. Doing so is a common beginner’s mistake.
Variables are ideal for storing values that change as your program runs. However, for all other values, you should prefer constants. Here’s why:
- Constants protect against accidental changes. You’ll see an error when you attempt to mutate a constant.
- The compiler produces more performant code when it knows a particular value is constant. It can even remove unnecessary constants entirely.
- By only using variables for values that change, your code becomes easier to understand. Anyone reading the code can tell at a glance which values your program will change and which are constant.
Compound assignment
The previous example used the current value of unitsInStock
to calculate its new value:
unitsInStock = unitsInStock - 1
This is such a common operation that Swift offers a compound assignment operator (-=
) for it:
unitsInStock -= 1
This operator reads the value of unitsInStock
, subtracts one, and stores the result back in unitsInStock
.
There are plenty more compound assignment operators for you to discover. The next example shows the compound assignment operators for addition (+=
), multiplication (*=
), and division (/=
). Each compound assignment is followed by its equivalent regular assignment:
unitsInStock += 1
unitsInStock = unitsInStock + 1
unitsInStock *= 2
unitsInStock = unitsInStock * 2
unitsInStock /= 2
unitsInStock = unitsInStock / 2
As you can tell, compound assignments are easier to read, which is why you should prefer them over regular assignments.
Naming rules and guidelines
When picking a name for a constant or variable, these are the rules you must adhere to:
- The name cannot be a reserved keyword, such as
let
,var
, orimport
. These words hold a special meaning in Swift. - Names may contain digits, but not as the first character. This means
player1
is a valid name, but1up
is not. - Names cannot contain spaces. Most other Unicode characters are allowed.
On top of these rules, the following guidelines are equally important:
- Start the name with a lowercase letter. If the name consists of multiple words, start the extra words with an uppercase letter and don’t separate them. This naming style is known as camel case, of which
unitsInStock
is an example. - Names should be descriptive and informative. Don’t try to make your names as short as possible. Instead, focus on writing code that’s easy to read and understand.
- Use English words so that your code fits in with Swift’s keywords and function names.
Types
In Swift, every constant and variable has a type associated with it. This type determines the values you can assign to that constant or variable.
You decide the type of a constant or variable when you declare it. However, most declarations don’t require an explicit type. Thanks to a feature called type inference, Swift can infer and assign the correct type for you.
Here’s an example from earlier:
var unitsInStock = 12
From this declaration, Swift infers the type of unitsInStock
as Int
, which is the default type for integers. This means unitsInStock
can only hold integers, not floating-point numbers:
unitsInStock = 11.5
The size of an Int
depends on your hardware, but it’s usually 64 bits. Swift also has types for fixed-width integers (Int8
, Int16
, ...) and unsigned integers (UInt
, UInt8
, ...), but unless you have a good reason to use them, stick with Int
.
Next, consider the following declaration:
let angle = 45.0
Here, Swift infers the type of angle
as Double
, which is the default type for floating-point numbers.
As its name implies, values of type Double
are 64-bit double-precision floating-point numbers. Swift also has a 32-bit single-precision type Float
, but unless you have a good reason to use it, use Double
instead.
When you initialize a constant or variable with an expression, the type of the expression determines the type of the constant or variable:
let radians = (angle / 180) * .pi
From this expression, Swift infers the type of radians
as Double
. Here’s why:
- The type of
angle
isDouble
. - The type of a literal depends on its context. In this case, Swift knows the division operator requires two operands of the same type, so it infers the type of
180
asDouble
. The result of the division is aDouble
as well. .pi
is shorthand forDouble.pi
, which is a value of typeDouble
. Therefore, both operands for the multiplication are of typeDouble
, and so is the result.
As an exercise, infer the types of all the constants, variables, and expressions in this chapter so far. After you’ve found the correct types, change some of the literals from integer to floating-point — or vice versa — to see how that affects the code.
Most of these changes will result in type errors. Familiarize yourself with these errors as they’re one of the most common kinds of errors you’ll encounter in Swift.
Type annotations
The declaration of a constant or variable can include a type annotation to make the type explicit. Such an annotation is required when Swift is unable to infer a type for you:
var age: Int
var height: Double
This code declares variables age
and height
but doesn’t initialize them. This means the contents of age
and height
are unknown. They can be zero or an unknown bit pattern left by a previous application that used this piece of memory.
For safety reasons, Swift doesn’t let you read uninitialized variables:
print(age)
You must assign initial values to age
and height
before you can use them:
age = 36
height = 184.5
print(age)
print(height)
Type annotations take precedence over type inference. In the following example, the annotation : Double
overrides the inferred type Int
:
var weight: Double = 74
In this particular case, it’s better to use an initial value of 74.0 instead. Rely on type inference where possible and only use type annotations when necessary.
Converting between types
Consider the following expression:
age + height
This expression is invalid because the addition operator requires two operands of the same type, yet age
is an Int
while height
is a Double
. To perform this addition, you need to either convert age
to a Double
or convert height
to an Int
. Here’s how you do that:
Double(age) + height
age + Int(height)
The result of the first expression is a Double
, whereas the result of the second expression is an Int
. These results are different because the conversion of height
from Double
to Int
truncates its value from 184.5 to 184.
Note that these conversions create new values of types Double
and Int
. They don’t affect the values stored in age
and height
, nor their types. The type of a constant or variable is fixed during its declaration and cannot be changed later on.
Type safety
Swift requires every constant and variable to have a type, either explicit or inferred. The compiler checks your code to verify that the values you assign are of the correct type. This strict typing adds an additional layer of safety — known as type safety — to your code. In languages that don’t offer type safety, type errors often go undetected during development and show up as bugs when the program is running.
Swift offers even more safety by not performing implicit type conversions. As you’ve seen in the previous example, type conversions can result in a loss of data. A conversion from Double
to Int
truncates the fractional part. Even a conversion from Int
to Double
can discard information.
Consider the following integer:
1073904854319572423
This integer has more significant digits than can be represented in a Double
. When you convert this number from Int
to Double
, the resulting number is rounded, resulting in a loss of precision:
1.0739048543195725e+18
For these reasons, Swift avoids implicit type conversions. The decision to convert between types and accept the potential data loss is up to you.
Up next
Constants and variables can hold all sorts of data, not just numbers. In the next chapter, you’ll learn how you can use text as data.
Before you continue to that chapter, take a small break from reading and apply what you’ve learned so far to solve the upcoming exercises.