• Rust 从入门到精通05数据类型


    Rust 是 静态类型statically typed)语言,也就是说在编译时就必须知道所有变量的类型。

    在 Rust 中,每一个值都属于某一个 数据类型data type),分为两大类:

    ①、标量(scalar):整型、浮点型、布尔类型、字符类型

    ②、复合(compound):元祖(tuple)、数组(array)、结构体(struct)

    1、标量scalar

    每个类型有一个单独的值。

    1.1 整型

    表示没有小数部分的数字,分为有符号(以 i 开头)和无符号(以 u 开头)整型。

    数字类型的默认类型是 i32。

    长度 有符号 无符号
    8-bit i8 u8
    16-bit i16 u16
    32-bit i32 u32
    64-bit i64 u64
    128-bit i128 u128
    arch isize usize

    每一个有符号的整型可以储存包含从 -(\(2^{n - 1}\)) 到 \(2^{n - 1}\) - 1 在内的数字,这里 n 是整型定义的长度。所以 i8 可以储存从 -\(2^7\)\(2^7\) - 1 在内的数字,也就是从 -128 到 127。无符号的变体可以储存从 0 到 \(2^{n - 1}\) 的数字,所以 u8 可以储存从 0 到 \(2^8 - 1\) 的数字,也就是从 0 到 255。

    另外,isizeusize 类型依赖运行程序的计算机架构:64 位架构上它们是 64 位的, 32 位架构上它们是 32 位的。

    1.1.1 所有数字字面量,可以在任意地方添加下划线_

    fn int_test(){
        //所有数字字面量,可以在任意地方添加下划线_
        let x : u32 = 1_2_3;
        let y = x + 1;
        //打印结果为 124
        println!("{}",y);
    }
    

    1.1.2 字面量可以跟类型后缀,表示数字具体类型

    //字面量可以跟类型后缀,表示数字具体类型
    fn int_test2(){
        let x = 123i32;
        let y = x + 1;
        //打印结果为 124
        println!("{}",y);
    }
    

    1.1.3 直接对整型字面量调用函数

    //直接对整型字面量调用函数
    fn int_test3(){
        let x : i32 = 9;
        //打印结果为 729
        println!("9 power 3 = {}",x.pow(3));
    }
    

    1.1.4 整数溢出

    Rust 对于整数溢出的处理方式如下:
    ①、默认情况下,在debug模式下编译器会自动插入整数溢出检查,一旦发生溢出,则会引发 panic;
    ②、在 release 模式下,不检查整数溢出,而是自动舍弃高位的方式。

    1.1.5 如何选择

    通常默认类型 i32 即可,它通常是最快的。

    1.2 浮点

    Rust 有两个原生的 浮点数floating-point numbers)类型,它们是带小数点的数字。是基于 IEEE 754-2008 标准的浮点类型,分别是 f32f64,分别占 32 位和 64 位。默认类型是 f64,因为在现代 CPU 中,它与 f32 速度几乎一样,不过精度更高。

    fn float_test(){
        //123.0 f32类型
        let f1 = 123.0f32;
        //0.1 f64类型
        let f2 = 0.1f64;
    }
    

    1.3 布尔类型

    布尔类型(bool)代表的是“是”和“否”的二值逻辑。它有两个值:

    true和false

    一般用在逻辑表达式中,可以执行“与”“或”“非”等运算。

    fn bool_test(){
        let x = true;
        //取反运算
        let y = !x;
    
        //逻辑与,带短路功能
        let z = x && y;
    
        //逻辑或,带短路功能
        let z = x || y;
    
    }
    

    1.4、字符类型

    字符类型由 char 表示。它可以描述任何一个符合 unicode 标准的字符值。在代码中,单个的字符字面量用单引号包围(不同于字符串用):

    1.4.1 4个字节字符

    let heart_eyed_cat = '';
    

    因为 char 类型的设计目的是描述任意一个 unicode 字符,因此它占据的内存空间不是1个字节,而是 4 个字节。

    这意味着它可以比 ASCII 表示更多内容。在 Rust 中,拼音字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的 char 值。Unicode 标量值包含从 U+0000U+D7FFU+E000U+10FFFF 在内的值。

    1.4.2 1个字节字符-u8

    let x : u8 = 1;

    对于 ASCII 字符其实只需要占据一个字节的空间,因此Rust 提供了单字节字符字面量来表示 ASCII 字符。

    注意:我们还可以通过一个字母 b 在字符或者字符串前面,代表这个字面量存储在 u8 类型数组中,这样占用空间比 char 型数组要小一些。

    let x : u8 = 1; let y : u8 = b'A';

    2、复合compound

    复合类型Compound types)可以将多个值组合成一个类型

    2.1 元祖(tuple)

    ①、由圆括号()包含一组表达式组成;

    ②、长度固定,一旦声明,其长度不会增大或缩小。

    ③、rust中可以存放不同类型的数据类型

    2.1.2 实例

    fn tuple_test1(){
        //包含两个元素:1和false
        let a = (1i32,false);
        //包含两个元素:1和元祖,元祖包含两个字符1和2
        let b = (1,("1","2"));
    }
    

    2.1.3 如果元祖只有一个元素,应该添加一个逗号,用来区分括号表达式和元祖

    //如果元祖只有一个元素,应该添加一个逗号,用来区分括号表达式和元祖
    fn tuple_test2(){
        //a 是一个元祖,只有一个元素1
        let a = (1,);
        //b 是一个括号表达式,它是 i32类型
        let b = (1);
    }
    

    2.1.4 访问元祖元素

    ①、模式匹配解构

    //元祖:模式匹配
    fn tup_test4(){
        let tup = (1,1.1,2);
        let (x,y,z) = tup;
        println!("x={},y={},z={}",x,y,z);
    }
    

    ②、数字索引

    //元祖:数字索引
    fn tup_test5(){
        let tup = (1,1.1,2);
        println!("x={},y={},z={}",tup.0,tup.1,tup.2);
    }
    

    2.2 数组(array)

    ①、由中括号[] 包含一组表达式组成;

    ②、数组中每个元素的类型必须相同(元祖tuple可以不同);

    ③、长度固定,一旦声明,其长度不会增大或缩小。

    2.2.1 实例

    有三种方式声明。

    //数组:实例
    fn array_test1(){
        //1、省略类型和长度
        let a = [1,1,1,1];
    
        //2、声明类型和长度
        let b:[i32;4] = [1,1,1,1];
    
        //3、声明初始值和长度
        let c = [1;4];
    
        println!("{}",a == b);//true
        println!("{}",a == c);//true
        println!("{}",c == b);//true
    }
    

    2.2.2 访问数组元素

    ①、通过下标访问

    初始下标是0

    //数组:访问元素
    fn array_test2(){
        let a = [1,2,3,4];
        println!("a[0]={}",a[0]);
        println!("a[1]={}",a[1]);
        println!("a[2]={}",a[2]);
    }
    

    ②、通过 get() 方法

    注意返回值是 Option

    //数组:访问元素
    fn array_test3(){
        let a = [1,2,3,4];
        let first = a.get(0);
        let last = a.get(4);
        println!("{:?}",first);//Some(1)
        println!("{:?}",last);//None
    }
    

    2.2.3 数组越界访问异常

    如果声明的数组有4个,但是访问下标大于或等于4,则会在运行时抛出异常(编译能过)。

    //数组:访问元素
    fn array_test3(){
        let a = [1,2,3,4];
        println!("a[4]={}",a[4]);
    }
    

    2.2.4 避免数组越界程序崩溃

    如果我们不确定读取数组的索引是否合法,上面通过索引的方式访问就会发生异常,导致程序奔溃。

    为了避免这种情况,我们可以使用 get(index) 的方法来获取数组中的元素,其返回值是 Option

    
    //数组:访问元素
    fn array_test3(){
        let a = [1,2,3,4];
        let first = a.get(0);
        let last = a.get(4);
        println!("{:?}",first);//Some(1)
        println!("{:?}",last);//None
    }
    

    2.3 结构体(struct)

    结构体和元祖类似,都可以把多个类型组合到一起,作为新的类型。
    结构体又可以分为三种具体类型:

    // 具名结构体
    struct Name_Struct {
        x : f32,
        y : f32,
    }
    // 元祖结构体
    struct Tuple_Struct(f32,f32);
    
    // 单元结构体
    struct Unit_Struct;
    

    2.3.1 具名结构体

    //结构体
    fn struct_test1(){
        struct Point{
            x : i32,
            y : i32,
        }
        let p = Point{x:0,y:0};
        println!("{},{}",p.x,p.y);
    }
    

    ①、每个元素之间采用逗号分开,最后一个逗号可以省略不写。
    ②、类型依旧跟在冒号后面,但是不能使用自动类型推导功能,必须显示指定。

    局部变量和结构体变量一致,可以省略掉重复的冒号初始化

    //局部变量和结构体变量一致,可以省略掉重复的冒号初始化
    fn struct_test2(){
        struct Point{
            x : i32,
            y : i32,
        }
        let x = 10;
        let y = 20;
        let p = Point{x,y};
        println!("{},{}",p.x,p.y);
    }
    

    2.3.2 元祖结构体tuple struct

    这是前面介绍的 tuple 和 struct 两种类型的混合,tuple struct 结构有名字,但是成员没有名字。

    名字加圆括号,类型有单独的名字,成员没有单独的名字。

    fn tuple_struct(){
        struct Color (
            i32,
            i32,
            i32
        );
    }
    

    访问方法

    通过下标访问:

    fn tuple_struct(){
        struct Color (
            i32,
            i32,
            i32
        );
    
        let v1 = Color(1,2,3);
        println!("{},{},{}",v1.0,v1.1,v1.2)
    }
    

    2.3.3 单元结构体

    // 单元结构体
    struct Unit_Struct;
    

    单元结构体不会占用任何内存空间。

    3、枚举 enum

    如果说 tuple、struct、tuple struct 在 Rust 中代表的是多个类型的“与”关系,那么 enurn类型在 Rust 中代表的就是多个类型的“或”关系。

    Rust 的 enurn 中的每个元素的定义语法与 struct 的定义语法类似。可以像空结构体一样,不指定它的类型;也可以像 tuple struct 一样,用圆括号加无名成员;还可以像正常结构体一样,用大括号加带名字的成员。

    fn main() {
        let x = enum_define::Int(12);
        let y = enum_define::Float(3.2);
        let z = enum_define::Move {x:1,y:2};
        let k = enum_define::Color(255,255,255);
        match x {
            enum_define::Int(i) => {
                println!("{}",i);
            },
            enum_define::Float(f) => {
                println!("{}",f);
            },
            enum_define::Move{x,y} => {
                println!("{} {}",x,y);
            },
            enum_define::Color(x,y,z) => {
                println!("{}{}{}",x,y,z);
            }
        }
    }
    
    enum enum_define{
        Int(i32),
        Float(f32),
        Move{x:i32,y:i32},
        Color(i32,i32,i32),
    }
    

    4、特殊数据类型

    4.1 Never 类型

    表示不可能返回值的数据类型。

    ①、类型理论中,叫做底类型,底类型不包含任何值,但它可以合一到任何其它类型;

    ②、Never 类型用感叹号“!" 表示;

    ③、目前还未稳定,但是rust内部已经开始用了。

    5、常见错误

    5.1 类型转换必须通过 as 关键字显式声明

    //类型转换必须通过 as 关键字显式声明
    fn switch_test(){
        let var1 : i8 = 1;
        let var2 : i32 = var1;
    }
    

    报错如下:

    增加 as 关键字显示声明即可。

    //类型转换必须通过 as 关键字显式声明
    fn switch_test(){
        let var1 : i8 = 1;
        let var2 : i32 = var1 as i32;
    }
    

    5.2 复合数据类型允许递归,但是不允许直接嵌套

    //复合数据类型不允许直接嵌套
    fn recursive(){
        struct recur {
            data : i32,
            rec : recur
        }
    }
    

    报错如下:

  • 相关阅读:
    [zoj3627]模拟吧
    [zoj3623]背包模型
    [hdu4358]树状数组
    [hdu1272]并查集
    [hdu3308]线段树
    [hdu5033]单调队列
    [hdu1506]单调队列(栈)
    [hdu2888]二维RMQ
    [hdu4123]dfs区间化+RMQ
    [hdu1242]优先队列
  • 原文地址:https://www.cnblogs.com/ysocean/p/16589811.html
Copyright © 2020-2023  润新知