[rust] basic data type

Time:2021-7-27

This article introduces the built-in data types provided by rust.

Boolean type

Boolean typeLogical values representing yes and No. It has two values:trueandfalseGenerally used in logical expressions, and, or, and not operations can be performed:

fn main() {
    let x = true;
    let y: bool = ! x;    // False, inverse operation
    
    let z = x && y;        // False, logic and operation, with short circuit function
    let z = x || y;        // True, logical or operation, with short circuit function
    let z = x & y;        // False, bitwise and operation, without short circuit function
    let z = x | y;        // True, bitwise OR operation, without short circuit function
    let z = x ^ y;        // True, bitwise exclusive or operation, without short circuit function
}

Some comparison expressions are of boolean type:

fn logical_op(x: i32, y: i32) {
    let z = x < y;    //  Z is a boolean type
    println!("z = {}", z);
}

Boolean expressions can be used inif/whileIn the expression, as a conditional expression:

if a >= b {
    ...
} else {
    ...
}

C / C + + will implicitly convert characters, integers, floating-point numbers and pointers to Boolean values; Python allows strings, lists, dictionaries, collections to be used in Boolean context. For them, if these values are not0Or empty, treat them astrue。 Relatively speaking, rust is more stringent:if/whileThe condition of an expression must be a Boolean expression.

Rust’sasOperators canboolValues are explicitly converted to integer types:

assert_eq!(false as i32, 0);
assert_eq!(true as i32, 1);

However, numeric types cannot be converted to Boolean types, in which case they must be explicitly converted:

let x = 0;
let y = 1;
assert_ eq!( x as bool, false);    // Error, as is not allowed to convert numeric type to boolean type
assert_ eq!( y as bool, true);    // Error, as is not allowed to convert numeric type to boolean type

//Explicit comparison must be used
if x == 0 {...}
if y == 1 {...}

Theoretically, a boolean type needs only one bit to represent, but rust uses an entire byte in memory asboolValue, thus allowing the creation of a pointer to a Boolean value.

Character type

Character type of rustcharUse 4 bytes of memory space to save a single Unicode code point. In the code, a single character literal is surrounded by a pair of single quotation marks:

let love = ' ❤' // You can embed any Unicode character directly

Escape characters can be used in character literals:

let c1 = '\n';          // Newline character
let c2 = '\u{7FFF}'; // Unicode character

According to the standard of Unicode characters,charThe range of code points saved by type is[0x0000,0xD7FF]U[0xE000,0x10FFFF]in Rust’s type system will conduct dynamic check to ensurecharThe value of is always within the above legal range.

Rust never implicitlycharAnd other types. If necessary, you can useasOperator willcharType to numeric type. If the size of the target numeric type is less than 4 bytes, the high-order memory of the character will be truncated:

assert_eq!('*' as i32, 42);
assert_eq!('😻' as i32, 128571);
assert_ eq!(''  as i8, 59);    // High byte truncated

Unexpectedly,asOperators can onlyu8Type conversion tocharThis is because any other numeric type may generate illegal Unicode code points, which will lead to character value checking at run time. However, the functions of the standard librarystd::char::from_u32()Canu32Convert value toOption<char>Value to ensure correctness.

In addition, the standard library also provides common tool 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 type

Like the integer types in most other programming languages, the rust integer type is a set of fixed size types corresponding to the types directly implemented in modern CPU hardware. The naming of rust numeric type follows a rule, that is, write the bit width and expression form at the same time:

Number of bytes Unsigned integer Signed integer
1 u8 i8
2 u16 i16
4 u32 i32
8 u64 i64
Machine related usize isize

The integer type of rust is represented by a complement. If a variable is a signed type, its highest bit is the sign bit, which is used to distinguish whether the value is positive or negative; If a variable is an unsigned type, its highest bit, like other bits, is used to represent data.

What needs special attention isusizeandisizeType, the memory space they occupy is uncertain, which is related to the platform on which the program executes. If on a 32-bit system, four bytes are used; If on a 64 bit system, eight bytes are used.

