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 ]

Objects

C and Nim

A basic object definition in Nim is like a C struct type.

type
  Book = object
    author: string
    title: string
    pages: int

var b1: Book    # created on the stack

# b1 = nil  # ERROR

𝥶If the type is object, then instances of this type will be stored on the stack. Data types stored on the stack can't be nil.

When you create an instance, you can't decide where to store it (on the stack or on the heap). It depends upon the type definition. If it's object, then it's stored on the stack. If it's ref object (see later), then it's stored on the heap.

C vs Nim
typedef char* string;

typedef struct
{
    string author;
    string title;
    int pages;
} Book;

int main()
{
    Book b1; // created on the stack
    b1.author = "Asimov";
    b1.title = "Foundation";
    b1.pages = 255;

    // initialization:
    Book b2 = {"Frank Herbert", "Dune", 412};

    return 0;
}
type
  Book = object
    author: string
    title: string
    pages: int

var b1: Book    # created on the stack

b1.author = "Asimov"
b1.title = "Foundation"
b1.pages = 255

echo b1  
# (author: "Asimov", title: "Foundation", pages: 255)

let b2 = Book(author: "Frank Herbert", title: "Foundation", pages: 255)

echo b2  
# (author: "Frank Herbert", title: "Foundation", pages: 255)

𝥶The two examples (C and Nim) are very similar. The variables (b1 and b2) are stored on the stack. It means they are value types. They have value semantics. When you pass it to a procedure, a copy is made. When you use the "=" (assignment) operator, a copy is made.

value semantics
#include <stdio.h>

typedef char *string;

typedef struct
{
    string author;
    string title;
    int pages;
} Book;

void call_sub(Book b)
{
    b.pages = 500;
    // `b` is a copy; we modify the copy
}

int main()
{
    Book b1 = {"Asimov", "Foundation", 255};

    printf("pages: %d\n", b1.pages); // 255
    call_sub(b1);
    printf("pages: %d\n", b1.pages); // 255 (still 255)

    Book b3 = b1; // full copy
    b3.pages = 500;
    printf("b3 pages: %d\n", b3.pages); // 500
    printf("b1 pages: %d\n", b1.pages); // 255 (still 255)

    return 0;
}
type
  Book = object
    author: string
    title: string
    pages: int

proc call_sub(b: Book) =
  #b.pages = 500  # ERROR: `b` is immutable
  # but `b` is a copy of the argument it was called with
  discard

var
  b1: Book = Book(author: "Asimov", title: "Foundation", pages: 255)
  b3: Book

echo "b1 pages: ", b1.pages  # 255
call_sub(b1)
b3 = b1  # full copy
b3.pages = 500
echo "b3 pages: ", b3.pages  # 500
echo "b1 pages: ", b1.pages  # 255 (still 255)

Python and Nim

In Python, we have classes. When you make an instance of a class, the instance will be stored on the heap (and thus it's garbage collected). On the stack you have just a reference that points to the object, which is on the heap.

Java does the same thing.

If you want this kind of behaviour, then the type must be ref object (instead of object).

type
  #      vvv
  Book = ref object
    author: string
    title: string
    pages: int

var b: Book

b = nil    # allowed, `b` is a reference
Python vs Nim
class Book:
    def __init__(self, author, title, pages):
        self.author = author
        self.title = title
        self.pages = pages

    def __str__(self):
        return f"Book({self.author}, {self.title}, {self.pages})"


def main():
    b1 = Book("Asimov", "Foundation", 255)
    print(b1)  # Book(Asimov, Foundation, 255)

    b3 = b1  # b3 and b1 are references
    # pointing to the very same object!
    b3.pages = 500
    print(b3.pages)  # 500
    print(b1.pages)  # 500 (!!!)


if __name__ == "__main__":
    main()
type
  #      vvv
  Book = ref object
    author: string
    title: string
    pages: int

var
  b1 = Book(author: "Asimov", title: "Foundation", pages: 255)
  b3 = b1
  # b3 and b1 are references
  # pointing to the very same object!

b3.pages = 500
echo b3.pages       # 500
echo b1.pages       # 500 (!!!)

𝥶Types defined with the ref keyword are called reference types. When an instance of a reference type is passed to a procedure, then no copy is made of the underlying object. The object is passed by reference.


Combining the two

When you need both the non-ref and ref types, you can do the following:

type
  BookObj = object
    author: string
    title: string
    pages: int
  BookRef = ref BookObj    # no need to repeat the whole definition
  • The convention is to use the Obj suffix for the non-ref name and Ref for the ref name.
  • With BookRef = ref BookObj you don't have to repeat the whole definition.

Pass by reference

In Python, when you create an instance, the object will be stored on the heap, and on the stack you'll have a reference (pointing to the object on the heap). When you pass the object to a procedure, the object will be passed by reference. It means that if you modify the object in the procedure, the call site will see the changes.

In Nim, when you instantiate a ref object, the same thing happens. You get a reference on the stack that points to the object stored on the heap.

Let's see a simplified example. Now the Book type has just one attribute.

pass by reference
class Book:
    def __init__(self, pages):
        self.pages = pages


def modify(b):
    b.pages = 500


def main():
    b1 = Book(255)

    print(b1.pages)  # 255
    modify(b1)
    print(b1.pages)  # 500  (!!!)


if __name__ == "__main__":
    main()
type
  #         vvv
  BookRef = ref object
    pages: int

proc modify(b: BookRef) =
  b.pages = 500

var
  b1 = BookRef(pages: 255)

echo b1.pages       # 255
modify(b1)
echo b1.pages       # 500  (!!!)

Notice the signature of the Nim proc:

proc modify(b: BookRef)

It's not "b: var BookRef", but still, the object on the call site can be modified inside the proc. This proc received the reference of an object (of a ref object), and inside a proc the underlying object can be modified.

In Nim, by default, the formal parameters are read-only. b is also read-only (immutable), but since it's a reference (a pointer), its value is read-only, the memory location it points to. That is, it cannot point to anything else, but the pointed object can change. The reference is immutable, not the pointed object.

Here is a variation of the procedure:

proc modify(b: BookRef) =
  let b3 = BookRef(pages: 50)
  b.pages = 500                    # OK, the pointed object can be modified
  # b = b3                         # ERROR: `b` cannot be re-assigned

Immutable references are strange

As mentioned earlier, a let reference may act strange. The value of the reference (the memory address it points to) is immutable, not the pointed object.

The reference cannot point to anywhere else. But the underlying object may be modified.

let references
type
  #         vvv
  BookRef = ref object
    pages: int

proc modify(b: BookRef) =
  b.pages = 500

let
  b1 = BookRef(pages: 255)
  b3 = BookRef(pages: 50)

echo b1.pages       # 255
modify(b1)
echo b1.pages       # 500  (!!!)

b1.pages = 1000     # OK
echo b1.pages       # 1000 (!!!)

# b1 = b3           # ERROR: `b1` cannot be re-assigned

𝥶Here, b1 is a let reference. Later, you can write "b1.pages = 1000", it's fine.

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 18, 10:45