Rust variables: Difference between revisions
(→Scope) |
|||
(12 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
= Declaration = | = Declaration = | ||
<blockquote> | <blockquote> | ||
You may declare variables in rust, but it is not necessary. | You may declare variables in rust, but it is not necessary.<br> | ||
It is illegal to use a variable before it has been bound to a value (null does not exist in rust). | |||
<syntaxhighlight lang="rust"> | <syntaxhighlight lang="rust"> | ||
let foo: u8; | let foo: u8; | ||
Line 13: | Line 15: | ||
let age = 200u8; // type-suffix | let age = 200u8; // type-suffix | ||
let age = 200; // implied type | let age = 200; // implied type | ||
let age: i32 = age; // type cast from 'u8' to 'i32' | |||
// conditional assignment | |||
let weather = if season == "fall" { "lovely" } else { "fine I guess" } | |||
// loop assignment | |||
let result = loop { break 123 } // break can be passed a return val | |||
// conditional on assignment (match-option, that only handles success) | |||
if let Some(val) = my_val { | |||
println!("my val is {}", val); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- Assignment --> | </blockquote><!-- Assignment --> | ||
= Type Casting = | |||
<blockquote> | |||
When type fits into another (ex. u8 to u32). | |||
<syntaxhighlight lang="rust"> | |||
let age: u8 = 30; | |||
let age: u32 = age; // type cast from 'u8' to 'i32' | |||
u64::from(30i8); // type cast from 'i8' to 'u64' | |||
</syntaxhighlight> | |||
When type does not fit into another (ex. i32 to i8). | |||
<syntaxhighlight lang="rust"> | |||
use std::convert::TryFrom; | |||
i8::try_from(100i32).ok(); // try cast from 'i32' to 'u8' (result type) | |||
</syntaxhighlight> | |||
</blockquote><!-- Type Casting --> | |||
= Literals = | = Literals = | ||
Line 29: | Line 61: | ||
<blockquote> | <blockquote> | ||
Constants can be declared in any scope (including global).<br> | Constants can be declared in any scope (including global).<br> | ||
constants cannot be changed once assigned. | constants cannot be changed once assigned.<br> | ||
memory addresses for the same const will not be the same (const is inlined where used). | |||
<syntaxhighlight lang="rust"> | <syntaxhighlight lang="rust"> | ||
const SALT: &str = "$6$r1ohStL5/UwpNnls"; | const SALT: &str = "$6$r1ohStL5/UwpNnls"; // inlined where used | ||
static RETRIES: i8 = 5; | static RETRIES: i8 = 5; // not-inlined where used (same mem-addr) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- Constants --> | </blockquote><!-- Constants --> | ||
Line 64: | Line 97: | ||
= Mutability and Freezing = | = Mutability and Freezing = | ||
<blockquote> | |||
== Mutability == | |||
<blockquote> | <blockquote> | ||
All variables are immutable by default in rust.<br> | All variables are immutable by default in rust.<br> | ||
Line 72: | Line 107: | ||
age += 1; | age += 1; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- Mutability --> | |||
== Freezing == | |||
<blockquote> | |||
You can bind mutable variables as immutable within inner scopes.<br> | You can bind mutable variables as immutable within inner scopes.<br> | ||
This will prevent them from being modified within that scope.<br> | This will prevent them from being modified within that scope.<br> | ||
Line 83: | Line 121: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- Freezing --> | |||
== Shadowing == | |||
<blockquote> | |||
You can shadow immutable variables, the result is effectively a new variable. | |||
<syntaxhighlight lang="rust"> | |||
// valid! | |||
let height = 2; println!("{height}"); | |||
let height = 3; println!("{height}"); | |||
</syntaxhighlight> | |||
</blockquote><!-- Shadowing --> | |||
</blockquote><!-- Mutability --> | </blockquote><!-- Mutability --> | ||
= Shadowing/Type Conversion = | |||
<blockquote> | |||
<syntaxhighlight lang="rust"> | |||
let num = "32"; | |||
let num: i8 = num.parse().unwrap(); // converts 'str' to 'i8' | |||
</syntaxhighlight> | |||
</blockquote><!-- Shadowing/Type Conversion --> | |||
= Copy/Cloning Values = | |||
<blockquote> | |||
== Stack Allocated Copies (Shallow) == | |||
<blockquote> | |||
Fixed-size variables allocated to the stack can implement the <code>Copy</code> trait.<br> | |||
This allows you to shallow copy objects through assignment. | |||
<syntaxhighlight lang="rust"> | |||
let x = 5; | |||
let mut y = x; | |||
y += 1; | |||
println!("x = {}, y = {}", x, y); | |||
// x = 5, y = 6 | |||
</syntaxhighlight> | |||
</blockquote><!-- Stack Allocated Copies --> | |||
== Heap Allocated Copies (Deep) == | |||
<blockquote> | |||
<syntaxhighlight lang="rust"> | |||
// copy pointer | |||
let foo_orig = String::from("foo"); | |||
let foo_copy = foo_orig; | |||
// deep copy | |||
let foo_orig = String::from("foo"); | |||
let foo_copy = foo_orig.clone(); // new allocation in heap | |||
</syntaxhighlight> | |||
</blockquote><!-- Heap Allocated Copies (Deep) --> | |||
</blockquote><!-- Copy/Cloning Values --> | |||
= Introspection = | = Introspection = | ||
<blockquote> | <blockquote> | ||
Easiest way is to call a method on it that doesn't exist.<br> | |||
the stacktrace will have the type. | |||
<syntaxhighlight lang="rust"> | <syntaxhighlight lang="rust"> | ||
// | let foo = 123; | ||
foo.cuss(); // <-- invalid method | |||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- Introspection --> | </blockquote><!-- Introspection --> |
Latest revision as of 23:06, 7 February 2023
Declaration
You may declare variables in rust, but it is not necessary.
It is illegal to use a variable before it has been bound to a value (null does not exist in rust).let foo: u8;
Assignment
let age: u8 = 200; // typed let age = 200u8; // type-suffix let age = 200; // implied type let age: i32 = age; // type cast from 'u8' to 'i32' // conditional assignment let weather = if season == "fall" { "lovely" } else { "fine I guess" } // loop assignment let result = loop { break 123 } // break can be passed a return val // conditional on assignment (match-option, that only handles success) if let Some(val) = my_val { println!("my val is {}", val); }
Type Casting
When type fits into another (ex. u8 to u32).
let age: u8 = 30; let age: u32 = age; // type cast from 'u8' to 'i32' u64::from(30i8); // type cast from 'i8' to 'u64'When type does not fit into another (ex. i32 to i8).
use std::convert::TryFrom; i8::try_from(100i32).ok(); // try cast from 'i32' to 'u8' (result type)
Literals
Literal types can be declared without assigning a type.
let float = 3.14; // f64 let integer = 7; // i32
Constants
Constants can be declared in any scope (including global).
constants cannot be changed once assigned.
memory addresses for the same const will not be the same (const is inlined where used).const SALT: &str = "$6$r1ohStL5/UwpNnls"; // inlined where used static RETRIES: i8 = 5; // not-inlined where used (same mem-addr)
Scope
Variable scope is bound to the block they are defined in
{ ... }
.
Blocks may be defined arbitrarily to create inner scopes.
Variables defined in outer scopes are accessible in inner scopes.fn foo() { let foo = 1; { println!("{}", foo); } }
Access Control
Everything is private in rust by default, unless explicitly declared otherwise.
Elements are declared public with thepub
modifier.fn foo() { ... } // private function pub fn foo() { ... } // public function
Mutability and Freezing
Mutability
All variables are immutable by default in rust.
You can make them mutable with themut
modifier.let mut age: i8 = 30; age += 1;Freezing
You can bind mutable variables as immutable within inner scopes.
This will prevent them from being modified within that scope.
This is known as freezing a variable.let mut age: i8 = 30; { let age = age; // immutable until block scope ends }Shadowing
You can shadow immutable variables, the result is effectively a new variable.
// valid! let height = 2; println!("{height}"); let height = 3; println!("{height}");
Shadowing/Type Conversion
let num = "32"; let num: i8 = num.parse().unwrap(); // converts 'str' to 'i8'
Copy/Cloning Values
Stack Allocated Copies (Shallow)
Fixed-size variables allocated to the stack can implement the
Copy
trait.
This allows you to shallow copy objects through assignment.let x = 5; let mut y = x; y += 1; println!("x = {}, y = {}", x, y); // x = 5, y = 6Heap Allocated Copies (Deep)
// copy pointer let foo_orig = String::from("foo"); let foo_copy = foo_orig; // deep copy let foo_orig = String::from("foo"); let foo_copy = foo_orig.clone(); // new allocation in heap
Introspection
Easiest way is to call a method on it that doesn't exist.
the stacktrace will have the type.let foo = 123; foo.cuss(); // <-- invalid method