The literal value of an integer value can be expressed in many ways:

let var1: i32 = 32;        // decimal system
let var2: i32 = 0xFF;    // hexadecimal
let var3: i32 = 0o55;    // octal number system
let var4: i32 = 0b1001;    // Binary

Literals can be underlined anywhere to improve readability. For example:

let var5 = 0x_1234_ABCD;    

In addition, if the type of integer face quantity is not specified, it is used by defaulti32Type; If you want to explicitly specify the type, you can add a suffix to the end of the literal:

let var8 = 32;            // No write type. The default is I32
let var6 = 123usize;    // I6 is of type use
let var7 = 0x_ ff_ u8;    // Var7 is of type U8

u8Value is a bit special because it can represent very common ASCII characters, which rust specifically provides forByte literal:

let a = b'A';    // U8 represents the character a, that is, 65

Escape characters can also be used for byte literals:

let b = b'\\';    // U8 represents the character \, that is, 92
let c = b'\n';    // U8 represents a newline character, that is, 10
let d = b'\t';  // U8 represents the tab, that is, 9

Different integer types can be usedasOperator for type conversion. For example:

assert_ eq!( 10_ i8 as u16, 10_ u16);    // When 0 is high, it is filled in from low to high.
assert_ eq!( 10_ i8 as u16, 10_ u16);    // When 0 is high, it is filled in from low to high.
assert_ eq!(- 1i16 as i32, -1_ i32);    // For negative numbers, fill 1 in the high position from small to large, which is correct.

assert_ eq!( 1000_ i16 as u8, 232_ u8);        // Type truncation occurs and high-order data is discarded.
assert_ eq!( 65535_ u32 as i16, -1_ i16);    // Type truncation occurs and high-order data is discarded.

The operation of rust integer type is different from that of other languages. In the arithmetic operation of integers, one headache is “overflow”. In C language, unsigned arithmetic operation will never overflow. If it exceeds the representation range, the high-order data will be discarded automatically; For signed arithmetic operations, the behavior after overflow is undefined.

Undefined behavior helps the compiler make some radical performance optimizations, but it may produce strange bugs in extreme cases. Rust’s design idea is more inclined to prevent bugs than unconditionally press efficiency. Therefore, rust’s approach to this issue is:By default, when compiling in debug mode, the compiler will add additional code to check for overflow. Once overflow occurs, it will be thrownpanic; When compiling in release mode, the compiler does not check for integer overflow, but discards high bits.At the same time, the compiler also provides an independent compilation option to manually set the processing strategy in case of overflow:overflow-checks=yes/no。

If you need to more finely control the behavior of integer overflow, you can call thecheck_*、saturating_*、wrapping_*Series of functions, where:

  • checked_*The type returned by the series of functions isOption<_>, when overflow occurs, the returned value isNone。
  • saturating_*The type returned by the series of functions is an integer. In case of overflow, the returned value is the maximum or minimum value of the range that the type can represent.
  • wrapping_*A series of functions are truncated directly.

When security is very important, try to use these methods to replace 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 type

Rust supports single and double precision floating-point types specified by IEEE, followingIEEE 754-2008Standard. These types include positive and negative infinity, distinguish between positive and negative zeros, and have a non numeric value:

type accuracy Range
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 use suffixesf32andf64To explicitly specify the type of literal.

Rust standard library isIEEEThe special value of a defined floating-point number defines a constant, such asINFINITY、NEG_INFINITY、NAN, andMINandMAXWait. At the same time, the standard library also defines common mathematical functions for two types of precision floating-point numbers:

fn main() {
    assert_eq!(5f32.sqrt() * 5f32.sqrt(), 5.0);
    assert_eq!(-1.01f64.floor(), -1.0);
    assert!((-1.0 / std::f32::INFINITY).is_sign_negative());
}

In addition, rust does not accept automatic type conversion from integer type to floating-point type.