Hi there !
I am trying to chain procedures that mutate a reference object, but I can't managed to do it.
Here is a minimal example:
type
Child* = ref object
value: int
Parent* = ref object
child: Child
proc set_value*(self: var Child, value: int): Child {.discardable.} =
self.value = value
return self
proc get_child*(self: var Parent): Child {.discardable.} =
var child = Child()
self.child = child
return child
var parent = Parent()
parent.get_child().set_value(10)
This example triggers the following compile error, but I dont understand why I can't mutate the reference return by the get_child procedure.
Error: type mismatch: got <Child, int literal(10)>
but expected one of:
proc set_value(self: var Child; value: int): Child
first type mismatch at position: 1
required type for self: var Child
but expression 'get_child(parent)' is immutable, not 'var'
expression: set_value(get_child(parent), 10)
Any ideas ?
Thanks :)
You need to make the return value var as well.
proc set_value*(self: var Child, value: int): var Child {.discardable.} =
self.value = value
return self
proc get_child*(self: var Parent): var Child {.discardable.} =
var child = Child()
self.child = child
return child
However then var child escapes it's stack frame so I would normally make it a template instead.
template get_child*(self : var Parent) : Child {.discardable.} =
var child = Child()
self.child = child
child
But the template doesn't work with discardable so combine both
proc return_child(child : var Child) : var Child {.discardable.} =
child
template get_child*(self : var Parent) : Child =
var child = Child()
self.child = child
return_child(child)
You don't need var in any of those procs because you are using ref objects and you are modifying the memory pointed to by the reference, not the reference itself.
i.e. this compiles
type
Child* = ref object
value: int
Parent* = ref object
child: Child
proc set_value*(self: Child, value: int): Child {.discardable.} =
self.value = value
return self
proc get_child*(self: Parent): Child {.discardable.} =
let child = Child()
self.child = child
return child
let parent = Parent()
parent.get_child().set_value(10)
Hey !
Thanks you for the quick answers !
As @solo989, just returning var Child like in the snippets below does not work has it triggers a compile error.
proc set_value*(self: var Child, value: int): var Child {.discardable.} =
self.value = value
return self
proc get_child*(self: var Parent): var Child {.discardable.} =
var child = Child()
self.child = child
return child
# Raises:
# Error: 'child' escapes its stack frame; context: 'child'; see https://nim-lang.org/docs/var_t_return.html
Finally, @mratsim solutions works fine and make perfect sens :)
working solution all in one snippet
type
Child* = ref object
value: int
Parent* = ref object
child: Child
proc set_value*(self: var Child, value: int): var Child {.discardable.} =
self.value = value
return self
proc return_child(child : var Child) : var Child {.discardable.} =
child
template get_child*(self : var Parent) : Child =
var child = Child()
self.child = child
return_child(child)
var parent = Parent()
parent.get_child().set_value(10)
However I would use @mratsim's solution instead unless you are using objects instead of ref objects.I know this code isn't meant to be used, but the given answers for var Child were wrong to the point where I have to respond. We want the variable self.child not child. No need for templates or anything
type
Child* = object
value: int
Parent* = object
child: Child
proc set_value*(self: var Child, value: int): var Child {.discardable.} =
self.value = value
self
proc get_child*(self: var Parent): var Child =
self.child = Child()
self.child
var parent = Parent()
parent.get_child().set_value(10)
echo parent.child.value # 10
Again, this isn't idiomatic code, just had to point it out.