🌱
Dev Compendium
  • Ethereum
    • Solidity
      • EVM
      • Architecture
      • Execution Context
      • Transactions
      • Gas
      • Calldata, Memory & Storage
      • Gas Optimisation
      • Function Declarations
      • receive() & fallback()
      • CALL vs. DELEGATE CALL
    • Yul
      • Introduction
      • Types
      • Basic Operations
      • Storage
      • Memory
        • Arrays
        • Structs
        • Tuples, revert, keccak256
        • Logs and Events
        • Gotchas
        • abi.encode
      • Calldata
        • External Calls
        • Dynamic Length Inputs
        • Transferring Value
        • Receiving Contract Calls
      • Contracts in Yul
      • Other Yul Functions
    • Foundry
    • Security
      • Common Vulnerabilities
      • Best Practices
      • Development Workflow
      • Contract Migration
    • Auditing Tools
      • Slither
      • Mythril
      • Fuzzing
    • Upgradable Contracts
      • Upgrade Patterns
      • ERC-1967 Implementation
      • Deployment
    • MEV
    • Tooling
      • Chainlink
      • IPFS
      • Radicle
    • Frontend
      • Contract Hooks
      • Wallet Connection
        • wagmi.sh
        • Rainbow Kit
      • thirdweb
    • Protocol Research
      • Uniswap v2
      • Uniswap v3
      • Curve
      • GMX
  • Starkware
    • Fundamentals
    • Account Abstraction
    • Universal Deployer
    • Cairo 1.0
    • starknet.js
    • Security Model
  • Zero Knowledge
    • Group Theory
    • ECDSA
  • Rust
    • Basic Operations
    • Set up
    • Primitives
    • Control Flow
    • Mutability & Shadowing
    • Adding Behavior
    • Lifetimes
    • Std Library
  • SUI
    • Architecture
    • Consensus Mechanism
    • Local Node Setup
    • Sui Client CLI
    • Move Contracts
      • Move
      • Move.toml
      • Move.lock
      • Accessing Time in Sui Move
      • Set up Development Framework
      • Debug & Publish
      • Package Upgrades
      • Sui Move Library
      • Difference from Core Move
    • Object Programming
      • Object Basics
      • Using Objects
      • Immutable Objects
      • Object Wrapping
      • Dynamic Fields
      • Collections
      • Unit Testing
      • Deployment with CLI
  • NEAR
    • Architecture
    • Contract Standards
      • Fungible Token (NEP-141)
      • Non-Fungible Token (NEP-171)
      • Storage Management (NEP-145)
      • Events (NEP-297)
      • Meta-Transactions
    • Rust Contracts
      • Development Workflow
      • Smart Contract Layout
      • Storage Management
      • Events & Meta-transactions
      • Method Types
      • Upgrading Contracts
      • Unit Testing
    • NEAR Libraries
    • Environment Variables
    • Serialisation
    • Security Concepts
    • Collections
    • JS SDK
Powered by GitBook
On this page
  1. Rust

Mutability & Shadowing

PreviousControl FlowNextAdding Behavior

Last updated 1 year ago

Mutability

This code will not compile:

let x = 0; x = 1;

The let keyword in Rust works a bit like how the const keyword works in JavaScript, in that all identifiers it declares are immutable. In JavaScript, const identifiers simply cannot be reassigned, but its properties can still be changed. In Rust, immutability is total: no reassignments, and no mutation of contents either. You could say it’s like const + [Object.freeze](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze>) in JavaScript.

This seems like a pretty stringent limitation at first. However, there’s an escape hatch: the mut keyword. Actually, if you’ve been following along and trying out the code examples, you’ll already know about it—here’s the error message you’d get if you tried to compile the above example:

error[E0384]: cannot assign twice to immutable variable x --> src\\main.rs:4:5 | 3 | let x = 0; | - | | | first assignment tox | help: consider making this binding mutable:mut x` 4 | x = 1; | ^^^^^ cannot assign twice to immutable variable

For more information about this error, try rustc --explain E0384.`

Do you see it? The Rust compiler itself suggests a way to fix our code by using the mut keyword!

Having to use a whole additional keyword just to make a variable mutable may seem kind of inconvenient at first. However, immutable-by-default variable declarations make it easier for you to let the compiler make certain guarantees about your code, which can help detecting bugs early, for instance.

Shadowing

You may have noticed that re-bound the my_tuple identifier a second time using the let keyword. If we were to do something like this in a language like JavaScript, we’d get an error:

Uncaught SyntaxError: redeclaration of let my_tuple

In Rust, however, this is allowed. It simply defines another (new, discrete) identifier that just happens to shadow (override the name of) another identifier.

This can be useful when you wish to change the type or mutability of an identifier.

Scoping

Identifiers in Rust are block-scoped, meaning that an identifier declared within a set of curly braces {} is inaccessible outside of the braces.

fn main() { let x = 0; { // yonly exists in this scope let y = 1; println!("{} {}", x, y); } // scope ends println!("{} {}", x, y); //y is out of scope }

Output:

error[E0425]: cannot find value yin this scope --> src/main.rs:7:26 | 7 | println!("{} {}", x, y); //yis out of scope | ^ help: a local variable with a similar name exists:x``

a previous example