Rust 快速入门60分① 看完这篇就能写代码了

news2024/9/23 19:28:28

Rust 一门赋予每个人构建可靠且高效软件能力的语言https://hannyang.blog.csdn.net/article/details/130467813?spm=1001.2014.3001.5502关于Rust安装等内容请参考上文链接,写完上文就在考虑写点关于Rust的入门文章,本专辑将直接从Rust基础入门内容开始讲起。标题《快速入门60分》并不指60分钟,而是希望自己写完这个专辑后,可以得个60分,也能掌握Rust60%上下的内容,请看第一章《变量与常量》:

目录

一、变量与常量

1.1 变量

1.1.1. 变量命名

1.1.2. 变量声明、赋值

1.1.3. Snake case

1.1.4. 禁止使用和避免使用

1.1.5 匿名变量

1.2 基本数据类型

1.2.1 整型

1.2.2 浮点型 

1.2.3 字符型

1.2.4 布尔型

1.3 变量的可变和不可变

1.3.1 不可变变量

1.3.2 可变变量

1.3.3 变量遮蔽

1.3.4 静态变量(全局变量)

1.4 常量

1.4.1 常量命名

1.4.2 常量用法

1.4.3 标准库常量

1.5 本章小结


一、变量与常量

1.1 变量

1.1.1. 变量命名

变量名由字母、数字或下划线组成,应该具有描述性,能够清楚地表达变量的含义。命名的基本规则和大多数编程语言基本相同,有些细节上稍微有所不同。规则如下:

  1. 变量名必须以字母或下划线开头。
  2. 变量名不能以数字开头。
  3. 变量名区分大小写,推荐使用 snake_case 规则(字母全部小写和下划线)。
  4. 禁止使用和 Rust 关键字同名的变量名。
  5. 避免使用和 Rust 标准库中已有的函数、类型或模块同名的变量名。
  6. 对于私有变量 (private variables),可以使用 _ 作为前缀,区分开公共变量和私有变量。

1.1.2. 变量声明、赋值

Rust 变量声明使用 let 关键字,有些早期版本的basic语言也用LET(现在通常是DIM)

示例:

fn main() {
    let x = 323;
    let y = 3.23;
    print!("x: {}, ", x);
    println!("y: {}", y);
    //输出 x: 323, y: 3.23
}

上面示例中,fn 是函数 function 的缩写,表示 main() 是这个rust程序的主函数;

let 变量名 = 常数;就是一个变量声明、赋值语句;

print!() 和 println!() 是打印的“宏”,宏不是函数,但功能也就相近于Java中的System.out.print()函数和System.out.println()函数的功能,两者输出差一个换行符。

// 表示注释语句,注释与C++相同,行注释用 //,块注释用 /*  */;Rust另外还有文档注释。

函数体中每一行都是一个语句(当然语句也可以跨多行表达),语句由各种表达式组成。第一条语句必须有标点符号分号作结尾,表达式一般没有符号作结尾的。关于Rust中的“宏”,它和C/C++中的“宏”还是不同的,更多函数相关内容,放到之后的函数章节再讲。

let 语句也可以分成两行,先声明再分配值(赋值绑定):

fn main() {
    let x;
    x = 100;
    println!("x: {}", x); 
}

let 语句还可以一行赋值多个变量,但要加上括号(其实是复合数据类型之一的元组):

fn main() {
    let (x, y) = (3, 4);
    let z = (x*x+y*y) as f64;
    let z = z.sqrt().round() as i32;
    println!("{},{},{}", x, y, z); // 3,4,5
    let (a, b, c) = (1, 2, 3);
    println!("{},{},{}", a, b, c); // 1,2,3
}

其中,as 也是Rust关键字之一,在这里用于强制转换数据类型。 

sqrt()、round() 分别为平方根、取整函数。

1.1.3. Snake case

Snake case 是一种命名规范,它使用小写字母,单词之间用下划线 "_" 连接。

Rust不推荐在变量中有大写字母,示例如下:

fn main() {
    let Int = 100;
    let My_Integer = 200;
    let my_integer = 300;
    
    println!("{} {} {}", Int, My_Integer, my_integer)
}

