Learning Rust by Contrasting with TypeScript: Part 13

Understanding the need for smart pointers.

This article is part of a series starting with Learning Rust by Contrasting with TypeScript: Part 1 .

Let us walk through the examples in the Rust Book Smart Pointers section and contrast them with TypeScript .

The code for this section is available to download in two parts; Rust download and TypeScript download .

Smart Pointers

A pointer is a general concept for a variable that contains an address in memory. This address refers to, or “points at,” some other data. The most common kind of pointer in Rust is a reference… Smart pointers, on the other hand, are data structures that not only act like a pointer but also have additional metadata and capabilities.

— Rust — Smart Pointers

There are a number of different smart pointers, and you a create custom ones, the Rust Book focuses on just three: Box<T>, Rc<T> , and RefCel<T> ; each solving a particular type of problem.

Using Box<T> to Point to Data on the Heap

The most straightforward smart pointer is a box, whose type is written Box<T>. Boxes allow you to store data on the heap rather than the stack. What remains on the stack is the pointer to the heap data.

— Rust — Using Box<T> to Point to Data on the Heap

While the concept itself is straightforward, it is not until the Cons example does the usefulness of Box<T> become clear with their key feature being:

Because a Box<T> is a pointer, Rust always knows how much space a Box<T> needs: a pointer’s size doesn’t change based on the amount of data it’s pointing to.

— Rust — Using Box<T> to Point to Data on the Heap

Because of this feature, we can define a recursive data structure, List :

In TypeScript , never do you concern yourself about data being stored on the stack or the heap; we just think of everything (except maybe primitives) as references. With this in mind, we do not need any additional language feature to support a similar recursive data structure.

Observations:

  • Did try to use a tuple structure (really just an array) and TypeScript complained about the circular reference; interfaces had no such problem

Treating Smart Pointers Like Regular References with the Deref Trait
Running Code on Cleanup with the Drop Trait

The following describes the core concepts:

Smart pointers are usually implemented using structs. The characteristic that distinguishes a smart pointer from an ordinary struct is that smart pointers implement the Deref and Drop traits. The Deref trait allows an instance of the smart pointer struct to behave like a reference so you can write code that works with either references or smart pointers. The Drop trait allows you to customize the code that is run when an instance of the smart pointer goes out of scope.

— Rust — Smart Pointers

Because in TypeScript , we only have one kind of reference and the garbage collector deallocates memory whenever data does not have a reference to it, the problems these traits solve are not relevant in TypeScript .

Rc<T>, the Reference Counted Smart Pointer

Rust has a type called Rc<T>, which is an abbreviation for reference counting. The Rc<T> type keeps track of the number of references to a value which determines whether or not a value is still in use. If there are zero references to a value, the value can be cleaned up without any references becoming invalid.

— Rust — Rc<T>, the Reference Counted Smart Pointer

Again, the usefulness of it is not apparent until you see it solve a particular problem; in this case it is allows you to create variables b and c based on variable a in the following example.

In this case, the Rc::clone(&a) is creating a new pointer to the data on the heap; uses reference counting to avoid having to be concerned about the lifetime of a .

Thinking about TypeScript , this is essentially the same approach the garbage collector handles for you automatically; here again we do not need any additional language feature to essentially do the same thing.

RefCell<T> and the Interior Mutability Pattern

Interior mutability is a design pattern in Rust that allows you to mutate data even when there are immutable references to that data; normally, this action is disallowed by the borrowing rules. To mutate data, the pattern uses unsafe code inside a data structure to bend Rust’s usual rules that govern mutation and borrowing

— Rust — RefCell<T> and the Interior Mutability Pattern

Unfortunately, the example in the Rust Book is a complex use case; so much so, I wrote a simplified use for RefCell<T> inspired by the following:

However, there are situations in which it would be useful for a value to mutate itself in its methods but appear immutable to other code. Code outside the value’s methods would not be able to mutate the value. Using RefCell<T> is one way to get the ability to have interior mutability.

— Rust — RefCell<T> and the Interior Mutability Pattern

Observations:

  • Here the counter variable is immutable, yet we can still call increment which mutates the data held in the RefCell

Again in TypeScript , there is no need for such a construct as all references are mutable.

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章