Variables
Nushell values can be assigned to named variables using the let, const, or mut keywords. After creating a variable, we can refer to it using $ followed by its name.
Types of Variables
Immutable Variables
An immutable variable cannot change its value after declaration. They are declared using the let keyword,
let val = 42
$val
# => 42
$val = 100
# => Error: nu::parser::assignment_requires_mutable_variable
# =>
# => × Assignment to an immutable variable.
# => ╭─[repl_entry #3:1:1]
# => 1 │ $val = 100
# => · ──┬─
# => · ╰── needs to be a mutable variable
# => ╰────
# => help: declare the variable with `mut`, or shadow it again with `let`However, immutable variables can be 'shadowed'. Shadowing means that they are redeclared and their initial value cannot be used anymore within the same scope.
let val = 42 # declare a variable
do { let val = 101; $val } # in an inner scope, shadow the variable
# => 101
$val # in the outer scope the variable remains unchanged
# => 42
let val = $val + 1 # now, in the outer scope, shadow the original variable
$val # the variable is now shadowed, and its original value is no longer available
# => 43Using let in a Pipeline
let can also be used as a step in a pipeline. At the end of a pipeline, let <name> stores the pipeline's result in the variable and also outputs it:
[3 1 2] | sort | let nums
# => ╭───┬───╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => ╰───┴───╯
$nums | length
# => 3In the middle of a pipeline, let stores its input and passes it through unchanged to the next command:
"hello" | let greeting | str length
# => 5
$greeting
# => helloIf you don't want to see the value at the end of a pipeline, add | ignore, or use the regular let nums = ... form.
Mutable Variables
A mutable variable is allowed to change its value by assignment. These are declared using the mut keyword.
mut val = 42
$val += 27
$val
# => 69There are a couple of assignment operators used with mutable variables
| Operator | Description |
|---|---|
= | Assigns a new value to the variable |
+= | Adds a value to the variable and makes the sum its new value |
-= | Subtracts a value from the variable and makes the difference its new value |
*= | Multiplies the variable by a value and makes the product its new value |
/= | Divides the variable by a value and makes the quotient its new value |
++= | Concatenates a list, string, or binary value to the variable |
Note
+=,-=,*=and/=are only valid in the contexts where their root operations are expected to work. For example,+=uses addition, so it can not be used for contexts where addition would normally fail. The result must also fit the variable's type:/always produces afloat, so$x /= 2is an error when$xholds anint(use$x = $x // 2instead).++=requires that the variable and the value are the same kind: both lists, both strings, or both binary values. To add a single item to a list, use$list ++= [$item].
More on Mutability
Closures and nested defs cannot capture mutable variables from their environment. For example
# naive method to count number of elements in a list
mut x = 0
[1 2 3] | each { $x += 1 } # error: $x is captured in a closure
# => Error: nu::parser::expected_keyword
# =>
# => × Capture of mutable variable.
# => ╭─[repl_entry #1:4:18]
# => 3 │
# => 4 │ [1 2 3] | each { $x += 1 } # error: $x is captured in a closure
# => · ─┬
# => · ╰── capture of mutable variable
# => ╰────To use mutable variables for such behaviour, you are encouraged to use the loops
Constant Variables
A constant variable is an immutable variable that can be fully evaluated at parse-time. These are useful with commands that need to know the value of an argument at parse time, like source, use and plugin use. See how nushell code gets run for a deeper explanation. They are declared using the const keyword
For example, if the file hello.nu in the current directory contains print "Hello from hello.nu", you can source it through a constant:
const script_file = 'hello.nu'
source $script_file
# => Hello from hello.nuChoosing between mutable and immutable variables
Try to use immutable variables for most use-cases.
You might wonder why Nushell uses immutable variables by default. For the first few years of Nushell's development, mutable variables were not a language feature. Early on in Nushell's development, we decided to see how long we could go using a more data-focused, functional style in the language. This experiment showed its value when Nushell introduced parallelism. By switching from each to par-each in any Nushell script, you're able to run the corresponding block of code in parallel over the input. This is possible because Nushell's design leans heavily on immutability, composition, and pipelining.
Many, if not most, use-cases for mutable variables in Nushell have a functional solution that:
- Only uses immutable variables, and as a result ...
- Has better performance
- Supports streaming
- Can support additional features such as
par-eachas mentioned above
For instance, loop counters are a common pattern for mutable variables and are built into most iterating commands. For example, you can get both each item and the index of each item using each with enumerate:
ls | enumerate | each { |elt| $"Item #($elt.index) is size ($elt.item.size)" }
# => ╭───┬─────────────────────────╮
# => │ 0 │ Item #0 is size 812 B │
# => │ 1 │ Item #1 is size 3.4 kB │
# => │ 2 │ Item #2 is size 28 B │
# => │ 3 │ Item #3 is size 11.2 kB │
# => ╰───┴─────────────────────────╯You can also use the reduce command to work in the same way you might mutate a variable in a loop. For example, if you wanted to find the largest string in a list of strings, you might do:
[one, two, three, four, five, six] | reduce {|current_item, max|
if ($current_item | str length) > ($max | str length) {
$current_item
} else {
$max
}
}
# => threeWhile reduce processes lists, the generate command can be used with arbitrary sources such as external REST APIs, also without requiring mutable variables. Here's an example that retrieves local weather data every hour and generates a continuous list from that data. The each command can be used to consume each new list item as it becomes available.
generate {|weather_station|
let res = try {
http get -ef $'https://api.weather.gov/stations/($weather_station)/observations/latest'
} catch {
null
}
sleep 1hr
match $res {
null => {
next: $weather_station
}
_ => {
out: ($res.body? | default '' | from json)
next: $weather_station
}
}
} khot
| each {|weather_report|
{
time: ($weather_report.properties.timestamp | into datetime)
temp: $weather_report.properties.temperature.value
}
}Performance Considerations
Using filter commands with immutable variables is often far more performant than mutable variables with traditional flow-control statements such as for and while. For example:
Using a
forstatement to create a list of 10,000 random numbers:timeit { mut randoms = [] for _ in 1..10_000 { $randoms = ($randoms | append (random int)) } }Result: 1sec 282ms 658µs 250ns
Using
eachto do the same:timeit { let randoms = (1..10_000 | each {random int}) }Result: 10ms 449µs 500ns
Using
eachwith 1,000,000 iterations:timeit { let randoms = (1..1_000_000 | each {random int}) }Result: 1sec 47ms 554µs 250ns
As with many filters, the
eachstatement also streams its results, meaning the next stage of the pipeline can continue processing without waiting for the results to be collected into a variable.For tasks which can be optimized by parallelization, as mentioned above,
par-eachcan have even more drastic performance gains.
Deleting Variables
A variable normally lives until the end of the scope it was declared in. To free a variable earlier, for example one that holds a large amount of data, delete it with unlet. Afterwards, the variable can no longer be used:
let a = 1
let b = 2
unlet $a $b
$a
# => Error: nu::shell::variable_not_found
# =>
# => × Variable not found
# => ╭─[repl_entry #1:4:1]
# => 3 │ unlet $a $b
# => 4 │ $a
# => · ─┬
# => · ╰── variable not found
# => ╰────Variable Names
Variable names in Nushell come with a few restrictions as to what characters they can contain. In particular, they cannot contain these characters:
. [ ( { + - * ^ / = ! < > & |It is common for some scripts to declare variables that start with $. This is allowed, and it is equivalent to the $ not being there at all.
let $var = 42
# identical to `let var = 42`