以上代码可以编译执行,但会有警告出现:

···Rust
warning: variable `Int` should have a snake case name
 --> D:\Rust\hello.rs:2:9
   |
2 |     let Int = 100;
   |         ^^^ help: convert the identifier to snake case (notice the capitalization): `int`
   |
  = note: `#[warn(non_snake_case)]` on by default

warning: variable `My_Integer` should have a snake case name
 --> D:\Rust\hello.rs:3:9
   |
3 |     let My_Integer = 200;
   |         ^^^^^^^^^^ help: convert the identifier to snake case: `my_integer`

warning: 2 warnings emitted

100 200 300
···

1.1.4. 禁止使用和避免使用

变量命名使用关键字,报错通不过编译,所以是禁止使用;但与标准库中已有的函数、类型同名,只是容易混淆,但编译不警告不报错,所以只能说是避免使用。如:

fn main() {
    //let fn = 10; //禁止使用
    //let let = 2; //禁止使用
    let u8 = 10u8; //避免使用
    let pow;       //避免使用
    pow = u8.pow(2);
    println!("{} {}", u8, pow)  //输出 10 100
}

其中, u8 变量类型8位无符号整型pow() 是标准库函数平方幂函数。

函数名、类型名称作变量名不会报错,而fn, let关键字作变量名则报错:

```Rust
expected identifier, found keyword
  |
help: escape `fn` to use it as an identifier 

```

1.1.5 匿名变量

