Rust syntax
≈ 7 min read
Names in other languages
No names in other languages are held for this entry yet.
Article bodies are English and are not machine-translated. A name in another language is not a translation of the article.
The syntax of Rust is the set of rules defining how a Rust program is written and compiled. Rust's syntax is similar to that of C and C++, although many of its features were influenced by functional programming languages such as OCaml.
The syntax of Rust is the set of rules defining how a Rust program is written and compiled. Rust's syntax is similar to that of C and C++, although many of its features were influenced by functional programming languages such as OCaml. fn main() { println!("Hello, World!"); } fn main() { let foo: i32 = 10; println!("The value of foo is {foo}"); } Variables are immutable by default, but adding the keyword allows the variable to be mutated. The following example uses , which denotes the start of a comment. fn main() { // This code would not compile without adding "mut". let mut foo: i32 = 10; println!("The value of foo is {foo}"); foo = 20; println!("The value of foo is {foo}"); } Multiple expressions can define multiple variables with the same name, known as variable shadowing. Variable shadowing allows transforming variables without having to name the variables differently. The example below declares a new variable with the same name that is double the original value: fn main() { let foo: i32 = 10; // This will output "The value of foo is 10" println!("The value of foo is {foo}"); let foo: i32 = foo * 2; // This will output "The value of foo is 20" println!("The value of foo is {foo}"); } Variable shadowing is also possible for values of different types. For example, going from a string to its length in bytes: fn main() { let letters: str = "abc"; let letters: usize = letters.len(); } fn main() { let x: i32 = { println!("this is inside the block"); 1 + 2 }; println!("1 + 2 = {x}"); } Trailing expressions of function bodies are used as the return value: fn add_two(x: i32) -> i32 { x + 2 } fn main() { let x: i32 = 10; if x > 5 { println!("value is greater than five"); } if x % 7 == 0 { println!("value is divisible by 7"); } else if x % 5 == 0 { println!("value is divisible by 5"); } else { println!("value is not divisible by 7 or 5"); } } and blocks can evaluate to a value, which can then be assigned to a variable: fn main() { let x: i32 = 10; let new_x: i32 = if x % 2 == 0 { x / 2 } else { 3 * x + 1 }; println!("{new_x}"); } fn main() { // Iterate over all integers from 4 to 10 let mut value: i32 = 4; while value fn main() { // Using `for` with range syntax for the same functionality as above // The syntax 4..=10 means the range from 4 to 10, up to and including 10. for value in 4..=10 { println!("value = {value}"); } } In the above code, is a value of type which implements the trait. The code within the curly braces is applied to each element returned by the iterator. Iterators can be combined with functions over iterators like , , and . For example, the following adds up all numbers between 1 and 100 that are multiples of 3: (1..=100).filter(|&x: i8| -> bool x % 3 == 0).sum() fn main() { let value: i32 = 456; let mut x: i32 = 1; let y = loop { x *= 10; if x > value { break x / 10; } }; println!("largest power of ten that is smaller than or equal to value: {y}"); let mut up: i32 = 1; 'outer: loop { let mut down: i32 = 120; loop { if up > 100 { break 'outer; } if down fn double(x: Option ) -> u64 { match x { Some(y) => y * 2, None => 0, } } Equivalently, this can be written with and : fn double(x: Option ) -> u64 { if let Some(y) = x { y * 2 } else { 0 } } let tuple: (u32, i64) = (3, -3); let array: [i8; 5] = [1, 2, 3, 4, 5]; let value: i64 = tuple.1; // -3 let value: i8 = array[2]; // 3 Arrays can also be constructed through copying a single value a number of times: let array2: [char; 10] = [' '; 10]; fn print_string(s: String) { println!("{}", s); } fn main() { let s: String = String::from("Hello, World"); print_string(s); // s consumed by print_string // s has been moved, so cannot be used any more // another print_string(s); would result in a compile error } The function takes ownership over the value passed in; Alternatively, can be used to indicate a reference type (in ) and to create a reference (in ): fn print_string(s: &String) { println!("{}", s); } fn main() { let s: String = String::from("Hello, World"); print_string(&s); // s borrowed by print_string print_string(&s); // s has not been consumed; we can call the function many times } Because of these ownership rules, Rust types are known as linear or affine types, meaning each value can be used exactly once. This enforces a form of software fault isolation as the owner of a value is solely responsible for its correctness and deallocation. When a value goes out of scope, it is dropped by running its destructor. The destructor may be programmatically defined through implementing the trait. This helps manage resources such as file handles, network sockets, and locks, since when objects are dropped, the resources associated with them are closed or released automatically. fn main() { let x: i32 = 5; // ------------------+- Lifetime 'a // | let r: &i32 = &x; // -+-- Lifetime 'b | // | | println!("r: {}", r); // | | // | | // -+ | } // ------------------+ The borrow checker in the Rust compiler then enforces that references are only used in the locations of the source code where the associated lifetime is valid. In the example above, storing a reference to variable in is valid, as variable has a longer lifetime ( ) than variable ( ). However, when has a shorter lifetime, the borrow checker would reject the program: fn main() { let r: &i32; // ------------------+- Lifetime 'a // | { // | let x: i32 = 5; // -+-- Lifetime 'b | r = &x; // ERROR: x does | | } // not live long -| | // enough | println!("r: {}", r); // | } // ------------------+ Since the lifetime of the referenced variable ( ) is shorter than the lifetime of the variable holding the reference ( ), the borrow checker errors, preventing from being used from outside its scope. Lifetimes can be indicated using explicit lifetime parameters on function arguments. For example, the following code specifies that the reference returned by the function has the same lifetime as (and not necessarily the same lifetime as ): fn remove_prefix (mut original: &'a str, prefix: &str) -> &'a str { if original.starts_with(prefix) { original = original[prefix.len()..]; } original } In the compiler, ownership and lifetimes work together to prevent memory safety issues such as dangling pointers. fn main() { let name1: Option = None; // In this case, nothing will be printed out if let Some(name) = name1 { println!("{name}"); } let name2: Option = Some("Matthew"); // In this case, the word "Matthew" will be printed out if let Some(name) = name2 { println!("{name}"); } } use std::ops::Add; // sum is a generic function with one type parameter, T fn sum (num1: T, num2: T) -> T where T: Add , // T must implement the Add trait where addition returns another T { num1 + num2 // num1 + num2 is syntactic sugar for num1.add(num2) provided by the Add trait } fn main() { let result1: i32 = sum(10, 20); println!("Sum is: {}", result1); // Sum is: 30 let result2: f32 = sum(10.23, 20.45); println!("Sum is: {}", result2); // Sum is: 30.68 } At compile time, polymorphic functions like sum are instantiated with the specific types the code requires; in this case, sum of integers and sum of floats. Generics can be used in functions to allow implementing a behavior for different types without repeating the same code. Generic functions can be written in relation to other generics, without knowing the actual type. use std::fmt::Display; let v: Vec > = vec![ Box::new(3), Box::new(5.0), Box::new("hi"), ]; for x in v { println!("{x}"); } If an element in the list does not implement the Display trait, it will cause a compile-time error. macro_rules! sum { ( $initial:expr $(, $expr:expr )* $(,)? ) => { $initial $(+ $expr)* } } fn main() { let x: i32 = sum!(1, 2, 3); println!("{x}"); // prints 6 }
Text derived from https://en.wikipedia.org/w/index.php?oldid=1376005943 under CC BY-SA 4.0; modifications have been made.