Variables
Variables are fundamental building blocks in Move programs that allow you to store and manipulate data. Understanding how to create and manage variables is essential for writing effective Move code.
Variable Declaration
Basic Variable Declaration
Variables in Move are declared using the let
keyword, followed by the variable name and an optional type annotation.
module my_module::variables {
public fun basic_variables() {
// Variable with type inference
let x = 42;
// Variable with explicit type annotation
let y: u64 = 100;
// Variable with explicit type and suffix
let z = 255u8;
// Boolean variable
let is_active = true;
// String variable
let message = string::utf8(b"Hello, Move!");
// Address variable
let account_addr = @0x1;
}
}
Variable Declaration with Different Types
public fun variable_types() {
// Integer types
let small_number: u8 = 255;
let medium_number: u16 = 65535;
let large_number: u64 = 18446744073709551615;
let very_large_number: u128 = 340282366920938463463374607431768211455u128;
// Boolean type
let flag: bool = true;
// Address type
let addr: address = @0xABCDEF;
// Vector type
let numbers: vector<u64> = vector[1, 2, 3, 4, 5];
// String type
let text: String = string::utf8(b"Move programming");
// Tuple type
let pair: (u64, String) = (42, string::utf8(b"answer"));
}
Variable Assignment and Mutability
Immutable Variables (Default)
By default, variables in Move are immutable, meaning they cannot be changed after declaration.
public fun immutable_variables() {
let x = 42;
// x = 50; // This would cause a compilation error
let message = string::utf8(b"Hello");
// message = string::utf8(b"World"); // This would cause a compilation error
}
Mutable Variables
To create a mutable variable, use the mut
keyword.
public fun mutable_variables() {
// Mutable variable declaration
let mut x = 42;
x = 50; // Now this is allowed
let mut counter = 0u64;
counter = counter + 1;
counter = counter + 1;
// Mutable vector
let mut numbers = vector[1, 2, 3];
vector::push_back(&mut numbers, 4);
vector::push_back(&mut numbers, 5);
// Mutable string
let mut message = string::utf8(b"Hello");
message = message + string::utf8(b" World");
}
Variable Reassignment
public fun variable_reassignment() {
let mut value = 10;
// Reassign with different value
value = 20;
// Reassign with calculation
value = value * 2;
// Reassign with function result
value = add(value, 5);
// Reassign with conditional
if (value > 50) {
value = 100;
} else {
value = 0;
};
}
fun add(a: u64, b: u64): u64 {
a + b
}
Variable Scoping
Local Variables
Variables declared within a function are local to that function and are automatically dropped when the function ends.
public fun local_variables() {
let x = 10; // Local variable
{
let y = 20; // Local variable in inner scope
let z = x + y; // Can access outer scope variable
// z is dropped at end of inner scope
};
// y is not accessible here
// z is not accessible here
// x is still accessible
let result = x * 2;
}
Variable Shadowing
Move allows variable shadowing, where a new variable with the same name can be declared in an inner scope.
public fun variable_shadowing() {
let x = 10;
{
let x = 20; // Shadows the outer x
// x is 20 in this scope
};
// x is 10 again in outer scope
}
Variable Lifetime
public fun variable_lifetime() {
let mut counter = 0u64;
while (counter < 5) {
let temp = counter * 2; // temp is created in each iteration
counter = counter + 1;
// temp is dropped at end of each iteration
};
// counter is still available here
let final_value = counter;
}
Variable Initialization
Immediate Initialization
Variables must be initialized when declared.
public fun immediate_initialization() {
// All variables must be initialized
let x = 42;
let y: u64 = 100;
let flag = true;
let message = string::utf8(b"Hello");
}
Conditional Initialization
Variables can be initialized conditionally.
public fun conditional_initialization() {
let condition = true;
let value = if (condition) {
100
} else {
200
};
// value is now either 100 or 200
}
Initialization with Function Calls
public fun initialization_with_functions() {
let result = calculate_value(10);
let message = create_message(string::utf8(b"Hello"));
let numbers = create_vector(5);
}
fun calculate_value(input: u64): u64 {
input * 2
}
fun create_message(prefix: String): String {
prefix + string::utf8(b" World")
}
fun create_vector(size: u64): vector<u64> {
let mut vec = vector::empty<u64>();
let i = 0;
while (i < size) {
vector::push_back(&mut vec, i);
i = i + 1;
};
vec
}
Variable Patterns
Destructuring Assignment
Move supports destructuring assignment for tuples and structs.
public fun destructuring() {
// Tuple destructuring
let (x, y) = (10, 20);
// Nested tuple destructuring
let ((a, b), c) = ((1, 2), 3);
// Struct destructuring (if you have a struct)
// let Point { x, y } = Point { x: 10, y: 20 };
}
Multiple Variable Declaration
public fun multiple_variables() {
// Declare multiple variables
let x = 1;
let y = 2;
let z = 3;
// Or use tuple destructuring
let (a, b, c) = (1, 2, 3);
// With different types
let (number, text, flag) = (42, string::utf8(b"Hello"), true);
}
Variable Best Practices
Choose Descriptive Names
public fun good_naming() {
// Good: Descriptive names
let user_age = 25;
let account_balance = 1000;
let is_user_active = true;
let user_name = string::utf8(b"Alice");
// Bad: Unclear names
let x = 25;
let y = 1000;
let flag = true;
let s = string::utf8(b"Alice");
}
Use Appropriate Types
public fun appropriate_types() {
// Use u8 for small numbers
let age: u8 = 25;
// Use u64 for most calculations
let balance: u64 = 1000000;
// Use u128 for large financial values
let total_supply: u128 = 1000000000000000000u128;
// Use bool for flags
let is_enabled: bool = true;
// Use address for account identifiers
let user_address: address = @0x1;
}
Minimize Mutable Variables
public fun minimize_mutability() {
// Good: Use immutable variables when possible
let max_attempts = 3;
let timeout_duration = 5000;
// Only use mut when you need to change the value
let mut current_attempt = 0;
let mut elapsed_time = 0;
while (current_attempt < max_attempts) {
current_attempt = current_attempt + 1;
elapsed_time = elapsed_time + 1000;
};
}
Initialize Variables Close to Use
public fun initialize_close_to_use() {
// Good: Initialize when needed
let result = perform_calculation(10);
if (result > 50) {
let message = string::utf8(b"High result");
// Use message here
} else {
let message = string::utf8(b"Low result");
// Use message here
};
// Bad: Initialize too early
// let message = string::utf8(b""); // Unused variable
// let result = perform_calculation(10);
// if (result > 50) {
// message = string::utf8(b"High result");
// } else {
// message = string::utf8(b"Low result");
// };
}
Common Variable Patterns
Counter Pattern
public fun counter_pattern() {
let mut counter = 0u64;
let max_count = 10;
while (counter < max_count) {
// Process something
counter = counter + 1;
};
}
Accumulator Pattern
public fun accumulator_pattern() {
let numbers = vector[1, 2, 3, 4, 5];
let mut sum = 0u64;
let i = 0;
let len = vector::length(&numbers);
while (i < len) {
let current = *vector::borrow(&numbers, i);
sum = sum + current;
i = i + 1;
};
// sum now contains the total
}
Flag Pattern
public fun flag_pattern() {
let numbers = vector[1, 2, 3, 4, 5];
let mut found = false;
let mut found_value = 0u64;
let target = 3;
let i = 0;
let len = vector::length(&numbers);
while (i < len && !found) {
let current = *vector::borrow(&numbers, i);
if (current == target) {
found = true;
found_value = current;
};
i = i + 1;
};
}
Temporary Variable Pattern
public fun temporary_variable_pattern() {
let x = 10;
let y = 20;
// Use temporary variable for complex calculation
let temp_sum = x + y;
let result = temp_sum * 2;
// Or for readability
let base_value = calculate_base(x, y);
let adjusted_value = apply_adjustment(base_value);
let final_result = apply_discount(adjusted_value);
}
fun calculate_base(a: u64, b: u64): u64 {
a + b
}
fun apply_adjustment(value: u64): u64 {
value * 2
}
fun apply_discount(value: u64): u64 {
value - (value / 10)
}
Variable Safety
Type Safety
Move's type system prevents many common programming errors.
public fun type_safety() {
let x: u64 = 42;
let y: u8 = 255;
// Type conversion is explicit
let z: u64 = x + (y as u64);
// This would cause a compilation error:
// let invalid = x + y; // Cannot add u64 and u8 directly
}
Bounds Checking
public fun bounds_checking() {
let mut index = 0u64;
let max_index = 10;
// Safe bounds checking
if (index < max_index) {
index = index + 1;
};
// Safe array access (if you had an array)
// if (index < vector::length(&array)) {
// let element = *vector::borrow(&array, index);
// };
}
Null Safety
Move doesn't have null values, which prevents null pointer errors.
public fun null_safety() {
// Move doesn't have null, so no null pointer errors
let value: u64 = 42; // Always has a value
// Use Option<T> for optional values
// let optional_value: Option<u64> = option::some(42);
}
Performance Considerations
Variable Reuse
public fun variable_reuse() {
let mut temp = 0u64;
// Reuse variable for different purposes
temp = calculate_value(10);
// Use temp...
temp = calculate_value(20);
// Use temp again...
temp = calculate_value(30);
// Use temp one more time...
}
fun calculate_value(input: u64): u64 {
input * 2
}
Avoid Unnecessary Variables
public fun avoid_unnecessary_variables() {
// Good: Direct return
let result = add(10, 20);
// Bad: Unnecessary intermediate variable
// let temp = add(10, 20);
// let result = temp;
}
fun add(a: u64, b: u64): u64 {
a + b
}
By understanding and following these variable management practices, you can write more readable, maintainable, and efficient Move code. Variables are the foundation of data manipulation in Move, and proper variable management is essential for building robust smart contracts.