Recent Changes - Search:

Oktatás

* Programozás 1
  + feladatsor
  + GitHub oldal

* Szkriptnyelvek
  + feladatsor
  + quick link

Teaching

* Programming 1 (BI)
  ◇ exercises
  ◇ quick link

teaching assets


Félévek

* 2025/26/2
* archívum


Linkek

* kalendárium
* tételsorok
* jegyzetek
* szakdolgozat / PhD
* ösztöndíjak
* certificates
* C lang.
* C#
* D lang.
* Java
* Nim
* Nim2
  + exercises
* XC=BASIC
* old
  ◇C++, ◇Clojure, ◇Scala


[ edit | logout ]
[ sandbox | passwd ]

Value semantics

See also Part 2: Reference semantics

Value types have value semantics, reference types have reference semantics.

The question is how a type behaves when copied or passed around.

(1) Primitive types

Primitive types have value semantics.

var
  a: int = 3
  b: int

b = a         # `b` is a copy
echo a        # 3
echo b        # 3

b = 5         # it has nothing to do with `a`
echo a        # 3
echo b        # 5

(2) string type

The string data type has value semantics.

var
  s: string = "hello"
  t: string = s         # a copy is made

t[0] = 'X'

echo s      # hello

echo t      # Xello

Pass it to a proc:

proc add_something(s: string) =
  #s.add("!!!")     # ERROR: `s` is immutable
  discard

var
  text = "hello"

echo text               # hello
add_something(text)     # (passed by value)
echo text               # hello

The string has value semantics, so when you pass a string to a proc, in theory, a copy is made of it. However, in practice, the compiler will often optimize away the copy and pass a hidden pointer instead. But this is an optimization of the compiler.

So, in practice, passing a string to a proc is not expensive.

(3) seq type

The seq type is similar to string. It also has value semantics!

var
  a: seq[int] = @[1, 2, 3]

  b: seq[int] = @[]

b = a               # a complete copy is made

echo a              # @[1, 2, 3]
echo b              # @[1, 2, 3]

b[0] = 99
echo a              # @[1, 2, 3]
echo b              # @[99, 2, 3]

(4) array type

The array type has value semantics.

var
  a: array[3, int] = [1, 2, 3]

  b: array[3, int]

b = a               # a copy is made

echo a              # [1, 2, 3]
echo b              # [1, 2, 3]

b[0] = 99
echo a              # [1, 2, 3]
echo b              # [99, 2, 3]

In C, you cannot assign an array to another array. Here, it works.

(5) object type

The object type also has value semantics!

type
  Person = object
    name: string
    age: int

var
  a: Person = Person(name: "Anna", age: 20)
  b: Person

b = a             # full, independent copy

echo a            # (name: "Anna", age: 20)
echo b            # (name: "Anna", age: 20)

b.age = 18
echo a.age        # 20    (didn't change)
echo b.age        # 18

Value types with let enjoy double protection

With a let value type you get both:

  • the value itself is immutable — you can't modify the value (content) of the variable
  • the variable cannot be re-assigned — you can't "point" it to something else
let
  a = 3
  b = 5

# a = 20        # ERROR: its value cannot change

# a = b         # ERROR: you cannot re-assign the variable

Passing a value type variable to a proc

In Nim, formal parameters are implicitly let variables. What comes from this?

  • If you call a proc with a value type, then it is passed by value. Since it is assigned to a let variable on the formal parameter list, inside the proc that let variable has double protection.
  • The compiler can apply tricks behind the scenes to pass arguments cheaply. When you pass a big value type argument, then it won't make a complete copy of it. Instead, it will pass it with a hidden pointer. Thus, even if you pass things by value, it can be a cheap operation.

Example: you pass a seq to a proc.

proc something(li: seq[int]) =
  li = @[1, 2, 3]    # ERROR: `li` cannot be re-assigned
  li[0] = 99         # ERROR: the sequence (content) cannot be changed
  # ...

Modifying a value type variable inside a proc

If you want to modify the value type variable on the call site, then you must use var on the formal parameter list:

#              vvv
proc modify(x: var int) =
  x *= 10

var
  a: int = 3

echo a          # 3
modify(a)
echo a          # 30

Now, in the proc, x is just an alias of the variable the proc was called with. Here, x is an alias of the variable a. When we modify x, we modify a directly on the call site.

However, it only works if the variable on the call site is mutable (var). Otherwise, it can't be changed:

proc modify(x: var int) =
  x *= 10

let             # !!! it's let this time, not var => immutable
  a: int = 3

modify(a)       # ERROR: `a` is immutable, cannot be modified

It produces a compile error.


Where are these value type variables stored in the memory?

  • a local int (char, etc.), object, arraystack
  • the elements of a seq or stringheap (always)
  • the header of a seq or stringstack

The compiler doesn't freely choose where to put things. The rules are fixed by the type system.

What the compiler does choose freely is the calling convention — whether to actually copy a value type when passing it to a proc, or to pass a hidden pointer instead as an optimization. But that's about how arguments are passed, not where variables are stored.

So to summarize:

  • where things are stored → determined by the type, not the compiler's mood
  • how arguments are passed → the compiler may optimize to avoid copies

Python's list vs Nim's seq

The two data structures are very similar. They both are dynamic arrays. The reference (Python) / header (Nim) is on the stack, the elements are on the heap.

However! In Python, the list is an object. And objects in Python are reference types! In Nim, a seq is a value type!

That is: if something is a value type, it doesn't necessarily mean that it's stored on the stack. Nim's seq is implemented the way that the elements are stored on the heap. But, a seq has value semantics.

"value type" ≠ "stored on the stack"

Python's list vs Nim's seq
# list has reference semantics

a = [1, 2, 3]
# `a` is a reference, stored on the stack
# [1, 2, 3] is a list object, stored on the heap
# `a` points to the list object
b = a
# `b` points to the same place where `a` points
# both references point to the very same list object

b[0] = 99
print(b)  # [99, 2, 3]

print(a)  # [99, 2, 3]     !!! changed
# seq has value semantics

var
  a = @[1, 2, 3]
  b = a

# `b` is a copy of `a`

b[0] = 99
echo b        # @[99, 2, 3]
# the copy is modified

echo a        # @[1, 2, 3]
# `a` remains unchanged

A value type variable cannot be nil

The special value nil is only valid for reference types in Nim. Value types always hold an actual value, never nil.

type
  Book = object
    title: string

var
  a: int = nil                # ERROR
  s: string = nil             # ERROR
  numbers: seq[int] = nil     # ERROR
  b: array[3, int] = nil      # ERROR
  o: Book = nil               # ERROR

Look at its bright side: you never have to worry about nil checks when working with value types.

Cloud City

  

Blogjaim, hobbi projektjeim

* The Ubuntu Incident
* Python Adventures
* @GitHub
* heroku
* extra
* haladó Python
* YouTube listák


Debrecen | la France


[ edit ]

Edit - History - Print *** Report - Recent Changes - Search
Page last modified on 2026 April 12, 22:10