If I have to access something like A.M[k] (or an even more complicated "object") several times, I'd like to create a "reference variable" (C++ jargon) R to it such that changing R does change the "underlying" "object". I could define a pointer P to it and use P[] all over the time but that's a bit ugly.
Many thanks for a hint, Helmut
Currently the only way of doing that is through the use of pointers. However, dereferencing is done automatically for fields, so this:
type
Abc = object
a, b: int
c: array[10, int]
var
a = Abc()
r = addr a
a.b = 10
a.c[5] = 3
works. You only need to dereference the variable if you're calling procedures, and even then there's the experimental feature implicitDeref:
{.experimental: "implicitDeref".} # enables implicitDeref for the whole module
proc modify(abc: var Abc) =
abc.c[3] = 2
r.modify()
# you can also use .experimental with .push if you only want to enable it
# for a certain section of your code
{.push experimental: "implicitDeref".}
r.modify()
{.pop.}
r[].modify()
Many thanks!
The big question: is it probably that experimental features stay or become even non-experimental?
fortunately views can put pointers back to their place. They're only very recently implemented and still experimental and thus this example below (which I think is correct) segfaults at runtime 🙁:
{.experimental: "views".}
proc test() =
var
x = 42
y: var int = x
y = 52
echo x
test()
If for your purposes a function-level scope of the "reference variable" is enough, you can simply use var modifier to the formal argument like this
type Abc = object
a, b: int
c: array[10, int]
proc foo(r: var Abc) =
r.a = 42
r.b = 24
r.c[0] = 123
var abc: Abc
foo(abc)
echo "abc.a = ", abc.a
echo "abc.b = ", abc.b
echo "abc.c = ", abc.c
The big question: is it probably that experimental features stay or become even non-experimental?
View types are regarded to be so important that we hope to have them non-experimental in 2021 but that doesn't hold for other experimental features.
You can use template for aliasing
template R: untyped = A.M[k]
R.x = "foo"
R.y = "bar"
byaddr template in std/decls looks as dangerous as pointers.
If you add/resize/delete elements to seq or table, byaddr variables can become invalid pointers. When you add an item to seq or table, it allocate new heap memory if existing heap is not big enough. Following example code runs without runtime errors while it uses invalid pointer.
For example:
import std/decls
var mySeq = @[1, 2]
var r {.byaddr.} = mySeq[0]
echo r
r = 7
echo mySeq[0]
mySeq.add(3)
echo r
r = 777
echo mySeq[0]
output:
1
7
7
7
Table example:
import std/decls, tables
var myTable = {"1": 1}.toTable
var r {.byaddr.} = myTable["1"]
echo r
r = 7
echo myTable["1"]
for i in 2..6:
myTable[$i] = i
echo r
r = 777
echo myTable["1"]
Output:
1
7
0
7
As @slonik_az suggests, creating a new scope that cannot modify or access container by defining a new proc and use reference variable only inside that scope seems safe and simple idea. Also making alias like @mratsim 's code seems safe as long as A.M[k] raise error when k become invalid index/key.
byaddr template in std/decls looks as dangerous as pointers.
That's true, hopefully well-known (cough) and it's the primary reason why we're getting view types.