Welcome back fellow Rustaceans🦀!

In the last article, we learned how to print custom data in Rust using the Debug and Display traits. Today, we're diving into the fundamentals: variables! Variables are the containers that store your data.

In Rust, we use the let keyword to declare a variable. Here's the basic structure:

let my_variable: data_type = value;
  • let: Tells Rust we're creating a variable.
  • my_variable: The name you choose for your variable.
  • data_type: The kind of data the variable will hold (more on this soon!).
  • value: The initial data you assign to the variable.

One key difference between strongly typed languages like C and loosely typed languages like Python lies in how you declare variables. In strongly typed languages like C, you must explicitly define the data type of each variable. Loosely typed languages, however, can infer the data type based on how the variable is used. Rust, while being strongly typed and compiled, adopts a slightly different approach. Let's try this example

In the above example, we declare a variable a of type unsigned int 32 (u32) and assign it a value of 1000. As expected, when we run the code, the value of 'a' is printed.

Now, let's remove the :u32 from the let statement, rebuild, and re-run our code. Surprisingly, it still works!

This shows that even though Rust is a statically typed language, it can often infer the type of a variable based on the value you give it. If you have Rust Analyzer installed, you'll notice it highlights the type of a as i32. That's because Rust defaults to a signed 32-bit integer for whole numbers.

In most cases, the Rust compiler can figure out the variable type based on how it's used. However, there are times when it can't, such as with function arguments (more on that later). In those situations, you'll need to specify the type explicitly.

Data Types

Rust provides a variety of data types to suit different needs. Here's a closer look at some common ones, along with their sizes and ranges:

Screenshot 2024-03-17 090340.png

One key difference you'll notice compared to C is how Rust handles characters. Unlike C, Rust's char type is not limited to a single byte. Instead, it represents a single Unicode Scalar Value. This means Rust's characters support the vast range of characters, symbols, and emojis used across different languages and writing systems. Using UTF-8 encoding ensures your Rust programs can handle text from around the world!


Pointer Size


The size of a pointer can vary depending on your computer's architecture. For instance:

  • On a 32-bit system, pointers typically occupy 4 bytes
  • On a 64-bit system, pointers often take up 8 bytes

Rust employs usize and isize to abstract away architecture-specific memory details, making your code more portable.

Data Type Description Purpose
usize Unsigned integer, size equivalent to a pointer on the system Indexing arrays, collections, memory operations
isize Signed integer, size equivalent to a pointer on the system Similar to usize, but allows negative values

These pointer-sized types offer advantages when working with memory addresses or memory-related operations. They can be equivalent to u32/u64 and i32/i64 depending on whether you're on a 32-bit or 64-bit system. They ensure your code is adaptable to different architectures without needing to explicitly specify the pointer size. This promotes code portability and simplifies memory management tasks.

Finding Size of a type

Sometimes, knowing the exact size a data type takes up in memory is important. If you're coming from a C background, the concept of sizeof might be familiar. Rust's standard library offers a similar functionality with std::mem::size_of.

Let's check out the syntax of std::mem::size_of according to the Rust documentation https://doc.rust-lang.org/std/mem/fn.size_of.html

pub const fn size_of<T>() -> usize

This syntax might seem a bit unfamiliar if you're coming from a C background.

std::mem::size_of is a generic function. Generics in Rust allows you to write code that can work with multiple different data types without sacrificing type safety. We will learn about generics in much more detail later. For now, you can think of them as blueprints for functions where you can plug in specific data types later.

However, unlike functions with regular arguments, the Rust compiler sometimes needs extra help figuring out the specific type you want to use with a generic. This is where the turbo fish syntax (::<>) comes in. It's a way to explicitly tell the compiler which concrete type to fill into a generic placeholder. For example, size_of::<bool>  specifies that you want the size of a bool type specifically.
Let's print the size of all the types we have learnt so far.

use std::mem::size_of;

fn main() {
    println!("Data type sizes in Rust:");

    println!("bool:        {} bytes", size_of::<bool>());
    println!("char:        {} bytes", size_of::<char>());
    println!("i8:          {} bytes", size_of::<i8>());
    println!("i16:         {} bytes", size_of::<i16>());
    println!("i32:         {} bytes", size_of::<i32>());
    println!("i64:         {} bytes", size_of::<i64>());
    println!("u8:          {} bytes", size_of::<u8>());
    println!("u16:         {} bytes", size_of::<u16>());
    println!("u32:         {} bytes", size_of::<u32>());
    println!("u64:         {} bytes", size_of::<u64>());
    println!("f32:         {} bytes", size_of::<f32>());
    println!("f64:         {} bytes", size_of::<f64>());
    println!("usize:       {} bytes", size_of::<usize>());
    println!("isize:       {} bytes", size_of::<isize>());
}