下划线 _ 是一个特殊的变量名,更确切地说是变量名称的缺失,就称它为匿名变量。它的基本意思是舍弃,可以理解为废纸篓,这个变量扔掉不要了,不能被再次调用。(Go, Python里也有  _ 用作匿名变量,但细节各不相同,而且在python里 “_ = 5;print(_)” 是合法的

fn main() {
    let (a,_) = (6,2);
    println!("{}", a);
    //println!("{}", _); //报错^ `_` not allowed here
}

1.2 基本数据类型

1.2.1 整型

Rust整型 分为有符号整型(signed,标记 in)和无符号整型(unsigned,标记 un),区别在于数字是否有负数。带符号整型的安全存储范围为 -2^(n-1) 到 2^(n-1) - 1,无符号整型的安全存储范围为 0 到 2^n,n 为数据位长度,见下表:

位长度有符号无符号
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
arch
由系统构架而定
isizeusize

isize 和 usize 是根据系统架构决定的,例如带符号整型,如果系统是 64 位,类型为 i64,如果系统是 32 位,类型为 i32。(这和C++中的size_t类型相似

指定类型和默认类型

变量声明时,可以先指定类型,再分配绑定数值,变量名后面使用冒号跟随类型来明确指定变量的类型,称为显式指定;Rust 是强类型语言,具有自动判断变量类型的能力,可理解为这是隐式指定。以下示例中声明的变量 z 并没有明确指定其具体类型,则默认为 i32 类型,这也是 Rust 的特性之一类型推断

fn main() {
    let x: u8;
    x = 123;
    let y = -1000i64;
    let z = -323;  //不指定类型,默认为i32
    println!("x: {}, y: {}, z: {}", x, y, z);
}

1.2.2 浮点型 

Rust浮点型 分为 32 位浮点数(f32)和 64 位浮点数(f64)。浮点型若不指定具体类型,则默认为 f64 浮点数,现在的高性能CPU对两种浮点数计算的速度几乎相同,但 64 位浮点数精度更高。

fn main() {
    let x: f32;
    x = 1.23;
    let y: f64 = 3.23;
    let z = -3.23;  //不指定类型,默认为f64
    println!("x: {}, y: {}, z: {}", x, y, z);
}

1.2.3 字符型

Rust字符型 是一个4字节 Unicode 码,支持中文等非英文字符,使用单引号''包括。

fn main() {
    let a = 'a';
    let b: char = '字';
    let c = '😊';
    println!("a: {}, b: {}, c: {}", a, b, c); 
}

1.2.4 布尔型

Rust布尔型 用 bool 表示,占用1个字节,值只能为 true 或 false,全小写非大写字母开头。(输出与声明同形,不像C/C++只能输出1和0

fn main() {
    let x = true;
    let y: bool = false;
    println!("x: {}, y: {}", x, y); //输出 x: true, y: false
}

1.3 变量的可变和不可变

1.3.1 不可变变量

先看一个实例:

fn main() {
    let x = 6;
    x = x + 1;  // 报错
    println!("{}", x)
}

在大多数编程语言中,上述实例应该打印出 7,但在Rust语言里却是报错: 

```Rust
error[E0384]: cannot assign twice to immutable variable `x`
 --> D:\Rust\hello.rs:3:5
   |
2 |     let x = 6;
   |         -
   |         |
   |         first assignment to `x`
   |         help: consider making this binding mutable: `mut x`
3 |     x = x + 1;
   |     ^^^^^^^^^ cannot assign twice to immutable variable
```

意思就是: 不能为不可变变量`x赋值两次`,并提示考虑绑定可变:`mut x`

特别之处: Rust变量是不可变的!

通常来说变量将是可变的,但是在 Rust 中预设变量却是不可变的。Rust 鼓励使用不可变变量,当然如果明确知道该变量是可变得,就得用关键字 mut 附加说明它是可变的变量。

1.3.2 可变变量

可变变量要用 let mut 两个关键字来声明,示例如下:

fn main() {
    let mut x = 6;  // 可变变量声明要在let后附加mut关键字
    x = x + 1;
    println!("{}", x)  // 输出 7
}

1.3.3 变量遮蔽

Rust可以定义一个与之前变量同名的新变量,称之为第一个变量被第二个 Shadowing 了,官方用的这个词 Shadowing,有时被翻译成隐藏有时被翻译成遮蔽,还有的翻译为重影。它的意思就是当使用相同变量的名称时,编译器将“看到”第二个变量。实际上,第二个变量 “遮蔽” 了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 let 关键字来多次隐藏。总之,它和可变变量不是同一个概念。

fn main() {
    let x = 6;
    let x = x + 1;
    println!("{}", x);  // 7
    let x = x * x;
    println!("{}", x);  // 49
}

1.3.4 静态变量(全局变量)

静态变量用 static 声明的变量的生命周期是整个程序,从启动到退出。这也是Rust中唯一的声明全局变量的方法。它的生命周期永远是'static, 它占用的内存空间也不会在执行过程中回收。 

注意点:

1. 全局变量必须在声明的时候马上初始化;
2. 命名时字母要全部大写,否则编译有警告信息;
3. 全局变量的初始化必须是编译期就能确定的常量;
4. 带有 mut 修饰的全局变量,在使用的时候必须使用 unsafe 关键字;
5. 全局变量可以写到函数外面,被任意一个函数使用。

fn main() {
    static G1 : i32 = 3;
    println!("{}, {}", G1, G1 + 20);

    static mut G2 : i32 = 4;
    unsafe {
        // G1 = 2; // cannot assign to immutable static item `G1`
        G2 = G1 + 5;
        println!("{}, {}", G1, G2);
    }
}

/* 输出
3, 23
3, 8
*/

1.4 常量

常量,和变量一样都是用来存储数据的标识符,常量使用 const 关键字声明。常量的值是不可变的,不允许使用mut关键字修饰这个变量绑定。

1.4.1 常量命名

常量的命名规则和方法与静态变量基本类似,命名时字母也要全部大写

错误示例:常量字母不全部大写可以通过编译,但有警告信息。

const pi:f32 = 3.14159; //报错
const Pi:f32 = 3.14159; //报错

fn main() {
    println!("{}", pi);
    println!("{}", Pi);
}

```
warning: constant `pi` should have an upper case name
 --> D:\Cpp\hello.rs:1:7
   |
1 | const pi:f32 = 3.14159;
   |       ^^ help: convert the identifier to upper case: `PI`
   |
  = note: `#[warn(non_upper_case_globals)]` on by default

warning: constant `Pi` should have an upper case name
 --> D:\Cpp\hello.rs:2:7
   |
2 | const Pi:f32 = 3.14159;
   |       ^^ help: convert the identifier to upper case (notice the capitalization): `PI`

warning: 2 warnings emitted

3.14159
3.14159

```

1.4.2 常量用法

常量必须显式指定类型以及声明同时就赋值,并且常量的值必须是编译时可确定的常数或者是常量表达式。示例如下:

const PI: f64 = 3.14159;
const FLAG: bool = true;
const HOURS: u32 = 12;
const SECONDS: u32 = 60 * 60 * HOURS;
const MIN_NUM: i32 = 1 << 31;
const MAX_NUM: i32 = -(1 + MIN_NUM);

fn main() {
    println!("{}", PI);
    println!("{}", FLAG);
    println!("{}", HOURS);
    println!("{}", SECONDS);
    println!("{}", MIN_NUM);
    println!("{}", MAX_NUM);
}

/* 输出:
3.14159
true
12
43200
-2147483648
2147483647
*/

错误示例:常量不可以隐式赋值,或者先声明后赋值。

const PI = 3.14159; //报错
const FLAG:bool;  //报错

fn main() {
    FLAG = true;  //报错
    println!("{}", PI);
    println!("{}", FLAG);
}

```
error: free constant item without body
 --> D:\Cpp\hello.rs:2:1
   |
2 | const FLAG:bool;
   | ^^^^^^^^^^^^^^^-
   |                |
   |                help: provide a definition for the constant: `= <expr>;`

error: missing type for `const` item
 --> D:\Cpp\hello.rs:1:9
   |
1 | const PI = 3.14159;
   |         ^ help: provide a type for the constant: `: f64`

error[E0070]: invalid left-hand side of assignment
 --> D:\Cpp\hello.rs:5:10
   |
5 |     FLAG = true;
   |     ---- ^
   |     |
   |     cannot assign to this expression

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0070`. 

``` 

1.4.3 标准库常量

引用标准库中的常量,使用关键字 use 来导入。(类似 java和python 中 import

比如std::f32或std::f64中的一些数学常量:

use std::f32::consts::PI;
use std::f32::consts::FRAC_1_PI;
use std::f32::consts::FRAC_2_PI;
use std::f32::consts::SQRT_2;
use std::f32::consts::FRAC_1_SQRT_2;
use std::f32::consts::E;
use std::f32::consts::LN_2;
use std::f32::consts::LOG2_E;
use std::f32::consts::LOG10_E;
fn main() {
    println!("{}", PI); 
    println!("{}", FRAC_1_PI);
    println!("{}", FRAC_2_PI);
    println!("{}", SQRT_2);
    println!("{}", FRAC_1_SQRT_2);
    println!("{}", E);
    println!("{}", LN_2);
    println!("{}", LOG2_E);
    println!("{}", LOG10_E);
}

/* 输出
3.1415927
0.31830987
0.63661975
1.4142135
0.70710677
2.7182817
0.6931472
1.442695
0.4342945
改std::f64,则输出
3.141592653589793
0.3183098861837907
0.6366197723675814
1.4142135623730951
0.7071067811865476
2.718281828459045
0.6931471805599453
1.4426950408889634
0.4342944819032518
*/

1.5 本章小结

1. 变量(Variables):在 Rust 中,变量默认是不可变的(immutable),也就是说,一旦被赋值后,就不能再修改其值。如果需要修改变量的值,需要使用 `mut` 关键字来声明可变变量。

2. 常量(Constants):与变量不同,常量在声明后就不能再修改其值。在 Rust 中,使用 `const` 关键字来声明常量,常量的命名规则与变量相同,但必须使用大写字母命名,并且必须在声明时就赋值。

3. 可变绑定(Mutable bindings):除了使用 `mut` 关键字来声明可变变量外,还可以使用 `let` 关键字来声明一个可变绑定。可变绑定允许我们在不改变绑定本身的情况下修改绑定所指向的值。

4. 类型推断(Type inference):Rust 支持类型推断,也就是说,可以在声明变量或常量时省略类型,由编译器自动推断类型。例如,可以使用 `let x = 42;` 来声明一个整数变量,编译器会自动推断出 x 的类型为 i32。

5. 变量遮蔽(Variable shadowing):Rust 中可以使用相同的名称来声明一个新的变量或常量,这会遮蔽之前的变量或常量。这个特性可以用来在不改变原有代码的情况下修改变量的值或类型。

6. 变量的作用域由声明的位置开始,直到当前作用域的结束;变量在离开作用域后会被自动销毁。常量在整个程序运行期间都存在。

注:本文中所有代码都可以在Rust官方线上编译器里编译通过。

Rust PlaygroundA browser interface to the Rust compiler to experiment with the languageicon-default.png?t=N3I4https://play.rust-lang.org/?version=stable&mode=debug&edition=2021作者水平有限,如有不当之处,敬请指正。


本章完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/510591.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何预测药品市场规模

药品市场规模预测是一个非常关键的步骤&#xff0c;可以帮助判断该项目是否值得投资或开发。以下是一些常见的方法&#xff1a; 药品市场规模可以细分为治疗领域市场规模、药品种类市场规模、区域市场规模、渠道市场规模、品牌市场规模、性质市场规模等。这些规模的了解是一个非…

【Hello Algorithm】异或法

作者&#xff1a;小萌新 专栏&#xff1a;算法 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;介绍算法中的异或法 异或法 异或的概念异或的两个性质题目一 不使用额外变量交换两个数字题目二 出现奇数次的数字题目三 如何从一个整型数字中提取出…

石油化工行业室内外高精度人员定位系统解决方案

石油化工行业是高危行业&#xff0c;很容易发生安全事故&#xff0c;对于石化企业来说&#xff0c;加强人员的安全管控非常有必要。我们可以通过人员定位技术&#xff0c;提升石化企业安全管理水平。下面给大家分享石油化工行业室内外高精度人员定位系统解决方案。 方案概述 石…

BERT原理Fine TuningBert变种

文章目录 BERT原理训练时的任务任务一任务二任务二的改进 模型的输入 BERT - Fine Tuning单个句子的预测类序列标注类Q&A类seq2seq&#xff1f; BERT 变种Transformer-XLXLNetAutoregressive Language ModelDenoising Auto-Encoder乱序Two-Stream Attention与Transformer-X…

RocketMQ双主双从环境搭建

环境要求 64位操作系统&#xff0c;推荐 Linux/Unix/macOS 64位 JDK 1.8 服务器准备 准备4台服务器两台master两台slave&#xff0c;如果服务器紧凑&#xff0c;则至少需要两台服务器相互master-slave IP HOSTS 172.*******.120 rocketmq-nameserver1 rocketmq-master1 …

ElasticSearch小计

1、ElasticSearch简介 1.1、ElasticSearch&#xff08;简称ES&#xff09; Elasticsearch是用Java开发并且是当前最流行的开源的企业级搜索引擎。能够达到近实时搜索&#xff0c;稳定&#xff0c;可靠&#xff0c;快速&#xff0c;安装使用方便。客户端支持Java、.NET&#x…

Class 00 - 学习编程的方法不同职业所使用的编程语言

Class 00 - 学习编程的方法&不同职业所使用的编程语言 学习编程的方法什么是编程&#xff1f;不同职业所使用的编程语言数据分析网页设计移动应用开发Web应用开发游戏开发 Tips&#xff1a;学习编程语言的技巧 从电子表格到 SQL 再到 R电子表格、SQL和R:一个比较 学习编程的…

根据端口查询该程序占用的内存 gpu

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、如何根据端口号查询该程序的占用内存…

JavaScript语法基础

js学习路线 数据判度 1&#xff0c;类型分类undefined,Null,Number,String,Boolean 2,类型判断typeof操作符 var sTemp “tesst” 例如alert(typeos sTemp); //输出String alert(typeof 23);//输出number 3&#xff0c;instanceof操作符&#xff1a;用于判断一个引用类型属于…

【C++】C++中的继承

目录 一.继承的概念和定义1.继承的概念2.继承定义2.1定义格式2.2继承关系和访问限定符2.3继承基类成员访问方式的变化 二.基类和派生类对象赋值转换三.继承中的作用域四.派生类的默认成员函数五.继承和友元六.继承与静态成员七.复杂的菱形继承及菱形虚拟继承1.单继承2.多继承3.…

React 中 TypeScript 和装饰器及 Hooks

概念 TypeScript 是强类型语言&#xff0c;相对于JavaScript 弱类型语言&#xff0c;它具有类型检测的功能&#xff0c;扩展了JavaScript 的语法。 TS的安装与执行&#xff1a; //全局安装typescript npm install typescript -g// 第二个因为 本来的node是不可能支持 ts那种民…

2023网络安全学习路线 非常详细 推荐学习

前言&#xff1a;首先咱们聊聊&#xff0c;学习网络安全方向通常会有哪些问题 目录&#xff1a; 1、打基础时间太长 学基础花费很长时间&#xff0c;光语言都有几门&#xff0c;有些人会倒在学习 linux 系统及命令的路上&#xff0c;更多的人会倒在学习语言上&#xff1b; …

SSD系列1——网络结构

SSD系列&#xff1a; SSD系列1——网络结构 SSD系列2——PriorBox SSD系列3——损失计算 SSD网络结构概述 SSD在VGGNet的基础上&#xff0c;增加了4个卷积模块&#xff0c;这些卷积模块获得的特征图具有不同的感受野&#xff0c;可以较好地检测不同尺度的目标。 VGG16 SSD网络…

springboot 断点上传、续传、秒传实现

文章目录 前言一、实现思路二、数据库表对象二、业务入参对象三、本地上传实现三、minio上传实现总结 前言 springboot 断点上传、续传、秒传实现。 保存方式提供本地上传&#xff08;单机&#xff09;和minio上传&#xff08;可集群&#xff09; 本文主要是后端实现方案&…

AI绘画:Lora模型训练完整流程!

关于AI绘画(基于Stable Diffusion Webui)&#xff0c;我之前已经写过三篇文章&#xff0c;分别是 软件安装&#xff0c;基本的使用方法&#xff0c;微调模型LoRA的使用。 整体来说还是比简单的&#xff0c;搞个别人的模型&#xff0c;搞个提示词就出图了。今天来一个有些难度…

推荐11个好用的prompt工具网站(附链接+论文)

同学们&#xff0c;你们prompt是自己苦哈哈码的吗&#xff1f;可别了&#xff0c;有现成的工具为啥不用&#xff1f; 今天我就和大家分享一些好用的prompt工具网站&#xff0c;用熟了ChatGPT、midjourney、stable diffusion能玩起来更爽&#xff01;搜罗了有十几个&#xff0c…

智能汽车实验二(视觉传感器标定)

实验二 视觉传感器标定&#xff08;实验报告&#xff09; 【实验目的】 1、了解开源图像处理库OpenCV的结构&#xff0c;掌握OpenCV的基本使用方法。 2、了解开源图像处理库OpenCV的基本模块功能&#xff0c;掌握常用图像处理方法。 3、掌握摄像机标定算法&#xff0c;学会使用…

Xilinx 7系列FPGA内置ADC

Xilinx 7系列FPGA全系内置了一个ADC&#xff0c;称之为XADC。这个XADC&#xff0c;内部是两个1mbps的ADC&#xff0c;可以采集模拟信号转为数字信号送给FPGA内部使用。 XADC内部可以直接获取芯片结温和FPGA的若干供电电压&#xff08;7系列不包括VCCO&#xff09;&#xff0c;用…

麒麟KylinV10SP1(2203)推荐安装一些硬件监控类软件与使用

目录 前言 1、tlp 电源管理 &#xff08;1&#xff09;查看电池容量、使用量、为Thinkpad设定电池充电开始结束阈值 &#xff08;2&#xff09;查看硬盘比如NVME SSD的型号种类、当前温度、读写量等信息&#xff1b; &#xff08;3&#xff09;查看CPU型号以及频率上下限、…

软件测试简单么,发展前景如何?

随着人工智能时代的到来&#xff0c;IT行业受到了越来越多人的重视。软件测试作为把控软件质量必不可少的环节&#xff0c;其重要性可见一斑。 软件测试可以说是算得上IT行业里相对简单的语言&#xff0c;但是也只是相对哈&#xff0c;如果想学习下去还是要看个人的学习能力的…