Key Takeaways
- A
valbinding cannot be reassigned; avarbinding can. - Neither keyword determines whether the referenced object is mutable.
- A
val Arraystill allows element updates, while Scala’s standardListis immutable. - Prefer
valby default. Usevarwhen reassignment makes an algorithm clearer, and keep mutable state narrowly scoped.
These rules follow Scala’s distinction between variable reassignment and collection mutation.
The core difference in Scala val vs var is whether you can assign another value to the same name. A val fixes the binding—the association between a name and its value—while a var allows that association to change. Neither keyword freezes an object’s contents.
The basic distinction is the same in Scala 2 and Scala 3. The examples below use syntax supported by both; try each complete example independently in a Scala REPL, the interactive interpreter. Blocks marked “does not compile” deliberately demonstrate errors.
When to Use val
Use val when you don’t need to replace the value associated with a name. This example declares name and prints its value:
val name = "Joe"
println(name) // JoeNow consider this separate, intentionally non-compiling example:
// Intentionally does not compile
val name = "Joe"
name = "Ted"The compiler rejects the second line because it attempts to reassign a val. That restriction removes one thing you need to track when reading the surrounding code: whether another assignment has replaced its value.
Both val and var can hold the result of a computation. Saving a computed result isn’t what distinguishes them; allowing reassignment is.
When to Use var
Use var when the same name needs to hold a different value later. This Joe-to-Ted example is independent of the preceding examples and runs successfully:
var name = "Joe"
name = "Ted"
println(name) // TedThe assignment replaces the value associated with name. It doesn’t modify the original string.
Reassignment is useful for counters, local accumulators, and algorithms that advance through explicit states. Prefer val unless you have a reason to reassign, rather than treating var as categorically bad.
A val Array Is Still Mutable
Rebinding replaces the value associated with a name. Mutation changes an existing object. An array demonstrates why these are separate operations:
val array = Array(1, 2, 3)
array(0) = 4
println(array(0)) // 4This code runs: the array now contains 4, 2, 3. The name array still refers to the same array; only its first element changed. Scala arrays permit element updates even when their binding uses val.
Replacing the entire array is different. This separate example intentionally does not compile:
// Intentionally does not compile
val array = Array(1, 2, 3)
array = Array(5, 6, 7)The final line attempts to reassign the binding, which val prohibits. You don’t need var merely to update array elements.
Immutable List: Create a New Value Instead
Scala’s standard List is immutable: you don’t replace its elements in place. Instead, operations produce another list while leaving the original unchanged:
val xs = List(1, 2, 3)
val ys = 0 :: xs
println(xs) // List(1, 2, 3)
println(ys) // List(0, 1, 2, 3)The :: operation prepends an element and returns a new list. Neither binding is reassigned, and xs remains unchanged. The new list shares the original list as its tail; creating a new list here doesn’t require copying every existing element.
For immutable application data beyond collections, continue with Scala case classes.
Local var Accumulation vs a Collection Operation
A local accumulator is a reasonable use of var. Here, total belongs to one method invocation and is updated during a sequential traversal:
def sumLocally(numbers: List[Int]): Int = {
var total = 0
numbers.foreach { number =>
total += number
}
total
}
println(sumLocally(List(1, 2, 3))) // 6foreach visits the list’s elements, and the method’s last expression supplies its result. For this particular task, however, the collection already provides a clearer operation:
val numbers = List(1, 2, 3)
val total = numbers.sum
println(total) // 6sum adds the collection’s numeric elements without requiring an accumulator in your calling code. Prefer the operation that expresses your intent directly; keep a local var when it makes a more involved algorithm easier to follow.
To understand the function passed to foreach, see Scala lambdas and higher-order functions.
How to Choose Between val and var
Ask two independent questions: can this name be reassigned, and can this collection’s elements be changed in place?
| Declaration | Reassign the name? | Change elements in place? |
|---|---|---|
val xs = Array(1, 2, 3) | No | Yes |
var xs = Array(1, 2, 3) | Yes | Yes |
val xs = List(1, 2, 3) | No | No |
var xs = List(1, 2, 3) | Yes | No |
Decision rule: start with val. Switch to var only when you need to replace the bound value. Choose a mutable collection separately when you need in-place updates; declaring an immutable list with var doesn’t make that list mutable.
Common Mistakes
- Calling every val an immutable object. The binding cannot be reassigned, but the referenced object may still change.
- Using var just to update an array element. Element updates don’t replace the array binding.
- Assuming local mutation and shared mutation carry the same risks. A counter confined to a sequential method invocation is different from state accessed concurrently by multiple threads.
Concurrent updates to shared mutable state can lose changes through a data race. Keep mutation confined where possible; if you share it, use appropriate synchronization or a concurrency-safe design. A val reference alone doesn’t make a mutable object thread-safe.
FAQ
What is the difference between val and var in Scala?
A val binding cannot be reassigned after initialization; a var binding can. Neither keyword determines whether the referenced object is mutable.
Can you reassign a val in Scala?
No. Assigning another value to an existing val binding causes a compilation error; declare a new binding or use var if reassignment is required.
Can a val Array be modified in Scala?
Yes. Array supports element updates, so array(0) = 4 is allowed even when array was declared with val. Assigning a different array to that same binding is not allowed.
Why should I prefer val over var?
Using val prevents accidental reassignment and gives readers one less changing value to track. Pair it with immutable data when you also want to prevent in-place changes to that data.
Is var always bad practice in Scala?
No. A narrowly scoped var can make an accumulator or stateful algorithm straightforward. Prefer an existing collection operation when it expresses the same intent more clearly, and take extra care with shared mutable state.
Further Reading
- Scala variables and data types — declarations, reassignment, and type inference.
- Scala Book: two types of variables — the same distinction in the Scala 2 guide.
- Tour of Scala: basics — expressions, bindings, functions, and methods.
- Scala collection types — immutable lists and mutable arrays.
- Mutable and immutable collections — collection behavior independent of binding declarations.
- List API reference — prepending, structural sharing, and sum.
- Futures and promises — concurrent callbacks and shared-state hazards.
