首页 > 其他分享 >Rust实战系列-Rust介绍

Rust实战系列-Rust介绍

时间:2022-08-19 23:37:27浏览次数:93  
标签:实战 cargo 系列 编译 编译器 let 内存 Rust

学习资料:rust in action[1]

1. Rust 安装

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source"$HOME/.cargo/env"


2. hello world

  • 创建 hello 项目
mkdir rust_tmp && cd rust_tmp
cargo new hello
cd hello
cargo run


图片

看到这样的输出,就表示已经成功运行了 Rust 项目,尽管还没写任何代码。接下来看看发生了什么。

  • Cargo

Cargo 是一个同时提供项目构建和软件包管理功能的工具。也就是说,Cargo 执行 rustc(Rust 编译器)将 Rust 代码转换为可执行的二进制文件或共享库。cargo new会创建一个遵循标准模板的项目,目录结构如下:

图片

Cargo.toml:描述项目的元数据信息(项目名,版本,依赖)

[package]
name = "hello"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]


src:源码目录,Rust 的源码文件扩展名为 .rs

创建好项目后,运行 cargon run 命令启动项目,这个过程完成了很多工作。

当敲下 cargon run 命令准备 run 项目时,实际上并没有可以 run 的内容,因此,cargon 会以 debug 模式编译项目,编译生成的可执行文件位于:target/debug/hello,然后执行这个文件,输出我们看到的内容:“Hello, world!”。

编译之后,项目的目录结构发生了变化,在根目录多了 Cargo.lock 文件和 target 目录,它们都是通过 cargon 进行管理的,不需要人工修改。

Cargo.lock 指定了所有依赖项的确切版本号,这样,在 Cargo.toml 被修改之前,项目编译过程都会以相同的方式进行。

3. 第一个 Rust 项目

目标:输出不同语言的 hello world,理解 Rust 的两个特性:易于迭代和原生支持 Unicode。

直接修改 hello 项目中 src/main.rs 的内容:

fn main() {
    println!("Hello, world!");

    let southern_germany = "Grüß Gott!";
    let japan = "ハロー・ワールド";
    let china = "你好,世界!";

    let regions = [southern_germany, japan, china];

    for region in regions.iter() {
        println!("{}", &region);
    }

}


  • 感叹号表示引用了一个宏

  • Rust 中的变量赋值,更恰当的称呼是变量绑定,使用 let 关键字

  • 原生支持 Unicode,不需要考虑乱码问题

  • 使用方括号表示数组

  • 很多数据类型可以通过 iter()返回迭代器

  • &表示取出地址的值

修改后,项目的执行结果:

图片

4. 文本处理

接下来,通过实例了解 Rust 的文本处理能力。主要包括以下特性:

  • 常见的控制流机制:包括 for 循环和 continue 关键字

  • 函数语法:虽然 Rust 不是面向对象的,因为它不支持继承,但它继承了面向对象语言的这个特点

  • 高级编程:函数可以同时接受和返回函数。例如,实例第 19 行包括一个闭包,也被称为匿名函数或 λ(lambda)函数

  • 类型注解:虽然用得不多,但偶尔也需要这些注解提示编译器(第 28 行)

  • 有条件地编译:编译项目时,不会编译第 22-24 行的内容

  • 隐式返回:Rust 提供了一个 return 关键字,但它通常被省略了

fn main() {                 // <1>
    let penguin_data = "\
    common name,length (cm)
    Little penguin,33
    Yellow-eyed penguin,65
    Fiordland penguin,60
    Invalid,data
    ";

    let records = penguin_data.lines();    // <2>

    for (i, record) in records.enumerate() {   // <3>
      if i == 0 || record.trim().len() == 0 {  // <4>
        continue;
      }

      let fields: Vec<_> = record     // <5>
        .split(',')                   // <6>
        .map(|field| field.trim())    // <7>
        .collect();                   // <8>

      ifcfg!(debug_assertions) {              // <9>
        eprintln!("debug: {:?} -> {:?}",
                   record, fields);            // <10>
      }

      let name = fields[0];                    // <11>
      ifletOk(length) = fields[1].parse::<f32>() { // <12>
          println!("{}, {}cm", name, length);        // <13>
      }
    }
  }


  1. 可执行项目必须有一个 main 函数

  2. 将字符串按行拆分成切片

  3. 遍历每行字符串,i 是下标,record 是 item

  4. 跳过表头和空行

  5. Vec 类型是向量的简称,向量是一种数组,在需要时可以动态扩展。下划线要求编译器推断出向量的元素类型。即变量名 fields,类型为 Vec,Vec 中元素类型 Rust 推导。

  6. 将 record 拆分成子字符串数组(以 逗号 为分隔符)

  7. 对于循环,可以使用高级函数,这里去掉空白字符。map()对 split 出来的每个子字符串应用函数 term(),field 临时变量表示每个子字符串(个人理解,不一定对)

  8. Collects 迭代的结果并保存到向量 fields 中

  9. 这个代码块是为了调试,感叹号 ! 表示宏调用,宏类似于函数,返回代码而不是值,通常用于简化常见的模式

  10. 打印到标准错误输出, {:?} 语法请求这两种数据类型的默认调试格式作为输出

  11. Rust 支持用整数下标对集合进行索引

  12. 将字符串解析为 f32(单精度浮点数)类型,parse 可以将字符串解析为任何实现了 FromStr trait 的类型(在 Rust 中,为了安全起见,不允许隐式的数据类型转换),使用 Ok()函数是为了在 if 的条件中创建 length 变量并进行赋值操作

  13. 打印到 stdout,{} 语法表示 Rust 应该使用用户自定义的方法来输出字符串的值,而不是用 {:?} 来显示调试结果

