What is the Meaning of the Asterisk * Symbol in Rust?

Have you recently seen the asterisk (*) symbol in Rust while reading someone else’s code and you are not sure what it means? No worries, this article will explain the meaning of * and show you how it can become handy during development.

The asterisk (*) symbol is Rust’s dereference unary operator. The dereference operator extracts the value of a variable omitting the pointer or reference. To use the dereference operator, add the asterisk (*) symbol right before a given variable T. This will result in *T which gets the value of T.

Understanding the Dereference Operator

Take a look at the following example to get a better understanding of the dereference operator.

fn main() {
  let my_number = 1;
  let other_number = *&my_number;

  assert_eq!(my_number, other_number);
}

The previous code assigns the value to other_number by dereferencing the shared reference of my_number (&my_number). Finally, the code asserts that the values of my_number and other_number are equal.

This is the same as duplicating the value of my_number to other_number by implicitly generating a copy.

fn main() {
  let my_number = 1;
  let other_number = my_number;

  assert_eq!(my_number, other_number);
}

If both code snippets are the same, then, is the purpose of the dereference operator to generate a duplicate value?

Kind of.

An important aspect is that the dereference operator is used in pointer types.

Understanding pointers

A pointer is another way to tell what the memory address variables have. An example of an address in memory could be 0x80537ff7fc or 0x80537ff814.

If you want to see the pointer or memory location of a variable use the format {:p} when using the println! macro. This will display the memory location as a hexadecimal number.

fn main() {
   let my_number = 1;
   println!("my_number memory location {:p}", &my_number);
}

Dereferencing values from pointers

Rust has different pointer types. These are:

  • Shared References (&)
  • Mutable References (&mut)
  • Raw pointers (*const and *mut)
  • Smart Pointers

If I bring back the initial code snippet used to show the behavior of the dereference operator,

fn main() {
  let my_number = 1;
  let other_number = *&my_number;

  assert_eq!(my_number, other_number);
}

you will notice the unary (*) operator is used on a shared reference &my_number rather than directly on my_number. If you attempt to user the unary operator in my_number , .i.e., *my_number, you will get the following error:

error[E0614]: type {integer} cannot be dereferenced

This is because the i32 type doesn’t implement the Deref trait.

To use the dereference operator, the type that the dereference operator is applied to needs to implement the Deref trait and DerefMut trait for mutable references.

Since you cannot use the unary (* ) operator on my_number, you will need to get a shared reference (&) of the variable, meaning &my_number to use the dereference operator.

In other words, &my_number will get an address in memory which serves only as a reference of the location of value 1.

Now, since the address of &my_number could be something like 0x6e047ffa14 , to extract the value that is referencing the address 0x6e047ffa14, you will use the dereference unary operator *&my_number, which results in the value of 1.

Derefencing a mutable references

The derefence operator can become handy when updating the value of mutable references. Given the following example,

fn dereferencing_mutable_references() {
    let text = &mut String::from("foo");
    *text = String::from("bar");

    assert_eq!(text, "bar");
}

notice how the unary (*) operator in here is used to assign a new value String::from("bar") with a type of &mut String. This allows you to reassign the value over an over.

fn dereferencing_mutable_references() {
    let text = &mut String::from("foo");
    *text = String::from("one");
    *text = String::from("two");
    *text = String::from("three");
    *text = String::from("four");
    *text = String::from("five");
}

If you attempt to reassign the value of text without using the unary (*) operator, like you see in the following example,

fn main() {
    let text = &mut String::from("foo");
    text = String::from("one");
}

you will get the following error error[E0384]: cannot assign twice to immutable variable text .

Derefencing a smart pointers

Dereferencing Strings

Here is an example of dereferencing a String, which is a smart pointer.

// Strings are considered smart pointers as they own memory and allow you to manipulate
// it as well as having other metadata such as "capacity"
fn dereferencing_strings() {
    let string = String::from("Hello");
    // *string will generate a string literal str without a known size at compilation time
    // Hence, if you attempt,
    // let other_string = *string;
    // the code have errors at compilation time.

    let other_str = &*string;
    let other_string = String::from(&*string);

    assert_eq!(string, other_str);
    assert_eq!(string, other_string);
}

If you paid closed attention to the code, you will find out that *String::from("Hello") or *my_string can’t be used without getting a shared reference & of the value. The reason for this is, *my_string will generate a string literal (str ). Unfortunately, it is not possible for a variable to own a string literal. That’s why it is necessary to get the shared reference of *my_string or &*string;

Derefererencing Structs

Structs are also smart pointers. Custom structs can’t use the dereference operator as they don’t implement the Deref trait by default as you can see in the following snippet of code,

#[derive(Debug)]
struct MyStruct {
    value: i32,
    another_value: i32
}

fn dereferencing_structs() {
    let my_struct = MyStruct {value: 1, another_value: 2};
    // this code will fail in here
    let value = *my_struct;

    assert_eq!(1, value);
}

In this case, it is not possible to know which value from the struct MyStruct Rust will attempt to dereference as MyStruct has the keys value and another_value.

To allow dereferencing on the struct MyStruct, implement the Deref trait.

use std::ops::Deref;

#[derive(Debug)]
struct MyStruct {
    value: i32,
    another_value: String
}

impl Deref for MyStruct {
    type Target = i32;

    fn deref(&self) -> &Self::Target {
        &self.value
    }
}

fn dereferencing_structs() {
    let my_struct = MyStruct {value: 1, another_value: String::from("a")};
    let test = *my_struct;

    assert_eq!(1, test);
}

When implementing Deref , it is required to define the type Target. This Target type should match the dereferenced value of any of the struct MyStruct keys (value, another_value). For this example, the type Target is based on value (i32).

Then, to finalize implementing the Deref trait, add the deref function. This function returns the key value of the struct MyStruct. Therefore, once you use the dereference operator on an instance of MyStruct, it will extract the data from the value key.

let my_struct = MyStruct {value: 1, another_value: String::from("a")};
let test = *my_struct;

assert_eq!(1, test);

Derefencing raw pointers

The types *const T and *mut T are considered raw pointers. According to the rust documentation, dereferencing a raw pointer is an unsafe operation. Hence, if you attempt to dereference a raw pointer, such as,

fn dereferencing_raw_pointers() {
    let test = "Hello!";
    let raw = &test as *const &str;
    
    // This will fail with the error error[E0614]: type `i32` cannot be dereferenced
    let another_test =  *raw ;
}

Rust will throw the following error at compile time:

error[E0614]: type i32 cannot be dereferenced

However, Rust allows to dereference raw pointers as long as you use the unsafe keyword.

fn dereferencing_raw_pointers() {
    let test = "Hello!";
    let raw = &test as *const &str;
    let another_test = unsafe { *raw };

    assert_eq!("Hello!", another_test);
}

Conclusion

All in all, this article explain the meaning of the asterisk (*) symbol in Rust, which is the dereference unary operator. The dereference operator is in charge of extracting the value off of a pointer or reference. A pointer is a memory location assigned to a variable in Rust. It is possible to use the dereference operator in all the different pointer types:

  • Shared References (&)
  • Mutable References (&mut)
  • Raw pointers (*const and *mut)
  • Smart Pointers

For those Rust types that you cannot use the dereference operator, implement the Deref and/or DerefMut traits.

If you want to checkout all the examples used in this article, feel free to find them in my repository https://github.com/arealesramirez/rust-deference-operator-examples.

Do you understand what the asterisks symbol means now?

Share your thoughts, comments, suggestions in the Twitter of Become A Better Programmer or to my personal Twitter account.