Conclusion

In today's article, we learned about different data types in Rust. We explored how Rust's chars offer more flexibility than C, and how we can use usize and isize to streamline working with pointers. In our upcoming articles, we'll delve into the core concepts of Rust and explore how they differ from C in areas like pointers, argument parsing, and more. If you'd like to strengthen your understanding of these C concepts, we have a fantastic course available. Here's the link to join:

C Pointers: Secrets every Embedded Engineer must know! (90 days access) | Embedded Systems, in Pyjama!
One of the differences between a rookie and a pure pro Embedded Software or System’s Engineer is how well they understand pointers in C! For the aspiring engineers, the career growth is usually limited by the lack of intuitive understanding of how pointers work. Embedded code and OS code (like that of Linux) uses pointers so heavily that it is impossible to no come across these. I wish someone had created this course when I was starting off, could have saved 4 years of my life struggling with pointers. Designed by Silicon industry veterans (the inpyjama.com team), this course will enable you to think and use pointers like a professional senior/staff engineer in ~6Hrs - the way to visualise, think, reason and design using pointers like experts do.By the end of the course you would have learned: What pointers are, how to think about them and how to design with them. Different types of pointers - Datatype based, void and function (callbacks) pointers. Understand and use the syntax associated with pointers, the meaning of *, ., -> and & in context of the pointer. To reason about Multilevel pointers, Arrays and the difference between the two. To use Pointer Arithmetic and different ways to dereference pointers. To reasons about errors, fatalities resulting from the incorrect use of pointers. How opensource code uses pointers - Baremetal, FreeRTOS and Linux file operations. The course takes a detailed hands-on approach, explaining the code, the theory and the underlying system details one should consider when dealing with pointers in C. It is full of graphics, annotation and hands on demo that the students can try along in GitHub Codespaces.Course Contents: Section 1 : Mental Models, Motivation and Reasoning about pointers (7 lectures | 26min) What to Imagine? 8:40 Assignment - 1 IMPORTANT: Checking results of the assignments Resources (2) | 2:11 What is a pointer? 7:00 Assignment - 2 Why pointers? 8:25 Assignment - 3 Section 2 : Pointers: Syntax and Code 7 lectures | 25min Environment Setup 5:47 Declaring/Defining a Pointer 9:51 Assignment - 4 Pointer Variable and Address 1:32 * and & in relation to pointer variable 4:17 Declaration and Definition 4:02 Assignment - 5 Section 3 : Multi-level Pointers 12 lectures | 1hrs 23min Use of * and & Resources (1) | 23:38 Assignment - 6 Array and Pointers - Similarity and differences Resources (1) | 20:42 Assignment - 7 Many *s and Many &s Resources (1) | 4:39 Assignment - 8 pointer to pointer Resources (1) | 13:05 Assignment - 9 Array of pointers Resources (1) | 13:05 Assignment - 10 Different types of Pointers 7:51 Assignment - 11 Section 4 : Pointer to Data 7 lectures | 40min Pointing to data with a Datatype Resources (1) | 7:08 Assignment - 12 Pointer to a struct, *, . and -> Resources (1) | 12:28 Assignment - 13 Pointer Arithmetic Resources (1) | 16:37 Memory model and pointer to data 4:27 Assignment - 14 Section 5 : Pointer to code - Function pointers or Callback 7 lectures | 39min What is a function pointer? 11:57 function pointers syntax Resources (1) | 10:46 Assignment - 15 typedef and function pointers Resources (1) | 9:44 Assignment - 16 Example - function pointer array Resources (1) | 6:44 Assignment - 17 Section 6 : void pointer 5 lectures | 39min What is a void pointer? Resources (1) | 9:51 Assignment - 18 Whats is NULL and NULL pointer? Resources (1) | 14:34 Assignment - 19 void pointers in - Linux source code 14:44 Section 7 : Pointers, Dynamic allocation and problems 11 lectures | 52min heap: malloc() and free() Resources (1) | 13:19 Allocation failure Resources (1) | 13:44 Assignment - 20 Memory Leak 1:21 Assignment - 21 Dangling Pointer 6:00 Assignment - 22 Double free() 10:09 Assignment - 23 pointer manipulation and unowned memory 7:29 Assignment - 24 Section 8 : Open Source code and Pointers 3 lectures | 17min Baremetal Code 9:04 FreeRTOS 5:42 Linux 2:54 Why 90 day access? We have enough funds to pay (out of our pockets) for the backend and server streaming (video is heavy on band width) to offer garunteed access to the last use of this course. We do not wish to be bound to this platform forever and wish to move to better infrastructure in future. Promising lifetime access is very difficult at the moment for these reasons.


See you in the next article!