运行项目的输出结果:

图片

可以看到有输出以 debug 开头的行,通过 --release 参数去掉这部分调试内容。

图片

可以通过 -q(quiet) 来进一步减少输出信息:

图片

严格来说,rustc 才是 Rust 编译器,但我们并没有使用它来编译项目,cargon 代替我们调用 rustc ,简化编译过程。如果希望查看 rustc 编译过程的详细信息,使用 --verbose 或 -v 参数。(需要保证目标文件未被编译,如果已经编译则没有对应输出)

rustc:管理 Rust 源代码的编译

rustup:管理 Rust 的安装

5. Rust 的目标:安全

we need a safer systems programming language[2]

Rust 不受以下情况的影响:

  • 空悬指针(dangling pointers):对程序运行过程中变得无效的数据进行实时引用(指针被释放后,仍然引用原来的内存)

  • 数据竞争(data race):由于外部环境的变化,无法确定程序在运行过程中的行为(非线程安全的情况下,多线程对同一个地址空间进行写操作)

  • 缓存溢出(Buffer overflow):试图访问一个只有 6 个元素数组的第 12 个元素

  • 迭代器失效(Iterator invalidation):已经迭代的内容被中途修改后导致的问题(python 中遇到过这种问题)

当程序在调试模式下被编译时,Rust 也会对整数溢出进行保护。

什么是整数溢出:整数只能代表有限的一组数字;这些数字在内存中占用固定的长度。整数溢出是指当整数达到其极限时发生的情况。

  • 空悬指针的示例代码
#[derive(Debug)]// <1>
enum Cereal {       // <2>
    Barley, Millet, Rice,
    Rye, Spelt, Wheat,
}

fn main() {
    letmut grains: Vec<Cereal> = vec![];   // <3>
    grains.push(Cereal::Rye);               // <4>
    drop(grains);                           // <5>

    println!("{:?}", grains);               // <6>
}


  1. 允许 println! 打印 Cereal 枚举

  2. 枚举是一种有固定数量有效值的类型

  3. 初始化空的向量(数组)grains

  4. 向 grains 添加元素

  5. 删除向量 grains 和其中的内容

  6. 尝试访问被删除的值

代码中,Vec是用一个指向底层数组的内部指针实现的,尝试编译项目会出错:

图片

  • 数据竞争的示例代码
use std::thread;                          // <1>

fn main() {
    letmut data = 100;

    thread::spawn(|| { data = 500; });    // <2>
    thread::spawn(|| { data = 1000; });   // <2>

    println!("{}", data);
}


  1. 导入 thread 标准库

  2. thread::spawn() 产生一个新的线程,thread::spawn()的闭合由竖条和大括号表示(例如,|| {...})

使用 cargo 编译不会通过。

  • 缓存溢出的示例代码
fn main() {
  let fruit = vec!['

标签:实战,cargo,系列,编译,编译器,let,内存,Rust
From: https://www.cnblogs.com/sctb/p/16606864.html

相关文章

  • 新一代分布式实时流处理引擎Flink入门实战之先导理论篇-上
    @目录概述定义为什么使用Flink应用行业和场景应用行业应用场景实时数仓演变FlinkVSSpark架构系统架构术语无界和有界数据流式分析基础分层API运行模式作业提交流程顶层抽......
  • react发布一个组件库 系列篇(二)
    前言在上篇说到,不是特殊情况,我们尽量还是把源码打包编译成es5之后再发布到npm,这样用户使用就很方便。接下来我们就还拿上个背景举例子吧~安装编译和打包组件库进入到组......
  • 万物皆可集成系列:低代码释放用友深度价值(1)—系统对接集成
    用友U8+作为中国企业最佳经营管理平台的一个基础应用服务,在企业经营管理中得到非常广泛的使用。众所周知,用友U8+提供的基础能力可以满足企业日常管理的基本问题,但由于每个......
  • RabbitMQ 入门系列:1、MQ的应用场景的选择与Rabbit安装
    1、MQ的应用场景的选择:可以参考网上的对比图: 遥想当年,MQ都是MSMQ(微软)的,现在,都不在参考图上了。对MQ的使用,根据应用场景,选择适合的MQ即可,本系列仅介绍RabbitMQ。2、......
  • react发布一个组件库 系列篇(一)
    前言经常使用别人写好的组件库,然后安装引入使用即可。比如:npminstallbeautiful-tableimportBeautifulTablefrom'beautiful-table'functionApp(){return(......
  • No module named 'setuptools_rust' 安装oss2报错
    今天在测试机器上安装oss2报错了:Nomodulenamed'setuptools_rust'经过查询后记录一下OSS2介绍官方文档:https://help.aliyun.com/document_detail/32027.html一句......
  • MQ系列3:RocketMQ 架构分析
    MQ系列1:消息中间件执行原理MQ系列2:消息中间件的技术选型1背景我们前面两篇对主流消息队列的基本构成和技术选型做了详细的分析。从本篇开始,我们会专注当下主流MQ之一的......
  • Blazor预研与实战
    背景最近一直在搞一件事,就是熟悉Blazor,后期需要将Blazor真正运用到项目内。前期做了一些调研,包括但不限于Blazor知识学习组件库生态预研与现有SPA框架做比对与WebFor......
  • pytest系列——allure命令行参数详解
    一、查看allure命令的帮助文档allure-hallure命令的语法格式allure[options][command][commandoptions]options列表Options:--help命令行帮助文档......
  • Pytest系列(2-3)-conftest详解
    什么是conftest.py可以理解成一个专门存放fixture的配置文件 实际开发场景多个测试用例文件(test_*.py)的所有用例都需要用登录功能来作为前置操作,那就不能把登录功能写......