Rust Tutorial #4 - Data Types Rust Programming Tutorial: Primitive Data Types This blog post summarizes the key concepts of primitive data types in Rust, as covered in a video tutorial. We'll explore scalar and compound types, along with their subtypes and practical examples. Scalar Types Scalar types represent a single value. Rust is statically-typed, meaning you must explicitly or implicitly define the type of a variable. Integers Signed Integers: Represent both positive and negative numbers. Examples include i8 , i16 , i32 , i64 , and i128 , indicating the number of bits used for storage. i32 is the default integer type. Unsigned Integers: Represent only non-negative numbers. Examples include u8 , u16 , u32 , u64 , and u128 . Type Bits Range i8 8 -128 to 127 u8 8 0 to 255 i32 32 -2,147,483,648 to 2,147,483,647 u32 32 0 to 4,294,967,295 i64 64 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 u64 64 0 to 18,446,744,073,709,551,615 Example: let x: i32 = 2; // Explicitly defining an i32 integer. Floating-Point Numbers f32 (single-precision) and f64 (double-precision) are used to represent numbers with decimal components. f64 is the default. Example: let y: f32 = 10.9; Booleans Represents true or false values. Example: let z = false; Characters Represents a single Unicode character. Example: let c = 'a'; Compound Types Compound types can group multiple values together. Tuples Ordered, fixed-size collections of values of potentially different types. Example: let tup: (i32, f64, u8) = (500, 6.4, 1); Access elements using indexing: tup.0 , tup.1 , tup.2 . To modify a tuple, use the mut keyword. Arrays Ordered collections of values of the same type. Example: let arr = [1, 2, 3, 4, 5]; Access elements using indexing, similar to tuples. Arrays have a fixed size. Type Safety and Implicit Conversions Rust's type system helps prevent errors by enforcing type compatibility. Implicit conversions between types are limited; explicit conversion might be necessary. Be mindful of type mismatches to avoid runtime errors. Summary This tutorial covered the fundamental data types in Rust: integers, floating-point numbers, booleans, characters, tuples, and arrays. Understanding these types is crucial for writing correct and efficient Rust code. Remember to pay close attention to type declarations and potential type conversion issues to avoid common errors. as I was saying, we have primitive data types. Now these are the most basic data types. We can kind of use them without having to do anything special. And that's really what the primitive data types are just the most basic ones in a language. So that's what we're going to look at here. Now Rust has two categories of primitive data types. We have a scalar type and then we have a compound. or the types kind of fall into the categories of scalar and compound. Now scalar simply means a single value, so something like an integer, something like a Boolean if you're familiar with data types. Whereas compound means multiple values. So here we actually have an array and we have a tuple which we're going to look at in a minute. for now though, let's look at the scalar data types and how we use those in Rus. So as we know Russ is a statically typed language, that means it's very important for us to understand what type our variables are and we can implicitly have the type decided or we can explicitly define it. when we a variable. So if I do something like, let x equals 2 implicitly, the compiler is going to say this is an integer. However, I can explicitly define the type by doing something like colon and then i32. And what this means is that I am assigning the type of integer 32 to the variable x. Then I'm storing the value 2 as this type i 32. So this is the first type, right integer. This stands for a signed integer. that's using 32 bits to represent and store the number. Now, the number of bits essentially tells you how large of a number you can store and the range of numbers that can be stored in a variable, I'm not really going to get too much into binary here, because that's beyond the scope of what I'm, uh, meaning to teach, but 32 is actually the default value for an integer in rust. So what I mean by that is if I do something like, let x equals two, by default, the type of this variable is going to be i32 because this is an integer value. And we could assign it to many different types, but we default it to i32. Okay, so let's keep looking at integers. So as you saw, we had i32. This is the default. But we also have multiple other types of integers. We have i8, We have i 16. Like this, I believe we have i 64. And I think we even have all the way up to i 128. Now, again, these are the number of bits that's going to be used for all of these integers. And in case you're unfamiliar with an integer, this is any number that does not contain a decimal part. So any whole number essentially something like eight, something like negative nine something like 9727 negative. Ah that's totally fine. And since we have a signed integer that's just what the default integer is. That means we can use a negative sign for our value. Okay so those are the different types for our integers. We then have the data type of uiNT. Now uiNT is unsigned integer and it's exactly the same as integer except you can't use negatives. So if I do something like let x and then this is u32, I'm saying that i'm going to assign this variable the type of unsigned integer 32 and then give it the value of 972. now I can't assign anything to this variable that has a sign to it. Like I can't have a negative number here because this is an unsigned integer Using 32 bits to store the number. Now I don't want to get too much into binary here in the different ranges and all of that. But I'll just quickly mention that when we're looking at something like a U32 versus an i32, Or we could even go even simpler with something like U8 and i8. These are able to represent the exact same number of unique values. So using eight bits, you're always able to represent two to the exponent eight unique values. However, the range of values that you can represent is different. when you're using a u8 versus an i8. For example, with an unsigned integer, you don't have to represent any negative numbers. So the range of numbers you can represent is 0 to 2 to the exponent eight minus one. because you have eight bits, you put eight here for the exponent. And this is the range you can represent, I believe, from zero to uh, this is going to be I think 128 or it's going to be oh sorry 255. My apology, 0 to 255. That's the range of numbers you can represent with a u8. However, with an i8 this is going to change. now you're going to be a negative to the exponent and then this is going to be 7. You have to reduce the exponent by 1. 2. two to the exponent seven minus one. So with i eight you're gonna have a range of negative 128 to 127 in terms of the numbers you can actually represent whereas this is going to be zero to 255. Hopefully that makes sense. Uh, if it doesn't then I'd say refresh yourself on binary a little bit but I just wanted to quickly mention that okay, so we've looked at signed integers and unsigned integers and the different quantities of course with u32 we have u64 Uh, with a lowercase and we'd have u 128 as well if I spell that correctly Okay, so that is it for integers Next we're going to look at point values so in Rust we have two floating data types. We have f32 and f64 And this means we're going to have a floating point value using 32 bits And we're going to have a floating point value using 64 bits. This is also known as single precision And double precision. So let's have a look at one of those values. We could do something like let let's go floating point and then we'll say the type of this is going to be F32 and this could be equal to something like 10.9 So as soon as we have this decimal component here Now we know we're having a floating point value and we need to use the f32 or 64 type. Now the default type if you don't actually explicitly define it, is going to be f32 using single precision. Okay All right so I think that's really it for floats. Um those are the types f32, f64. If you're not sure just use f32. Now we can move on to Notes from https://rust-book.cs.brown.edu/ch03-02-data-types.html Data Types - The Rust Programming Language Rust Data Types: A Comprehensive Overview These notes summarize key concepts from Chapter 3.2 ("Data Types") of The Rust Programming Language . We'll explore various data types, their usage, and potential pitfalls. 1. Type Annotations and Errors Rust is a statically-typed language, meaning you must specify the type of a variable. Forgetting to do so leads to compilation errors. Example: The code let guess: u32 = "42".parse().expect("Not a number!"); correctly annotates guess as an unsigned 32-bit integer ( u32 ). Omitting : u32 results in a compiler error ( E0284 ). The compiler needs to know the type to perform type checking at compile time. 2. Integer Types Rust offers various integer types, differing in size and signedness. Type Size (bits) Signed? Range i8 8 Yes -128 to 127 i16 16 Yes -32,768 to 32,767 i32 32 Yes -2,147,483,648 to 2,147,483,647 i64 64 Yes -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 i128 128 Yes (Very large range) u8 8 No 0 to 255 u16 16 No 0 to 65,535 u32 32 No 0 to 4,294,967,295 u64 64 No (Very large range) u128 128 No (Very large range) usize System dependent No Size of a memory address isize System dependent Yes Size of a memory address Integer Literals: You can specify integer literals using various suffixes (e.g., 57u8 , -2i32 ). See Table 3-2 in the original document for a complete list. Integer Overflow: Attempting to store a value larger than the type's capacity leads to overflow . Rust handles this in different ways depending on the build mode (debug mode panics, release mode wraps around). 3. Floating-Point Numbers Rust supports two floating-point types: f32 : Single-precision floating-point number (32 bits) . f64 : Double-precision floating-point number (64 bits) . This is the default type for floating-point numbers. Example: let x = 2.0; // f64 let y: f32 = 3.0; // f32 4. Basic Arithmetic Operations Rust supports standard arithmetic operations: + (addition) - (subtraction) * (multiplication) / (division) % (remainder) 5. Booleans The bool type represents boolean values: true false Example: let t = true; let f: bool = false; 6. Characters The char type represents a Unicode scalar value . Example: let c = 'z'; let z: char = 'ℤ'; let heart_eyed_cat = '😻'; 7. Tuples Tuples are immutable collections of values of different types. Example: let tup: (i32, f64, u8) = (500, 6.4, 1); Accessing elements: let (x, y, z) = tup; or let five_hundred = x.0; 8. Arrays Arrays are fixed-size collections of values of the same type . Example: let a = [1, 2, 3, 4, 5]; let a: [i32; 5] = [1, 2, 3, 4, 5]; (explicit type and size) let a = [3; 5]; creates an array of five 3s . Accessing elements: let first = a[0]; Remember that indexing starts at 0 . Out-of-bounds access: Attempting to access an element outside the array's bounds leads to a panic (program crash). Key Takeaways Rust's type system enhances safety and performance. Understanding different data types is crucial for writing correct and efficient Rust code. Pay close attention to integer overflow and array bounds to prevent runtime errors. Use appropriate types for your data to avoid unexpected behavior.