Rust lang series episode #25 — pointers (#rust-series)
Good morning everyone, there is a new Rust lang episode. We will talk about pointers in Rust, how to handle them and few things around.
Content
- Pointers
- Stack and heap
- Box
- References
- Raw pointers
- Summary
Pointers
Pointer in programming jargon means something that points to certain place in memory. In especially low level programming languages we can create, get and work with pointers. As you can imagine it uncontrolled pointer is a strong "weapon" which can lead to many undesirable issues. That's why raw pointer in Rust it's not used as a primary feature and we rather using references if needed which has lot of guarantees but are on the other hand more restricted.
Now it should be mentioned that although technically there is just one physical memory (although there can be multiple modules) there are two major memory areas from application programming point of view. Those are stack and heap. Stack is place where function calls are stored, parameters and static variables are kept here whereas heap is a place for dynamic content in general.
Variables on stack
So far we've mostly created variables that live on stack as it's memory for static variables. There are some advantages but not always possible to use just stack especially when you need to work with dynamic content.
let on_stack = 1;
Variables on heap
When dealing with data types that can change their size or are created during runtime we need to use heap. In Rust we can use Box to allocate heap in safe way. Heap is therefore part of memory for dynamic allocation.
let on_heap = Box::new(1);
But don't be tricked, even if we define vector on stack its data are always on heap because it has dynamic content.
let on_stack_and_help = vec![1,2,3];
Create new boxed value
Now we show Box type which is major weapon for heap allocation.
Box<T>
is an owned pointer or simply "a box".
let boxed_integer = Box::new(5);
let boxed_vector = Box::new(vec![1,2,3]);
let boxed_str = Box::new("Boxed Steemit string");
box keyword
If you prefer to use nightly Rust version with latest language features, you can also use box keyword to create boxed variables.
#![feature(box_syntax)]
fn main() {
let boxed_integer = box 5;
let boxed_vector = box vec![1,2,3];
let boxed_str = box "Boxed Steemit string";
println!("boxed_integer: {}", boxed_integer);
println!("boxed_vector: {:?}", boxed_vector);
println!("boxed_str: {}", boxed_str);
}
# output
boxed_integer: 5
boxed_vector: [1, 2, 3]
boxed_str: Boxed Steemit string
Note that you can switch to nightly Rust version by using
rustup default nightly
and back to stable like this
rustup default stable
Automatic Box dereferencing
Notice that when Rust can provide auto dereference. It also works with Box in certain cases because we can imagine Box as safe reference and rust knows how to dereference it in cases like this. Therefore this will work
let boxed_str = Box::new("Boxed Steemit string");
println!("{}", boxed_str);
# output
Boxed Steemit string
And it's the same as this
let boxed_str = Box::new("Boxed Steemit string");
println!("{}", *boxed_str);
# output
Boxed Steemit string
Dereferencing boxes
If Rust compiler cannot dereference automatically, it's required to dereference manually with *
. My advice is to alway dereference explicitly, it makes code obvious for a reader or maintainer. Check this piece of code.
let a = Box::new(5);
let b = 5;
let c = *a + b;
println!("c : {}", c);
# output
c : 10
References
Speaking about pointers and related things, we already know reference which are either &T
or mutable &mut T
. References are controlled or guaranteed pointers in Rust. This means they are always valid and should never point to invalid memory position.
let original = 1;
let reference_to_original = &original;
println!("{}", reference_to_original);
# output
1
Raw pointers
There are also C-like raw pointer in rust with no lifetime and ownership in rust. They under lowest level of restrictions and cannot be dereferenced outside unsafe block
*const T
and *mut T
.
let original = "Steemiters know raw pointers";
let raw_pointer: *const &str = &original;
let points_at = unsafe {*raw_pointer};
println!("{}", points_at);
# output
Steemiters know raw pointers
Typical usage of raw pointers is FFI (Foreign Function Interface) which allows calling C language functions from various libraries which we will explain in the future.
Summary
Variables are created on
- stack - static
- heap - dynamic and boxed
Rust provides two major types of pointers with various levels of security
- safe - references
- unsafe - raw pointers
Postfix
That's all for now, thank you for your appreciations, feel free to comment and point out possible mistakes (first 24 hours works the best but any time is fine). May Jesus bless your programming skills, use them wisely and see you next time.
Meanwhile you can also check official documentation related to pointers: