Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get safe reference to existing object in Nim?

Tags:

nim-lang

I wrote a Nim procedure taking two objects as var parameters. They each are an object with an int field level. Before doing the real work of the procedure, I want to put the parameters in order by which has the larger level, so I do this:

proc subroutine(param1: var MyObject, param2: var MyObject) =
  var large: ptr MyObject = param1.addr
  var small: ptr MyObject = param2.addr

  # If in wrong order by level, swap large and small
  if large.level < small.level:
    large = param2.addr
    small = param1.addr

  # The rest of the proc references, only variables large and small, e.g.,
  large.level += 1
  small.level += 2

This seems to work for my application, but I notice that in the Nim documentation, the ptr type is called "unsafe", and it is suggested to be used only for low-level operations. There's a "safe" reference type ref, and it is suggested to use ref unless you really want to do manual memory management.

I don't want to do manual memory management, and I want the Nim garbage collector to handle the memory for these parameters for me, but I don't see a way to get safe ref's to the two parameters.

I really want to be able to write the algorithm (which is significantly more complex than the simple code I showed) in terms of the variables large and small, rather than param1 and param2. Otherwise, if I can only reference parameters param1 and param2, without making these aliases for them, I'd have to copy and paste the same algorithm twice to separately handle the cases param1.level < param2.level and param1.level >= param2.level.

Is there a more idiomatic Nim way to do something like this, without using a ptr type?

like image 308
Dave Doty Avatar asked Oct 08 '20 01:10

Dave Doty


2 Answers

There is no way you can turn a safe objecto into unsafe and viceversa, except by performing a copy of the object.

Normal variables are stored on the stack and thus will be destroyed when their function scope exists, but a reference can be stored into a global variable and accessed later. If this was possible, to make it safe, the compiler/language would need to know some way of extracting the variable from the stack so that it's still valid after its scope exists, or perform a copy manually behind your back, or some other magical thing.

That's also the reason why taking the address of a variable is unsafe. You can't guarantee its lifetime, because you could store that address somewhere else and try to use it later. However, in terms of memory guarantees, those variables should be kept alive at least for the time of your proc call, so it should be safe to use those address aliases within that proc without worrying.

That said, you could rewrite your code to use an intermediate proxy proc that performs the check and thus passes the correct variables in each slot. The intermediate proc guarantees that one of the variables will always be large, and you can avoid using unsafe references:

type
  MyObject = object
    level: int

proc subroutine_internal(large: var MyObject, small: var MyObject) = 
  assert large.level >= small.level
  large.level += 1
  small.level += 2

proc subroutine(param1: var MyObject, param2: var MyObject) =
  if param1.level < param2.level:
    subroutine_internal(param2, param1)
  else:
    subroutine_internal(param1, param2)

proc main() =
  var
    a = MyObject(level: 3)
    b = MyObject(level: 40)

  subroutine(a, b)
  echo a, b

main()
like image 89
Grzegorz Adam Hankiewicz Avatar answered Nov 15 '22 11:11

Grzegorz Adam Hankiewicz


type
  MyObject = object
    level: int

proc subroutine(param1, param2: var MyObject) =
  if param1.level > param2.level:
    swap(param1, param2)
  echo param1.level
  echo param2.level

var
  p1 = MyObject(level: 7)
  p2 = MyObject(level: 3)

subroutine(p1, p2)
p2.level = 13
subroutine(p1, p2)

Nim has the special swap() proc for that case. Var parameters are passed internally as pointers, so in parameter passing no copy is involved, and swap should do the copy as optimal as possible.

Of course, there can be cases where using ref objects instead of value objects can have advantages. Nim's references are managed pointers. You can find more information in the tutorials, and my one is located at http://ssalewski.de/nimprogramming.html#_value_objects_and_references.

like image 33
Salewski Avatar answered Nov 15 '22 10:11

Salewski