# Chapter 31. View Types  View types are more effective with "strict funcs". A view type is a type that is or contains one of the following types: • lent T (view into T) • openArray[T] (pair of (pointer to array of T, size)) For example: type View1 = openArray[byte] View2 = lent string View3 = Table[openArray[char], int] Exceptions to this rule are types constructed via ptr or proc. For example, the following types are not view types: type NotView1 = proc (x: openArray[int]) NotView2 = ptr openArray[char] NotView3 = ptr array[4, lent int] The mutability aspect of a view type is not part of the type but part of the locations it’s derived from. A view is a symbol (a let, var, const, etc.) that has a view type. Nim allows view types to be used as local variables. In the current implementation this feature needs to be enabled via {.experimental: "views".}. 219 A local variable of a view type borrows from the locations and it is statically enforced that the view does not outlive the location it was borrowed from. For example: {.experimental: "views".} proc take(a: openArray[int]) = echo a.len proc main(s: seq[int]) = var x: openArray[int] = s # 'x' is a view into 's' # it is checked that 'x' does not outlive 's' and # that 's' is not mutated. for i in 0 .. high(x): echo x[i] take(x) take(x.toOpenArray(0, 1)) # slicing remains possible let y = x # create a view from a view take y # it is checked that 'y' does not outlive 'x' and # that 'x' is not mutated as long as 'y' lives. main(@[11, 22, 33]) A local variable of a view type can borrow from a location derived from a parameter, another local variable, a global const or let symbol or a thread- local var or let. Let p be the proc that is analyzed for the correctness of the borrow operation. Let source be one of: • A formal parameter of p. Note that this does not cover parameters of inner procs. • The result symbol of p. • A local var or let or const of p. Note that this does not cover locals of inner procs. • A thread-local var or let. • A global let or const. 220 • A constant array/seq/object/tuple constructor. 31.1. Path expressions A location derived from source is then defined as a path expression that has source as the owner. A path expression e is defined recursively: • source itself is a path expression. • Container access like e[i] is a path expression. • Tuple access e[0] is a path expression. • Object field access e.field is a path expression. • system.toOpenArray(e, ...) is a path expression. • Pointer dereference e[] is a path expression. • An address addr e, unsafeAddr e is a path expression. • A type conversion T(e) is a path expression. • A cast expression cast[T](e) is a path expression. • f(e, ...) is a path expression if f's return type is a view type. Because the view can only have been borrowed from e, we then know that owner of f(e, ...) is e. If a view type is used as a return type, the location must borrow from a location that is derived from the first parameter that is passed to the proc. See Section 21.10, “Var return type” for details about how this is done for var T. A mutable view can borrow from a mutable location, an immutable view can borrow from both a mutable or an immutable location. If a view borrows from a mutable location, the view can be used to update the location. Otherwise it cannot be used for mutations. The duration of a borrow is the span of commands beginning from the assignment to the view and ending with the last usage of the view. For the duration of the borrow operation, no mutations to the borrowed locations may be performed except via the view that borrowed from the location. The borrowed location is said to be sealed during the borrow. 221 {.experimental: "views".} type Obj = object field: string proc dangerous(s: var seq[Obj]) = let v: lent Obj = s[0] # seal 's' s.setLen 0 # prevented at compile-time because 's' is sealed. echo v.field The scope of the view does not matter: {.experimental: "views".} type Obj = object field: string proc valid(s: var seq[Obj]) = let v: lent Obj = s[0] # begin of borrow echo v.field # end of borrow s.setLen 0 # valid because 'v' isn't used afterward The analysis requires as much precision about mutations as is reasonably obtainable, so it is more effective with the experimental strict funcs feature (see Chapter 30, Strict funcs). In other words --experimental:views:option: works better with --experimental:strictFuncs:option:. The analysis is currently control flow insensitive: proc invalid(s: var seq[Obj]) = let v: lent Obj = s[0] if false: s.setLen 0 echo v.field In this example, the compiler assumes that s.setLen 0 invalidates the borrow operation of v even though a human being can easily see that it will never do that at runtime. 222 31.2. Start of a borrow A borrow starts with one of the following: • The assignment of a non-view-type to a view-type. • The assignment of a location that is derived from a local parameter to a view-type. 31.3. End of a borrow A borrow operation ends with the last usage of the view variable. 31.4. Reborrows A view v can borrow from multiple different locations. However, the borrow is always the full span of v's lifetime and every location that is borrowed from is sealed during v's lifetime.