ch44.md 2.2 KB

Chapter 44. Smart pointers

As we can see, Isolated[T] is rather hard to work with and effectively turns an existing ref into a “unique ref”. It is most useful when the ref type stems from a library that we have no control over. If we have the control over the used pointer type and the pointer is used in a concurrent setting then ref should be avoided and instead a more refined “smart pointer” type should be used. For example, the “smartptrs” module from the “threading” package provides the SharedPtr, ConstPtr and UniquePtr types. A SharedPtr is a reference-counted pointer type that uses atomic instructions and thus can be shared between threads. A ConstPtr is a SharedPtr that enforces that the data it wraps can only be used for read accesses. And finally a UniquePtr is a pointer that has a single owner and can only be moved around. The following snippet outlines how SharedPtr can be used to create a singly linked list: import threading/smartptrs type

MyList = object   1
   data: string
   next: SharedPtr[MyList]       2

var head, tail: SharedPtr[MyList] 3 proc send(x: sink string) =

withMyLock headLock:
   tail = newSharedPtr MyList(data: x, next: move tail) 4
  if head.isNil: head = tail 5
                                                                       271

1 The node type a linked list consists of. 2 The next field is of type SharedPtr[MyList] so that there can be more

references to it than just the owning reference. This makes list traversals
convenient to write as we can avoid the destructive moves during
traversal.

3 The head and tail of the list have to be of type SharedPtr[MyList] too. 4 Appending to the list works much like insertion in the Isolated[T] case. 5 Since a SharedPtr supports a copy operation, both head and tail can point

to the same object.

The program using SharedPtr arguably can be a little easier than its Isolated variant, but both are no match for a seq container that is wrapped in a lock or a dedicated channel data structure. Pointers are hard to use, especially in a multithreading setting. Modern Nim code avoids pointers for this reason. It is far easier to program in a world of values with restricted aliasing capabilities.