This article describes the built-in data types provided by Rust.
Boolean type
Boolean types represent the logical values of yes and no. It has two values, true and false, and is commonly used in logical expressions to perform operations such as and, or, and not:
fn main() { let x = true; let y: bool = ! x; // let z = x &&y; / / false, logic and arithmetic, with a short circuit function let z = x | | y; // let z = x & y; / / false, the bitwise and operation, with no short circuit function let z = x | y; Let z = x ^ y; // let z = x ^ y; //true, bitwise xor, no short circuit function}
Some comparison expressions are of type Boolean:
fn logical_op(x: i32, y: i32) { let z = x < y; // z is a Boolean type println! ("z = {}", z); }
Boolean expressions can be used in if/while expressions as conditional expressions:
if a >= b {
...
} else {
...
}
C/C++ implicitly converts characters, integers, floating point numbers, and Pointers to Booleans; Python allows the use of strings, lists, dictionaries, and collections in the context of Boolean values. For them, if the values are not 0 or empty, they are treated as true. Rust is more restrictive: the condition of an if/while expression must be a Boolean expression.
The as operator of Rust can explicitly convert a bool to an integer:
assert_eq! (false as i32, 0); assert_eq! (true as i32, 1);
However, you cannot convert a numeric type to a Boolean, in which case an explicit conversion must be made:
let x = 0; let y = 1; assert_eq! (x as bool, false); // Error. As is not allowed to convert numeric types to Boolean types. Assert_eq! (y as bool, true); If x == 0 {... } if y == 1 {... }
In theory, a Boolean type needs only one bit to be represented, but Rust uses an entire byte in memory as a bool, thus allowing the creation of a pointer to a Boolean value.
Character types
The character type of Rust, char, uses 4 bytes of memory space to hold a single Unicode code point. In the code, single-character literals are enclosed by a pair of single quotes:
Let love = '❤' // can embed any Unicode character directly
Escape characters can be used in character literals:
let c1 = '\n'; // let c2 = '\u{7FFF}'; / / Unicode characters
According to the Unicode character standard, the char type is stored in the range of [0x0000,0xD7FF]U[0xE000,0x10FFFF]. The Rust type system dynamically checks to ensure that the char value is always within the valid range described above.
Rust never implicitly converts char to another type. If desired, you can use the as operator to convert a char to a numeric type. If the size of the target numeric type is less than 4 bytes, the higher-order memory of the character is truncated:
assert_eq! ('*' as i32, 42); assert_eq! (' 😻 as i32, 128571); assert_eq! (' 😻 as i8, 59); // The higher-order bytes are truncated
Surprisingly, the AS operator can only convert u8 types to char, since any other numeric type could potentially generate an illegal Unicode code point, which would result in a character value check at run time. However, the library function STD ::char::from_u32() converts the u32 value to an Option
value, ensuring correctness.
In addition, the library provides common utility functions for character types, such as:
assert_eq! ('*'.is_alphabetic(), false); assert_eq! ('b'.is_alphabetic(), true); assert_eq! ('8'.to_digit(), Some(8)); assert_eq! (std::char::from_digit(2, 10), Some('2'));
Integer types
Like integer types in most other programming languages, Rust integer types are a set of fixed-size types that correspond directly to those implemented in modern CPU hardware. The naming of Rust numeric types follows the rule of writing both the bit width and the representation:
The number of bytes | Unsigned integer | Signed integer |
---|---|---|
1 | u8 |
i8 |
2 | u16 |
i16 |
4 | u32 |
i32 |
8 | u64 |
i64 |
The machine related | usize |
isize |
The integer type of Rust is represented by complement. If a variable is of signed type, its highest bit is the sign bit, which is used to distinguish between positive and negative values. If a variable is of unsigned type, its top bit is used to represent data just like any other bit.
Of particular concern are the usize and isize types, which occupy an indefinite amount of memory, depending on the platform on which the program is executed. On a 32-bit system, use four bytes; On a 64-bit system, eight bytes are used.
Integer literals can be represented in a number of ways:
let var1: i32 = 32; // Decimal let var2: i32 = 0xFF; // Let var3: i32 = 0o55; // octal let var4: i32 = 0b1001; / / binary
Literals can be underlined anywhere to improve readability. Such as:
let var5 = 0x_1234_ABCD;
In addition, if you do not specify the type of the integer literal, the default type is i32. If you want to specify the type explicitly, you can add a suffix to the end of the literal:
let var8 = 32; // Let var6 = 123USize; //i6 is usize type let var7 = 0x_ff_u8; Var7 is of u8 type
The u8 value is a bit special because it can represent very common ASCII characters, for which Rust provides byte literals:
let a = b'A'; //u8 indicates the character A, that is, 65
Byte literals can also be escaped:
let b = b'\\'; Let c = b'\n'; let c = b'\n'; Let d = b'\t'; let d = b'\t'; //u8 represents the TAB character, which is 9
Different integer types can be converted using the as operator. Such as:
assert_eq! (10_i8 as u16, 10_u16); // For positive numbers, fill the high order with 0, correct. assert_eq! (10_i8 as u16, 10_u16); // For positive numbers, fill the high order with 0, correct. assert_eq! (-1i16 as i32, -1_i32); // For negative numbers, fill 1 in the highest order, correct. assert_eq! (1000_i16 as u8, 232_u8); // Type truncation occurs, discarding the high-order data. assert_eq! (65535_u32 as i16, -1_i16); // Type truncation occurs, discarding the high-order data.
Rust operates on integer types in a different way than other languages. One of the headaches in integer arithmetic is “overflow.” In C, the arithmetic operation of unsigned type never overflows. If it goes beyond the representation range, the high-order data is automatically discarded. For signed type arithmetic operations, the behavior after an overflow is undefined.
Undefined behavior allows the compiler to make some radical performance optimizations, but it can lead to weird bugs in extreme cases. Rust is designed more to prevent bugs than to squeeze efficiency unconditionally. As a result, Rust handles this problem as follows: By default, when compiling in debug mode, the compiler adds extra code to check for overflows, and when an overflow occurs, panic is caused. When compiling in release mode, the compiler does not check for integer overflow, instead discarding the high order. At the same time, the compiler provides a separate compilation option to manually set the handling policy for overflows: overflow-checks=yes/no.
If you want to control the behavior of integer overflows more precisely, you can call the library’s check_*, saturating_*, and wrapping_* functions, where:
checked_*
The type returned by a series of functions isOption<_>
In case of an overflow, the value returned isNone
.saturating_*
The type returned by a series of functions is an integer, and when an overflow occurs, the value returned is the maximum or minimum value in the range that the type can represent.wrapping_*
The series of functions is truncated directly.
When security is important, try to use these methods instead of the default arithmetic operators:
fn main() { let i = 100i8; println! ("checked: {:? }", i.checked_add(i)); //None println! ("saturating {:? }", i.saturating_add(i)); //127 println! ("wrapping: {:? }", i.wrapping_add(i)); / / - 56}
Floating point types
Rust supports single and double precision floating point types specified by IEEE and complies with the IEEE 754-2008 standard. These types include positive and negative infinities, a distinction between positive and negative zeros, and a non-numeric value:
type | precision | The scope of |
---|---|---|
f32 |
IEEE single precision (at least 6 decimal places) | $$(3.4 \ times10 ^ {38}, 3.4 \ times10 ^ {38}) $$ |
f64 |
IEEE double precision (at least 15 decimal places) | $$(1.8 \ times10 ^ {308}, 1.8 \ times10 ^ {308}) $$ |
As with integer types, you can explicitly specify the type of a literal using the suffixes f32 and f64.
The Rust library defines constants for special VALUES of FLOATing-point numbers defined by IEEE, such as INFINITY, NEG_INFINITY, NAN, MIN, and MAX. The library also defines common mathematical functions for both precision floating-point types:
fn main() { assert_eq! (5 f32. SQRT () * 5 f32. SQRT (), 5.0); assert_eq! (1.01 f64. Floor (), 1.0); assert! ((1.0 / STD: : f32: : INFINITY). Is_sign_negative ()); }
In addition, Rust does not accept automatic type conversions from integer types to floating point types.