DEV Community

Cover image for 🦀 Rust Master Class - Chapter 18: Advanced References
Oludayo Adeoye
Oludayo Adeoye

Posted on

🦀 Rust Master Class - Chapter 18: Advanced References

🦀 Rust Master Class - Chapter 18: Advanced References


Family reunion. Uncle arguing, cousin crying, grandmother mediating. Everyone connected, everyone borrowing. That's what advanced references feel like — until you understand the rules.


Advanced references in Rust involve complex binding patterns, specialized borrowing rules for compound types, and the subtyping relationships defined by variance. , ,

1. Binding and Reference Varieties

Rust distinguishes between the mutability of the binding (the variable holding the reference) and the mutability of the reference itself. Common types include:

  • ref: &T: An immutable binding of an immutable reference.
  • mut ref: &T: A mutable binding of an immutable reference (the reference can point to a different value, but the value cannot be changed through it). ,
  • ref: &mut T: An immutable binding of a mutable reference.
  • mut ref: &mut T: A mutable binding of a mutable reference.
  • ref: &mut &T: An immutable binding of a mutable reference to an immutable reference.

2. Destructuring References

You can use patterns to destructure references directly in function parameters or let statements. , This allows you to bind the underlying value rather than the reference.

Code Example:

fn some_fn_1(&value: &i100) {
    // value is an i100, not &i100
} 

fn main() {
    // Create a mutable variable
    let mut value = 42;
    let borrowed = &mut value;
    let &mut z = borrowed; // z now holds the value 42
}
Enter fullscreen mode Exit fullscreen mode

[Source: 93]

3. Borrowing in Compound Data Types

Borrowing rules apply differently depending on the collection type:

  • Tuples: Borrowing rules are applied on a per-element basis, allowing you to borrow one element as mutable and another as immutable simultaneously. ,
  • Vectors: Borrowing is applied on a per-vector basis; you cannot borrow different elements of the same vector with conflicting mutability.

Code Example:

    // Create a mutable variable
let mut tuple = (String::from("Rust"), String::from("Lang"));
    // Create a mutable variable
let mut_ref1 = &mut tuple.0; // Mutable borrow of first element
let immut_ref1 = &tuple.1;   // Immutable borrow of second element is OK
Enter fullscreen mode Exit fullscreen mode

[Source: 93]

4. Refutable and Irrefutable Patterns

Rust categorizes patterns based on whether they can fail to match. ,

  • Irrefutable Patterns: Patterns that always match any value of the expected type, such as variable bindings (x), wildcards (_), or full struct patterns. , These are required in let bindings and function parameters. ,
  • Refutable Patterns: Patterns that can fail to match, such as Some(x) or Err(e). These are only permitted in contexts like match or if let. ,

5. Reference and Deref Coercion

Coercion allows Rust to implicitly convert one reference type into another to simplify APIs. ,

  • Deref Coercion: Automatically converts a reference to a type that implements Deref into a reference of its target type (e.g., &String to &str or &Box<T> to &T). ,
  • Reference Coercion: Implicitly converts a mutable reference (&mut T) into an immutable reference (&T) when required by a function signature.
  • Transitivity: Coercion can chain; if A can coerce to B, and B can coerce to C, then A can coerce directly to C.

6. Subtyping and Variance

Variance defines how the subtyping of lifetimes affects the subtyping of types containing those lifetimes. ,

  • Covariance: If 'static outlives 'a, then &'static str is a subtype of &'a str. This means a function expecting a shorter lifetime can accept a reference with a longer lifetime. ,
  • Contravariance: Primarily applies to function arguments. A function type fn(&'a str) is a subtype of fn(&'static str) because a function that can handle any lifetime can certainly handle the 'static lifetime. ,
  • Invariance: Some types, like &mut T, have no subtyping relationship between lifetimes to ensure that a reference with a shorter lifetime isn't accidentally stored where a longer one is expected, which would cause a dangling pointer. ,

📖 Download the full PDF: https://drive.google.com/file/d/1ZD8RctlEjdzKwGqZPGtNTPJDoYmdly1S/view?usp=sharing

Part 18 of the Rust Master Class series — STEM EdTech | Automation Consulting | Rust Tutoring

RustLang #Programming #LearnToCode #STEM #EdTech


📚 Practice Resources

GitHub Repository: https://github.com/PacktPublishing/Rust-Programming-Master-Class-from-Beginner-to-Expert

Try it yourself: https://play.rust-lang.org/

Run the code from this chapter in the Rust playground, then clone the repo to continue your Rust journey!


Part 18 of the Rust Master Class series — STEM EdTech | Automation Consulting | Rust Tutoring

RustLang #Programming #LearnToCode #STEM #EdTech

Top comments (0)