如何获得对 Nim 中现有对象的安全引用?

Dav*_*oty 5 nim-lang

我编写了一个以两个对象作为参数的Nim过程var。它们每个都是一个带有int字段的对象level。在执行程序的实际工作之前,我想将参数按具有较大 的顺序排列level,因此我这样做:

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
Run Code Online (Sandbox Code Playgroud)

这似乎适用于我的应用程序,但我注意到在Nim 文档中,该ptr类型称为“不安全”,建议仅用于低级操作。有一个“安全”的引用类型refref除非你真的想手动管理内存,否则建议使用它。

我不想进行手动内存管理,我希望 Nim 垃圾收集器为我处理这些参数的内存,但我没有看到让ref这两个参数安全的方法。

我真的希望能够根据变量largesmall而不是param1和来编写算法(这比我展示的简单代码复杂得多)param2。否则,如果我只能引用参数param1and param2,而不为它们制作这些别名,我将不得不复制和粘贴相同的算法两次以分别处理情况param1.level < param2.levelparam1.level >= param2.level

有没有更惯用的 Nim 方法来做这样的事情,而不使用ptr类型?

Grz*_*icz 5

除非执行对象的副本,否则您无法将安全的对象变为不安全的对象,反之亦然。

普通变量存储在堆栈中,因此当它们的函数作用域存在时将被销毁,但可以将引用存储到全局变量中并在以后访问。如果这是可能的,为了使其安全,编译器/语言需要知道某种从堆栈中提取变量的方法,以便在其作用域存在后仍然有效,或者在背后手动执行复制,或者其他一些神奇的方法事物。

这也是取变量地址不安全的原因。您不能保证它的生命周期,因为您可以将该地址存储在其他地方并稍后尝试使用它。但是,就内存保证而言,这些变量至少应在您的 proc 调用期间保持活动状态,因此在该 proc 中使用这些地址别名应该是安全的,而不必担心。

也就是说,您可以重写代码以使用执行检查的中间代理 proc,从而在每个slot 中传递正确的变量。中间过程保证其中一个变量总是很大,你可以避免使用不安全的引用:

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()
Run Code Online (Sandbox Code Playgroud)