#set document(title: "6.1 Intro to pointers", author: "Modular Inc. / XYZ Homework") #set page(width: 8.5in, height: auto, margin: 1in) #import "@preview/cetz:0.5.2" #set text(font: ("STIX Two Text", "Libertinus Serif", "New Computer Modern"), size: 10.5pt, lang: "en") #show math.equation: set text(font: ("STIX Two Math", "New Computer Modern Math")) #set par(justify: true, leading: 0.62em, spacing: 0.9em) #set enum(spacing: 1.1em) // room between list items so tall inline fractions don't collide #set list(spacing: 1.1em) #set table(stroke: 0.5pt + rgb("#c7ccd3")) #let BLUE = rgb("#183B6F") // brand navy — section bars + example/solution labels (white on navy 11.09:1) #let ORANGE = rgb("#A94509") // brand primary-700 — AA-safe deep orange for TEXT (5.93:1 on white; raw brand #F37021 is 2.94:1 and must never carry text) #let RED = rgb("#DC2626") // brand error-600 #let GREEN = rgb("#059669") // brand success-600 (decoration only; small green text uses green-text #007942) #show heading.where(level: 1): it => block(width: 100%, above: 0pt, below: 16pt, fill: gradient.linear(BLUE, rgb("#2C5AA0")), inset: (x: 14pt, y: 12pt), radius: 3pt, text(fill: white, weight: "bold", size: 19pt, it.body)) #show heading.where(level: 2): it => block(width: 100%, above: 18pt, below: 10pt, fill: BLUE, inset: (x: 10pt, y: 6pt), radius: 2pt, text(fill: white, weight: "bold", size: 12pt, it.body)) #show heading.where(level: 3): it => text(fill: ORANGE, weight: "bold", size: 12.5pt, it.body) #show heading.where(level: 4): it => text(fill: BLUE, weight: "bold", size: 10.5pt, it.body) #let examplebox(label, title, body) = block(width: 100%, breakable: true, fill: rgb("#EFF1F5"), stroke: 0.5pt + rgb("#CFDDF0"), radius: 4pt, inset: 10pt, above: 12pt, below: 12pt)[ #block(below: 6pt)[#box(fill: BLUE, inset: (x: 6pt, y: 2pt), radius: 2pt, text(fill: white, weight: "bold", size: 8.5pt, label)) #h(0.4em) #strong[#title]] #body] // rail = decorative left rule (raw brand token); labelcolor = AA-safe label text shade #let notebox(label, rail, labelcolor, tint, body) = block(width: 100%, breakable: true, fill: tint, stroke: (left: 3pt + rail), inset: (left: 10pt, rest: 8pt), radius: (right: 4pt), above: 11pt, below: 11pt)[ #text(fill: labelcolor, weight: "bold", size: 7.5pt, tracking: 0.5pt)[#upper(label)] #linebreak() #body] #let solutionbox(body) = block(above: 4pt, below: 8pt)[ #text(fill: BLUE, weight: "bold", size: 8.5pt)[Solution] #linebreak() #body] #let figph(msg) = block(width: 100%, height: 60pt, fill: rgb("#f6f7f9"), stroke: (paint: rgb("#c7ccd3"), dash: "dashed"), radius: 4pt, inset: 10pt)[ #align(center + horizon, text(fill: rgb("#889"), style: "italic", size: 9pt, msg))] // Standardize inlined figure sizes: measure the natural CeTZ canvas, then scale to a // consistent envelope (aspect-aware; see build_typst.py FIG_* constants). Unlike the // print preamble, dimensions are FLOORED: in an editor a user can trim a figure to a // degenerate 1-D shape (a bare line), and w/h or tw/w would then divide by zero. #let _STD_W = 3.5 #let _WIDE_W = 5.6 #let _MAX_H = 3.4 #let _ASPECT_WIDE = 2.2 #let _UPSCALE_MAX = 1.15 #let stdfig(body) = context { let m = measure(body) let w = calc.max(m.width / 1in, 0.01) let h = calc.max(m.height / 1in, 0.01) let tw = if w / h > _ASPECT_WIDE { _WIDE_W } else { _STD_W } let s = calc.min(tw / w, _MAX_H / h, _UPSCALE_MAX) align(center, box(scale(x: s * 100%, y: s * 100%, reflow: true, body))) } #show figure: set block(breakable: false) #set figure(gap: 8pt) #show figure.caption: set text(size: 8.5pt, fill: rgb("#555")) == 6.1#h(0.6em)Intro to pointers A pointer is an indirect reference to one or more values stored in memory. The pointer is a value that holds an address to memory, and provides APIs to store and retrieve values to that memory. The value pointed to by a pointer is also known as a #emph[pointee]. The Mojo standard library includes several types of pointers, which provide different sets of features. All of these pointer types are #emph[generic]—they can point to any type of value, and the value type is specified as a parameter. For example, the following code creates an OwnedPointer that points to an Int value: from std.memory import OwnedPointer var ptr: OwnedPointer\[Int\] ptr = OwnedPointer(100)The ptr variable has a value of type OwnedPointer\[Int\]. The pointer #emph[points to] a value of type Int, as shown in Figure 1. #figure(figph[A local variable, ptr, points to an OwnedPointer\[Int\] which points to an Int pointee. The value of the OwnedPointer is the address of the Int pointee.], alt: "A local variable, ptr, points to an OwnedPointer[Int] which points to an Int pointee. The value of the OwnedPointer is the address of the Int pointee.", caption: none) Accessing the memory—to retrieve or update a value—is called #emph[dereferencing] the pointer. You can dereference a pointer by following the variable name with an empty pair of square brackets: \# Update an initialized value ptr\[\] += 10 \# Access an initialized value print(ptr\[\]) === Pointer terminology Before we jump into the pointer types, here are a few terms you'll run across. Some of them may already be familiar to you. - #strong[Safe pointers]: are designed to prevent memory errors. Unless you use one of the APIs that are specially designated as unsafe, you can use these pointers without worrying about memory issues like double-free or use-after-free. - #strong[Nullable pointers]: some languages use a sentinel value to represent a pointer that doesn't point to anything (a "null pointer"). None of the Mojo standard library pointer types are nullable. To model a nullable pointer, use the #link("https://mojolang.org/docs/std/collections/optional/Optional/")[Optional] type. For example, Optional\[UnsafePointer\] or Optional\[OwnedPointer\]. - #strong[Smart pointers]: own their pointees, which means that the value they point to may be deallocated when the pointer itself is destroyed. Non-owning pointers may point to values owned elsewhere, or may require some manual management of the value lifecycle. - #strong[Memory allocation]: some pointer types can allocate memory to store their pointees, while other pointers can only point to pre-existing values. Memory allocation can either be implicit (that is, performed automatically when initializing a pointer with a value) or explicit. - #strong[Uninitialized memory]: refers to memory locations that haven't been initialized with a value, which may therefore contain random data. Newly-allocated memory is uninitialized. The safe pointer types don't allow users to access memory that's uninitialized. Unsafe pointers can allocate a block of uninitialized memory locations and then initialize them one at a time. Being able to access uninitialized memory is unsafe by definition. - #strong[Copyability]: many pointer types can be copied implicitly (for example, by assigning a value to a variable): copied\_ptr = ptrThe pointer itself is a small amount of data to copy (typically 64 bits), and copying the pointer doesn't copy the pointee—both the original pointer and the copy point to the same memory location and the same value. === Pointer types The Mojo standard library includes several pointer types with different characteristics: - #link("https://mojolang.org/docs/std/memory/pointer/Pointer/")[Pointer] is a safe pointer that points to a single value that it doesn't own. - #link("https://mojolang.org/docs/std/memory/owned_pointer/OwnedPointer/")[OwnedPointer] is a smart pointer that points to a single value, and maintains exclusive ownership of that value. - #link("https://mojolang.org/docs/std/memory/arc_pointer/ArcPointer/")[ArcPointer] is a reference-counted smart pointer that points to an owned value with ownership potentially shared with other instances of ArcPointer. - #link("https://mojolang.org/docs/std/memory/unsafe_pointer/UnsafePointer/")[UnsafePointer] points to one or more consecutive memory locations, and can refer to uninitialized memory. Table 1 summarizes the different types of pointers: #figure(table( columns: 5, align: left, inset: 6pt, table.header([], [Pointer], [OwnedPointer], [ArcPointer], [UnsafePointer]), [Safe], [Yes], [Yes], [Yes], [No], [Allocates memory], [No], [Implicitly #super[1]], [Implicitly #super[1]], [Explicitly], [Owns pointee(s)], [No], [Yes], [Yes], [No #super[2]], [Implicitly copyable], [Yes], [No], [Yes], [Yes], [Nullable], [No], [No], [No], [No], [Can point to uninitialized memory], [No], [No], [No], [Yes], [Can point to multiple values (array-like access)], [No], [No], [No], [Yes], )) #super[1] OwnedPointer and ArcPointer implicitly allocate memory when you initialize the pointer with a value. #super[2] UnsafePointer provides unsafe methods for initializing and destroying instances of the stored type. The user is responsible for managing the lifecycle of stored values. \