Rust syntax

{{Short description|Set of rules defining correctly structured programs for the Rust programming language}}

File:Screen1 2.png

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++,{{Cite news |last=Proven |first=Liam |date=2019-11-27 |title=Rebecca Rumbul named new CEO of The Rust Foundation |url=https://www.theregister.com/2021/11/19/rust_foundation_ceo/ |access-date=2022-07-14 |website=The Register |language=en |quote="Both are curly bracket languages, with C-like syntax that makes them unintimidating for C programmers." |archive-date=2022-07-14 |archive-url=https://web.archive.org/web/20220714110957/https://www.theregister.com/2021/11/19/rust_foundation_ceo/ |url-status=live }}{{Cite web |last=Vigliarolo |first=Brandon |date=2021-02-10 |title=The Rust programming language now has its own independent foundation |url=https://www.techrepublic.com/article/the-rust-programming-language-now-has-its-own-independent-foundation/ |archive-url=https://web.archive.org/web/20230320172900/https://www.techrepublic.com/article/the-rust-programming-language-now-has-its-own-independent-foundation/ |archive-date=2023-03-20 |access-date=2022-07-14 |website=TechRepublic |language=en-US}} although many of its features were influenced by functional programming languages such as OCaml.{{sfn|Klabnik|Nichols|2019|p=263}}

Basics

Although Rust syntax is heavily influenced by the syntaxes of C and C++, the syntax of Rust is far more distinct from C++ syntax than Java or C#, as those languages have more C-style declarations, primitive names, and keywords.

Below is a "Hello, World!" program in Rust. The {{Rust|fn}} keyword denotes a function, and the {{Rust|println!}} macro (see {{Section link|2=Macros|nopage=y}}) prints the message to standard output.{{sfn|Klabnik|Nichols|2019|pp=5–6}} Statements in Rust are separated by semicolons.

fn main() {

println!("Hello, World!");

}

= Reserved words =

== Keywords ==

The following words are reserved, and may not be used as identifiers:

{{div col|colwidth=15em}}

  • as
  • async
  • await
  • break
  • const
  • continue
  • crate
  • dyn
  • else
  • enum
  • extern
  • false
  • fn
  • for
  • if
  • impl
  • in
  • let
  • loop
  • match
  • mod
  • move
  • mut
  • pub
  • ref
  • return
  • Self
  • self
  • static
  • struct
  • super
  • trait
  • true
  • type
  • union
  • unsafe
  • use
  • where
  • while

{{div col end}}

== Unused words ==

The following words are reserved as keywords, but currently have no use or purpose.

{{div col|colwidth=15em}}

  • abstract
  • become
  • box
  • do
  • final
  • gen
  • macro
  • override
  • priv
  • try
  • typeof
  • unsized
  • virtual
  • yield

{{div col end}}

= Variables =

Variables in Rust are defined through the {{rust|let}} keyword.{{sfn|Klabnik|Nichols|2023|p=32}} The example below assigns a value to the variable with name {{code|foo}} and outputs its value.

fn main() {

let foo = 10;

println!("The value of foo is {foo}");

}

Variables are immutable by default, but adding the {{rust|mut}} keyword allows the variable to be mutated.{{sfn|Klabnik|Nichols|2023|pp=32-33}} The following example uses {{code|//}}, which denotes the start of a comment.{{sfn|Klabnik|Nichols|2023|pp=49-50}}

fn main() {

// This code would not compile without adding "mut".

let mut foo = 10;

println!("The value of foo is {foo}");

foo = 20;

println!("The value of foo is {foo}");

}

Multiple {{code|let}} 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.{{sfn|Klabnik|Nichols|2023|pp=34-36}} The example below declares a new variable with the same name that is double the original value:

fn main() {

let foo = 10;

// This will output "The value of foo is 10"

println!("The value of foo is {foo}");

let foo = 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:

fn main() {

let letters = "abc";

let letters = letters.len();

}

= Block expressions and control flow =

A block expression is delimited by curly brackets. When the last expression inside a block does not end with a semicolon, the block evaluates to the value of that trailing expression:{{sfn|Klabnik|Nichols|2023|pp=6,47,53}}

fn main() {

let x = {

println!("this is inside the block");

1 + 2

};

println!("1 + 2 = {x}");

}

Trailing expressions of function bodies are used as the return value:{{sfn|Klabnik|Nichols|2023|pp=47-48}}

fn add_two(x: i32) -> i32 {

x + 2

}

== {{code|if}} expressions ==

An {{rust|if}} conditional expression executes code based on whether the given value is {{code|true}}. {{rust|else}} can be used for when the value evaluates to {{code|false}}, and {{rust|else if}} can be used for combining multiple expressions.{{sfn|Klabnik|Nichols|2023|pp=50-53}}

fn main() {

let x = 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");

}

}

{{rust|if}} and {{rust|else}} blocks can evaluate to a value, which can then be assigned to a variable:{{sfn|Klabnik|Nichols|2023|pp=50-53}}

fn main() {

let x = 10;

let new_x = if x % 2 == 0 { x / 2 } else { 3 * x + 1 };

println!("{new_x}");

}

== {{code|while}} loops ==

while can be used to repeat a block of code while a condition is met.{{sfn|Klabnik|Nichols|2023|p=56}}

fn main() {

// Iterate over all integers from 4 to 10

let mut value = 4;

while value <= 10 {

println!("value = {value}");

value += 1;

}

}

== {{code|for}} loops and iterators ==

For loops in Rust loop over elements of a collection.{{sfn|Klabnik|Nichols|2023|pp=57-58}}

{{rust|for}} expressions work over any iterator type.

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, {{rust|1=4..=10}} is a value of type {{rust|Range}} which implements the {{rust|Iterator}} 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 {{rust|map}}, {{rust|filter}}, and {{rust|sum}}. For example, the following adds up all numbers between 1 and 100 that are multiples of 3:

(1..=100).filter(|&x| x % 3 == 0).sum()

== {{code|loop}} and {{code|break}} statements ==

More generally, the {{rust|loop}} keyword allows repeating a portion of code until a {{rust|break}} occurs. {{rust|break}} may optionally exit the loop with a value. In the case of nested loops, labels denoted by {{rust|'label_name}} can be used to break an outer loop rather than the innermost loop.{{sfn|Klabnik|Nichols|2023|pp=54-56}}

fn main() {

let value = 456;

let mut x = 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 = 1;

'outer: loop {

let mut down = 120;

loop {

if up > 100 {

break 'outer;

}

if down < 4 {

break;

}

down /= 2;

up += 1;

println!("up: {up}, down: {down}");

}

up *= 2;

}

}

= Pattern matching =

The {{rust|match}} and {{rust|if let}} expressions can be used for pattern matching. For example, {{rust|match}} can be used to double an optional integer value if present, and return zero otherwise:{{sfn|Klabnik|Nichols|2019|pp=104–109}}

fn double(x: Option) -> u64 {

match x {

Some(y) => y * 2,

None => 0,

}

}

Equivalently, this can be written with {{rust|if let}} and {{rust|else}}:

fn double(x: Option) -> u64 {

if let Some(y) = x {

y * 2

} else {

0

}

}

Types

Rust is strongly typed and statically typed, meaning that the types of all variables must be known at compilation time. Assigning a value of a particular type to a differently typed variable causes a compilation error. Type inference is used to determine the type of variables if unspecified.{{sfn|Klabnik|Nichols|2019|pp=24}}

The default integer type is {{rust|i32}}, and the default floating point type is {{rust|f64}}. If the type of a literal number is not explicitly provided, it is either inferred from the context or the default type is used.{{sfn|Klabnik|Nichols|2019|pp=36–38}}

= Primitive types =

Integer types in Rust are named based on the signedness and the number of bits the type takes. For example, {{rust|i32}} is a signed integer that takes 32 bits of storage, whereas {{code|u8}} is unsigned and only takes 8 bits of storage. {{rust|isize}} and {{rust|usize}} take storage depending on the architecture of the computer that runs the code, for example, on computers with 32-bit architectures, both types will take up 32 bits of space.

By default, integer literals are in base-10, but different radices are supported with prefixes, for example, {{rust|0b11}} for binary numbers, {{rust|0o567}} for octals, and {{rust|0xDB}} for hexadecimals. By default, integer literals default to {{rust|i32}} as its type. Suffixes such as {{rust|4u32}} can be used to explicitly set the type of a literal.{{sfn|Klabnik|Nichols|2023|pp=36-38}} Byte literals such as {{rust|b'X'}} are available to represent the ASCII value (as a {{rust|u8}}) of a specific character.{{sfn|Klabnik|Nichols|2023|p=502}}

The Boolean type is referred to as {{rust|bool}} which can take a value of either {{rust|true}} or {{rust|false}}. A {{rust|char}} takes up 32 bits of space and represents a Unicode scalar value: a Unicode codepoint that is not a surrogate.{{Cite web |title=Glossary of Unicode Terms |url=https://www.unicode.org/glossary/ |access-date=2024-07-30 |website=Unicode Consortium |archive-date=2018-09-24 |archive-url=https://web.archive.org/web/20180924092749/http://www.unicode.org/glossary/ |url-status=live }} IEEE 754 floating point numbers are supported with {{rust|f32}} for single precision floats and {{rust|f64}} for double precision floats.{{sfn|Klabnik|Nichols|2019|pp=38–40}}

= Compound types =

Compound types can contain multiple values. Tuples are fixed-size lists that can contain values whose types can be different. Arrays are fixed-size lists whose values are of the same type. Expressions of the tuple and array types can be written through listing the values, and can be accessed with {{rust|.index}} or {{rust|[index]}}:{{sfn|Klabnik|Nichols|2023|pp=40-42}}

let tuple: (u32, i64) = (3, -3);

let array: [i8; 5] = [1, 2, 3, 4, 5];

let tuple: (bool, bool) = (true, true);

let value = tuple.1; // -3

let value = array[2]; // 3

Arrays can also be constructed through copying a single value a number of times:{{sfn|Klabnik|Nichols|2023|p=42}}

let array2: [char; 10] = [' '; 10];

Ownership and references

Rust's ownership system consists of rules that ensure memory safety without using a garbage collector. At compile time, each value must be attached to a variable called the owner of that value, and every value must have exactly one owner.{{sfn|Klabnik|Nichols|2019|pp=59–61}} Values are moved between different owners through assignment or passing a value as a function parameter. Values can also be borrowed, meaning they are temporarily passed to a different function before being returned to the owner.{{sfn|Klabnik|Nichols|2019|pp=63–68}} With these rules, Rust can prevent the creation and use of dangling pointers:{{sfn|Klabnik|Nichols|2019|pp=63–68}}{{sfn|Klabnik|Nichols|2019|pp=74–75}}

fn print_string(s: String) {

println!("{}", s);

}

fn main() {

let s = 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 {{code|print_string}} takes ownership over the {{rust|String}} value passed in; Alternatively, {{code|&}} can be used to indicate a reference type (in {{code|&String}}) and to create a reference (in {{code|&s}}):{{sfn|Klabnik|Nichols|2023|pp=71–72}}

fn print_string(s: &String) {

println!("{}", s);

}

fn main() {

let s = 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.{{Cite book |last1=Balasubramanian |first1=Abhiram |last2=Baranowski |first2=Marek S. |last3=Burtsev |first3=Anton |last4=Panda |first4=Aurojit |last5=Rakamarić |first5=Zvonimir |last6=Ryzhyk |first6=Leonid |title=Proceedings of the 16th Workshop on Hot Topics in Operating Systems |chapter=System Programming in Rust |date=2017-05-07 |chapter-url=https://doi.org/10.1145/3102980.3103006 |series=HotOS '17 |location=New York, NY, US |publisher=Association for Computing Machinery |pages=156–161 |doi=10.1145/3102980.3103006 |isbn=978-1-4503-5068-6 |s2cid=24100599 |access-date=June 1, 2022 |archive-date=June 11, 2022 |archive-url=https://web.archive.org/web/20220611034046/https://dl.acm.org/doi/10.1145/3102980.3103006 |url-status=live}}

When a value goes out of scope, it is dropped by running its destructor. The destructor may be programmatically defined through implementing the {{code|Drop}} 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.{{sfn|Klabnik|Nichols|2023|pp=327-30}}

= Lifetimes =

Object lifetime refers to the period of time during which a reference is valid; that is, the time between the object creation and destruction.{{Cite web |title=Lifetimes |url=https://doc.rust-lang.org/rust-by-example/scope/lifetime.html |access-date=2024-10-29 |website=Rust by Example |archive-date=2024-11-16 |archive-url=https://web.archive.org/web/20241116192422/https://doc.rust-lang.org/rust-by-example/scope/lifetime.html |url-status=live }} These lifetimes are implicitly associated with all Rust reference types. While often inferred, they can also be indicated explicitly with named lifetime parameters (often denoted {{code|'a}}, {{code|'b}}, and so on).{{Cite web |title=Explicit annotation |url=https://doc.rust-lang.org/rust-by-example/scope/lifetime/explicit.html |access-date=2024-10-29 |website=Rust by Example}}

Lifetimes in Rust can be thought of as lexically scoped, meaning that the duration of an object lifetime is inferred from the set of locations in the source code (i.e., function, line, and column numbers) for which a variable is valid.{{sfn|Klabnik|Nichols|2019|p=194}} For example, a reference to a local variable has a lifetime corresponding to the block it is defined in:{{sfn|Klabnik|Nichols|2019|p=194}}

fn main() {

let x = 5; // ------------------+- Lifetime 'a

// |

let r = &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.{{sfn|Klabnik|Nichols|2019|pp=75,134}}{{Cite web |last=Shamrell-Harrington |first=Nell |date=2022-04-15 |title=The Rust Borrow Checker – a Deep Dive |url=https://www.infoq.com/presentations/rust-borrow-checker/ |access-date=2022-06-25 |website=InfoQ |language=en |archive-date=2022-06-25 |archive-url=https://web.archive.org/web/20220625140128/https://www.infoq.com/presentations/rust-borrow-checker/ |url-status=live }} In the example above, storing a reference to variable {{code|x}} in {{code|r}} is valid, as variable {{code|x}} has a longer lifetime ({{code|'a}}) than variable {{code|r}} ({{code|'b}}). However, when {{code|x}} has a shorter lifetime, the borrow checker would reject the program:

fn main() {

let r; // ------------------+- Lifetime 'a

// |

{ // |

let x = 5; // -+-- Lifetime 'b |

r = &x; // ERROR: x does | |

} // not live long -| |

// enough |

println!("r: {}", r); // |

} // ------------------+

Since the lifetime of the referenced variable ({{code|'b}}) is shorter than the lifetime of the variable holding the reference ({{code|'a}}), the borrow checker errors, preventing {{code|x}} from being used from outside its scope.{{sfn|Klabnik|Nichols|2019|pp=194-195}}

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 {{code|original}} (and not necessarily the same lifetime as {{code|prefix}}):{{sfn|Klabnik|Nichols|2023|pp=208–12}}

fn remove_prefix<'a>(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.{{sfn|Klabnik|Nichols|2023|loc=[https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html 4.2. References and Borrowing]}}{{cite journal |last1=Pearce |first1=David |title=A Lightweight Formalism for Reference Lifetimes and Borrowing in Rust |url=https://dl.acm.org/doi/10.1145/3443420 |journal=ACM Transactions on Programming Languages and Systems |access-date=11 December 2024 |archive-url=https://web.archive.org/web/20240415053627/https://dl.acm.org/doi/10.1145/3443420 |archive-date=15 April 2024 |doi=10.1145/3443420 |date=17 April 2021 |volume=43 |pages=1–73 |url-status=live}}

User-defined types

User-defined types are created with the {{rust|struct}} or {{rust|enum}} keywords. The {{rust|struct}} keyword is used to denote a record type that groups multiple related values.{{sfn|Klabnik|Nichols|2019|p=83}} {{rust|enum}}s can take on different variants at runtime, with its capabilities similar to algebraic data types found in functional programming languages.{{sfn|Klabnik|Nichols|2019|p=97}} Both records and enum variants can contain fields with different types.{{sfn|Klabnik|Nichols|2019|pp=98–101}} Alternative names, or aliases, for the same type can be defined with the {{rust|type}} keyword.{{sfn|Klabnik|Nichols|2019|pp=438–440}}

The {{rust|impl}} keyword can define methods for a user-defined type. Data and functions are defined separately. Implementations fulfill a role similar to that of classes within other languages.{{sfn|Klabnik|Nichols|2019|pp=93}}

= Standard library =

class="wikitable"

|+Summary of Rust's types in the standard library

!Type

!Description

!Examples

{{Rust|String}}

|UTF-8-encoded strings (dynamic)

|{{plainlist|

  • {{Rust|String::new()}}
  • {{Rust|String::from("Hello")}}
  • {{Rust|"🦀🦀🦀".to_string()}}

}}

{{plainlist|

  • {{Rust|OsStr}}
  • {{Rust|OsString}}

}}

|Platform-native stringsOn Unix systems, this is often UTF-8 strings without an internal 0 byte. On Windows, this is UTF-16 strings without an internal 0 byte. Unlike these, {{Rust|str}} and {{Rust|String}} are always valid UTF-8 and can contain internal zeros. (borrowed{{Cite web |title=OsStr in std::ffi |url=https://doc.rust-lang.org/beta/std/ffi/struct.OsStr.html |access-date=2023-10-02 |website=The Rust Standard Library documentation |archive-date=2023-06-23 |archive-url=https://web.archive.org/web/20230623184907/https://doc.rust-lang.org/beta/std/ffi/struct.OsStr.html |url-status=live }} and dynamic{{Cite web |title=OsString in std::ffi |url=https://doc.rust-lang.org/beta/std/ffi/struct.OsString.html |access-date=2023-10-02 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624103428/https://doc.rust-lang.org/beta/std/ffi/struct.OsString.html |url-status=live }})

|{{plainlist|

  • {{Rust|OsStr::new("Hello")}}
  • {{Rust|OsString::from("world")}}

}}

{{plainlist|

  • {{Rust|Path}}
  • {{Rust|PathBuf}}

}}

|Paths (borrowed{{Cite web |title=Path in std::path |url=https://doc.rust-lang.org/beta/std/path/struct.Path.html |access-date=2023-10-02 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624103428/https://doc.rust-lang.org/beta/std/path/struct.Path.html |url-status=live }} and dynamic{{Cite web |title=PathBuf in std::path |url=https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html |access-date=2023-10-02 |website=The Rust Standard Library documentation |archive-date=2023-06-27 |archive-url=https://web.archive.org/web/20230627034115/https://doc.rust-lang.org/beta/std/path/struct.PathBuf.html |url-status=live }})

|{{plainlist|

  • {{Rust|Path::new("./path/to")}}
  • {{Rust|PathBuf::from(r"C:.\path\to")}}

}}

{{plainlist|

  • {{Rust|CStr}}
  • {{Rust|CString}}

}}

|C-compatible, null-terminated strings (borrowed{{Cite web |title=std::boxed |url=https://doc.rust-lang.org/std/boxed/index.html |access-date=2023-06-23 |website=The Rust Standard Library documentation |archive-date=2023-06-23 |archive-url=https://web.archive.org/web/20230623184904/https://doc.rust-lang.org/std/boxed/index.html |url-status=live }} and dynamic)

|{{plainlist|

  • {{Rust|c"Hello"}}
  • {{Rust|CStr::from_bytes_with_nul(b"Hello\0").unwrap()}}
  • {{Rust|CString::new("world").unwrap()}}

}}

{{Rust|Vec}}

|Dynamic arrays

|{{plainlist|

  • {{Rust|Vec::new()}}
  • {{Rust|vec![1, 2, 3, 4, 5]}}

}}

{{Rust|Option}}

|Option type

|{{plainlist|

  • {{Rust|None}}
  • {{Rust|Some(3)}}
  • {{Rust|Some("hello")}}

}}

{{Rust|Result}}

|Error handling using a result type

|{{plainlist|

  • {{Rust|Ok(3)}}
  • {{Rust|Err("something went wrong")}}

}}

{{Rust|Box}}

|A pointer to a heap-allocated value. Similar to C++'s [https://en.cppreference.com/w/cpp/memory/unique_ptr std::unique_ptr].

|

let boxed: Box = Box::new(5);

let val: u8 = *boxed;

{{Rust|Rc}}

|Reference counting pointer{{Cite web |title=Rc in std::rc |url=https://doc.rust-lang.org/beta/std/rc/struct.Rc.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080501/https://doc.rust-lang.org/beta/std/rc/struct.Rc.html |url-status=live }}

|

let five: Rc = Rc::new(5);

let also_five: Rc = five.clone();

{{Rust|Arc}}

|Atomic, thread-safe reference counting pointer{{Cite web |title=Arc in std::sync |url=https://doc.rust-lang.org/beta/std/sync/struct.Arc.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080503/https://doc.rust-lang.org/beta/std/sync/struct.Arc.html |url-status=live }}

|

let foo: Arc> = Arc::new(vec![1.0, 2.0]);

let a: Arc> = foo.clone(); // a can be sent to another thread

{{Rust|Cell}}

|A mutable memory location{{Cite web |title=Cell in std::cell |url=https://doc.rust-lang.org/beta/std/cell/struct.Cell.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080506/https://doc.rust-lang.org/beta/std/cell/struct.Cell.html |url-status=live }}

|

let c: Cell = Cell::new(5);

c.set(10);

Mutex

|A mutex lock for shared data contained within.{{Cite web |title=Mutex in std::sync |url=https://doc.rust-lang.org/beta/std/sync/struct.Mutex.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080502/https://doc.rust-lang.org/beta/std/sync/struct.Mutex.html |url-status=live }}

|

let mutex: Mutex = Mutex::new(0_u32);

let _guard: LockResult> = mutex.lock();

{{Rust|RwLock}}

|Readers–writer lock{{Cite web |title=RwLock in std::sync |url=https://doc.rust-lang.org/beta/std/sync/struct.RwLock.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080503/https://doc.rust-lang.org/beta/std/sync/struct.RwLock.html |url-status=live }}

|

let lock: RwLock = RwLock::new(5);

let r1: u8 = lock.read().unwrap();

{{Rust|Condvar}}

|A conditional monitor for shared data{{Cite web |title=Condvar in std::sync |url=https://doc.rust-lang.org/beta/std/sync/struct.Condvar.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080504/https://doc.rust-lang.org/beta/std/sync/struct.Condvar.html |url-status=live }}

|

let (lock, cvar): (Mutex, Condvar) = (Mutex::new(true), Condvar::new());

// As long as the value inside the `Mutex` is `true`, we wait.

let _guard: Mutex = cvar.wait_while(lock.lock().unwrap(), |pending| { *pending }).unwrap();

{{Rust|Duration}}

|Type that represents a span of time{{Cite web |title=Duration in std::time |url=https://doc.rust-lang.org/beta/std/time/struct.Duration.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080503/https://doc.rust-lang.org/beta/std/time/struct.Duration.html |url-status=live }}

|

Duration::from_millis(1) // 1ms

{{Rust|1=HashMap}}

|Hash table{{Cite web |title=HashMap in std::collections |url=https://doc.rust-lang.org/beta/std/collections/struct.HashMap.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080505/https://doc.rust-lang.org/beta/std/collections/struct.HashMap.html |url-status=live }}

|

let mut player_stats: HashMap = HashMap::new();

player_stats.insert("damage", 1);

player_stats.entry("health").or_insert(100);

{{Rust|1=BTreeMap}}

|B-tree{{Cite web |title=BTreeMap in std::collections |url=https://doc.rust-lang.org/beta/std/collections/struct.BTreeMap.html |access-date=2023-06-24 |website=The Rust Standard Library documentation |archive-date=2023-06-24 |archive-url=https://web.archive.org/web/20230624080503/https://doc.rust-lang.org/beta/std/collections/struct.BTreeMap.html |url-status=live }}

|

let mut solar_distance: BTreeMap = BTreeMap::from([

("Mercury", 0.4),

("Venus", 0.7),

]);

solar_distance.entry("Earth").or_insert(1.0);

Option values are handled using syntactic sugar, such as the if let construction, to access the inner value (in this case, a string):{{sfn|McNamara|2021}}

fn main() {

let name1: Option<&str> = None;

// In this case, nothing will be printed out

if let Some(name) = name1 {

println!("{name}");

}

let name2: Option<&str> = Some("Matthew");

// In this case, the word "Matthew" will be printed out

if let Some(name) = name2 {

println!("{name}");

}

}

Pointers

class="wikitable"

|+Summary of Rust's pointer and reference primitive types

!Type

!Description

!Examples

{{plainlist|

  • {{Rust|&T}}
  • {{Rust|&mut T}}

}}

|References (immutable and mutable)

|{{plainlist|

  • {{Rust|let x_ref: &T {{=}} {{not a typo|&x;}}}}
  • {{Rust|let x_ref: &mut T {{=}} &mut x;}}

}}

{{plainlist|

  • {{Rust|Option<&T>}}
  • {{Rust|Option<&mut T>}}

}}

|{{plainlist|

  • Option wrapped reference
  • Possibly null reference

}}

|{{plainlist|

  • {{Rust|None}}
  • {{Rust|let x_ref: Option<&T> {{=}} Some(&x);}}
  • {{Rust|let x_ref: Option<&mut T> {{=}} Some(&mut x);}}

}}

{{Plain list|* {{Rust|Box}}

  • {{Rust|Option>}}}}

|A pointer to heap-allocated value

(or possibly null pointer if wrapped in option)

|{{Plain list|* {{Rust|let boxed: Box {{=}} Box::new(0);}}

  • {{Rust|let boxed: Option> {{=}} Some(Box::new("Hello World"));}}}}
{{Plainlist|
  • {{Rust|*const T}}
  • {{Rust|*mut T}}
  • }}

    |{{Plainlist|

    • Raw pointers (immutable and mutable)
    • Possibly null; {{Rust|unsafe}} to dereference

    }}

    |{{Plainlist|

    • {{Rust|let x_ptr: *const T {{=}} &x as *const T;}}
    • {{Rust|let x_ptr: *mut T {{=}} &mut x as *mut T;}}

    }}

    To prevent the use of null pointers and their dereferencing, the basic & and &mut references are guaranteed to not be null. Rust instead uses Option for this purpose: Some(T) indicates that a value is present, and None is analogous to the null pointer.{{sfn|Klabnik|Nichols|2019|pp=101–104}} Option implements a "null pointer optimization", avoiding any spatial overhead for types that cannot have a null value (references or the NonZero types, for example).{{Cite web |title=std::option |url=https://doc.rust-lang.org/std/option/index.html#representation |access-date=2023-11-12 |website=The Rust Standard Library documentation}} Though null pointers are idiomatically avoided, the null pointer constant in Rust is represented by std::ptr::null().

    Rust also supports raw pointer types *const and *mut, which may be null; however, it is impossible to dereference them unless the code is explicitly declared unsafe through the use of an unsafe block. Unlike dereferencing, the creation of raw pointers is allowed inside of safe Rust code.{{sfn|Klabnik|Nichols|2019|pp=418–427}}

    Type conversion

    {{excerpt|Type conversion|Rust}}

    File:Rust 101.webm's Rust team (linux.conf.au conference, Hobart, 2017)]]

    Polymorphism

    = Generics =

    Rust's more advanced features include the use of generic functions. A generic function is given generic parameters, which allow the same function to be applied to different variable types. This capability reduces duplicate code{{sfn|Klabnik|Nichols|2019|pp=171–172}} and is known as parametric polymorphism.

    The following program calculates the sum of two things, for which addition is implemented using a generic function:

    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 = sum(10, 20);

    println!("Sum is: {}", result1); // Sum is: 30

    let result2 = 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.{{sfn|Klabnik|Nichols|2019|pp=171–172,205}}

    = Traits =

    File:UML class diagram Rust stdio.svg

    Rust's type system supports a mechanism called traits, inspired by type classes in the Haskell language, to define shared behavior between different types. For example, the Add trait can be implemented for floats and integers, which can be added; and the Display or Debug traits can be implemented for any type that can be converted to a string. Traits can be used to provide a set of common behavior for different types without knowing the actual type. This facility is known as ad hoc polymorphism.

    Generic functions can constrain the generic type to implement a particular trait or traits; for example, an add_one function might require the type to implement Add. This means that a generic function can be type-checked as soon as it is defined. The implementation of generics is similar to the typical implementation of C++ templates: a separate copy of the code is generated for each instantiation. This is called monomorphization and contrasts with the type erasure scheme typically used in Java and Haskell. Type erasure is also available via the keyword dyn (short for dynamic).{{sfn|Klabnik|Nichols|2019|pp=181,182}} Because monomorphization duplicates the code for each type used, it can result in more optimized code for specific-use cases, but compile time and size of the output binary are also increased.{{sfn|Gjengset|2021|p=25}}

    In addition to defining methods for a user-defined type, the impl keyword can be used to implement a trait for a type.{{sfn|Klabnik|Nichols|2019|pp=93}} Traits can provide additional derived methods when implemented.{{sfn|Klabnik|Nichols|2019|pp=182–184}} For example, the trait Iterator requires that the next method be defined for the type. Once the next method is defined, the trait can provide common functional helper methods over the iterator, such as map or filter.{{sfn|Klabnik|Nichols|2019|pp=281–283}}

    = Trait objects =

    Rust traits are implemented using static dispatch, meaning that the type of all values is known at compile time; however, Rust also uses a feature known as trait objects to accomplish dynamic dispatch, a type of polymorphism where the implementation of a polymorphic operation is chosen at runtime. This allows for behavior similar to duck typing, where all data types that implement a given trait can be treated as functionally equivalent.{{sfn|Klabnik|Nichols|2023|loc=[https://doc.rust-lang.org/book/ch18-02-trait-objects.html 18.2. Using Trait Objects That Allow for Values of Different Types]}} Trait objects are declared using the syntax dyn Tr where Tr is a trait. Trait objects are dynamically sized, therefore they must be put behind a pointer, such as Box.{{sfn|Klabnik|Nichols|2019|pp=441–442}} The following example creates a list of objects where each object can be printed out using the Display trait:

    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.{{sfn|Klabnik|Nichols|2019|pp=379–380}}

    Memory safety

    Rust is designed to be memory safe. It does not permit null pointers, dangling pointers, or data races.{{cite web |url=http://reviews.cnet.com/8301-3514_7-57577639/samsung-joins-mozillas-quest-for-rust/ |title=Samsung joins Mozilla's quest for Rust |last=Rosenblatt |first=Seth |date=2013-04-03 |publisher=CNET |access-date=2013-04-05 |archive-date=2013-04-04 |archive-url=https://web.archive.org/web/20130404142333/http://reviews.cnet.com/8301-3514_7-57577639/samsung-joins-mozillas-quest-for-rust/ |url-status=live}}{{cite web |last=Brown |first=Neil |date=2013-04-17 |title=A taste of Rust |url=https://lwn.net/Articles/547145/ |url-status=live |archive-url=https://web.archive.org/web/20130426010754/http://lwn.net/Articles/547145/ |archive-date=2013-04-26 |access-date=2013-04-25 |website=LWN.net}}{{Cite web|url=https://doc.rust-lang.org/nomicon/races.html|title=Races|website=The Rustonomicon|access-date=2017-07-03|archive-date=2017-07-10|archive-url=https://web.archive.org/web/20170710194643/https://doc.rust-lang.org/nomicon/races.html|url-status=live}}{{cite journal |last1=Vandervelden |first1=Thibaut |last2=De Smet |first2=Ruben |last3=Deac |first3=Diana |last4=Steenhaut |first4=Kris |last5=Braeken |first5=An |title=Overview of Embedded Rust Operating Systems and Frameworks |journal= Sensors|doi=10.3390/s24175818 |date=7 September 2024 |volume=24 |issue=17 |page=5818 |doi-access=free |pmid=39275729 |pmc=11398098 |bibcode=2024Senso..24.5818V }} Data values can be initialized only through a fixed set of forms, all of which require their inputs to be already initialized.{{cite web |title=The Rust Language FAQ |publisher=The Rust Programming Language |url=http://static.rust-lang.org/doc/master/complement-lang-faq.html |url-status=dead |archive-url=https://web.archive.org/web/20150420104147/http://static.rust-lang.org/doc/master/complement-lang-faq.html |archive-date=2015-04-20 |year=2015 |access-date=2017-04-24}}

    Unsafe code can subvert some of these restrictions, using the unsafe keyword.{{sfn|Klabnik|Nichols|2019|pp=418–427}} Unsafe code may also be used for low-level functionality, such as volatile memory access, architecture-specific intrinsics, type punning, and inline assembly.{{sfn|McNamara|2021|p=139, 376–379, 395}}

    Memory management

    Rust does not use garbage collection. Memory and other resources are instead managed through the "resource acquisition is initialization" convention,{{Cite web|title=RAII|url=https://doc.rust-lang.org/rust-by-example/scope/raii.html|access-date=2020-11-22|website=Rust by Example|archive-date=2019-04-21|archive-url=https://web.archive.org/web/20190421131142/https://doc.rust-lang.org/rust-by-example/scope/raii.html|url-status=live}} with optional reference counting. Rust provides deterministic management of resources, with very low overhead.{{Cite web|url=https://blog.rust-lang.org/2015/05/11/traits.html|title=Abstraction without overhead: traits in Rust|website=Rust Blog|access-date=October 19, 2021|archive-date=September 23, 2021|archive-url=https://web.archive.org/web/20210923101530/https://blog.rust-lang.org/2015/05/11/traits.html|url-status=live}} Values are allocated on the stack by default, and all dynamic allocations must be explicit.{{Cite web |title=Box, stack and heap |url=https://doc.rust-lang.org/stable/rust-by-example/std/box.html |access-date=2022-06-13 |website=Rust by Example |archive-date=2022-05-31 |archive-url=https://web.archive.org/web/20220531114141/https://doc.rust-lang.org/stable/rust-by-example/std/box.html |url-status=live }}

    The built-in reference types using the & symbol do not involve run-time reference counting. The safety and validity of the underlying pointers is verified at compile time, preventing dangling pointers and other forms of undefined behavior.{{sfn|Klabnik|Nichols|2019|pp=70–75}} Rust's type system separates shared, immutable references of the form &T from unique, mutable references of the form &mut T. A mutable reference can be coerced to an immutable reference, but not vice versa.{{sfn|Klabnik|Nichols|2019|p=323}}

    Macros

    Macros allow generation and transformation of Rust code to reduce repetition. Macros come in two forms, with declarative macros defined through macro_rules!, and procedural macros, which are defined in separate crates.{{sfn|Klabnik|Nichols|2023|pp=449–455}}{{sfn|Gjengset|2021|pp=101-102}}

    = Declarative macros =

    A declarative macro (also called a "macro by example") is a macro, defined using the macro_rules! keyword, that uses pattern matching to determine its expansion.{{cite web |title=Macros By Example |url=https://doc.rust-lang.org/reference/macros-by-example.html |website=The Rust Reference |access-date=21 April 2023 |archive-date=2023-04-21 |archive-url=https://web.archive.org/web/20230421052332/https://doc.rust-lang.org/reference/macros-by-example.html |url-status=live }}{{sfn|Klabnik|Nichols|2019|pp=446–448}} Below is an example that sums over all its arguments:

    macro_rules! sum {

    ( $initial:expr $(, $expr:expr )* $(,)? ) => {

    $initial $(+ $expr)*

    }

    }

    fn main() {

    let x = sum!(1, 2, 3);

    println!("{x}"); // prints 6

    }

    = Procedural macros =

    Procedural macros are Rust functions that run and modify the compiler's input token stream, before any other components are compiled. They are generally more flexible than declarative macros, but are more difficult to maintain due to their complexity.{{cite web |url=https://doc.rust-lang.org/reference/procedural-macros.html |title=Procedural Macros |website=The Rust Programming Language Reference |access-date=23 Mar 2021 |archive-date=7 November 2020 |archive-url=https://web.archive.org/web/20201107233444/https://doc.rust-lang.org/reference/procedural-macros.html |url-status=live}}{{sfn|Klabnik|Nichols|2019|pp=449–455}}

    Procedural macros come in three flavors:

    • Function-like macros custom!(...)
    • Derive macros #[derive(CustomDerive)]
    • Attribute macros #[custom_attribute]

    Interface with C and C++

    Rust has a foreign function interface (FFI) that can be used both to call code written in languages such as C from Rust and to call Rust code from those languages. {{As of|2024}}, an external library called CXX exists for calling to or from C++.{{Cite web|title=Safe Interoperability between Rust and C++ with CXX|url=https://www.infoq.com/news/2020/12/cpp-rust-interop-cxx/|date=2020-12-06|access-date=2021-01-03|website=InfoQ|language=en|archive-date=January 22, 2021|archive-url=https://web.archive.org/web/20210122142035/https://www.infoq.com/news/2020/12/cpp-rust-interop-cxx/|url-status=live}} Rust and C differ in how they lay out structs in memory, so Rust structs may be given a #[repr(C)] attribute, forcing the same layout as the equivalent C struct.{{cite web |title=Type layout |url=https://doc.rust-lang.org/reference/type-layout.html#the-c-representation |website=The Rust Reference |access-date=15 July 2022 |archive-date=2022-07-16 |archive-url=https://web.archive.org/web/20220716021840/https://doc.rust-lang.org/reference/type-layout.html#the-c-representation |url-status=live }}

    See also

    Notes

    {{Reflist|group=note}}

    References

    {{reflist|refs=

    {{cite web |title=Influences |url=https://doc.rust-lang.org/reference/influences.html |website=The Rust Reference |access-date=December 31, 2023 |archive-date=November 26, 2023 |archive-url=https://web.archive.org/web/20231126231034/https://doc.rust-lang.org/reference/influences.html |url-status=live}}

    }}

    Category:Rust (programming language)

    Category:Programming language syntax