概述
本文已经预设你是一个JS老手了。 我们先来比较一下JS和Rust的区别。
编译型语言和解释型语言的差异
我们都知道cpu是无法直接执行高级语言的,为了能让高级语言执行起来,就需要一个翻译官,将其转换为cpu能识别的机器码。 翻译就有三种方式: 1.先把所有代码翻译好,然后将机器码交给cpu执行,这种被称为编译型语言,它在执行之前,需要经过编译器进行编译。比如C/C++。 2.代码被放到一个解释器中执行,解释器在执行代码的过程中,即时的将代码转换为机器码,这种被称为解释型代码,它是边执行边转换的。比如JS和Python。 3.第三种,则是结合了编译和解释的方式。先使用编译方式进行生成中间码,然后在执行时使用运行时或者虚拟机解释中间码。比如Java和C#
这三种方式各有优缺点,这里就不一一展开来说了。 rust属于编译型语言,它的使用方式跟JS大为不同。 编译型语言是没有运行时的,相反的JS的运行时就是V8,Java的是JVM,C#的是CLR。这些虚拟机能帮开发者做很多事情,其中一项就是内存管理。 所谓内存的管理,主要就是内存的分配和回收。 在JS中,不用关心内存,你也可能写出流畅的代码,即使可能带来一点内存泄漏,但是也不太容易造成程序崩溃。 rust就不一样了,你需要非常关心内存。一定意义上来讲,rust让你更加贴近程序的本质:计算机里一切皆内存。
从开发体验上来讲,JS最大的特点是灵活,而rust对应的就是僵硬。当从JS切换到TS时,可能很多人已经会有这样的体验了,但是rust会比TS更加僵硬,尤其是在数据类型上。这种僵硬是编译型语言所必须的---编译是在代码执行前进行的,编译器需要保证一切数据结构是确定的,并且是稳定的,不会在运行时被更改。 Rust语言的设计精髓之一就是:确定性。
看一个demo
先看一个官方的demo,体会一下跟JS有什么不同。 注意,代码是需要用命令行或者终端执行启动命令的。 这段代码的作用是:
- 生成一个随机数让用户猜测
- 然后监听命令行,看用户在命令行输入了什么数字
- 将用户输入的数字与随机数对比,看是否相等
- 打印对比的结果
use std::io; //use是包引用,它的含义等同于 import {io} from 'std'。'std'是标准库,无需额外安装。
use rand::Rng; //同上一行,这个包用来生成随机数
fn main() {//这里有一个main函数,与JS不同。rust程序必须有且只有一个main函数,它是程序的入口。运行程序后执行的第一个函数,一定是main函数。
println!("Guess the number!");//等同于console.log
let secret_number = rand::thread_rng().gen_range(1..101);// 这里生成了一个1~100的随机数
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();//声明了一个String数据,注意有个mut。我们后面讲它的含义
io::stdin().read_line(&mut guess)//监听用户的输入,并将输入值赋值给guss变量。注意&mut,我们后面讲它的含义。
.expect("Failed to read line");//异常处理
let guess: u32 = guess.trim().parse()//使用了将guess的字符串值转变成数字,并赋值给一个新的guess变量,但是数据类型是u32的
.expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) { //对数据进行对比
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
这个程序里,有一些跟JS很像,即使不了解rust语法,也能猜出来个大概。但是有一些语法就看不懂了,不要急,我们慢慢来。 先看一下rust的基础类型:
类型
JS中的数据类型分为原始类型和object两个大类。 Rust类似的,也分了两类,基础类型和复合类型。 基础类型对应原始类型,复合对应object类型。
基础类型
基础类型包含整数、浮点数、布尔类型和字符类型。
整数/浮点数
在JS中只有number类型,不区分整数和浮点数。 但是Rust的是区分的,而且分的非常细。 看一个rust的声明:
let x:u8=100;
let y: f32 = 3.0;
这里面u8表示的意思是'无符号的8位整数',我们解释一下这句话:
-
无符号表示的正整数,有符号表示的是可以为负数。数字在内存中最终是以二进制表示的,符号就指的是二进制的第一位数字是否为1.
-
8表示的是位数,位数指的是内存的寻址空间。8位表示是这个整数类型,最多能容纳2的7次方-1的值,也就是0~255。这里要注意,如果给一个u8类型赋值超过255,就会报错。
-
如果使用了有符号类型,就只能表示'-128 到 127' 。体会一下区别 4.rust支持的整数类型如下: 长度 | 有符号 | 无符号 | | ------- | ------- | ------- | | 8-bit |
i8
|u8
| | 16-bit |i16
|u16
| | 32-bit |i32
|u32
| | 64-bit |i64
|u64
| | 128-bit |i128
|u128
| | arch |isize
|usize
| -
浮点数简单一点,只有f32和f64。f32是单精度浮点数,f64则是双精度浮点数。
-
数值类型的使用,加减乘除取余则跟JS一致。
布尔类型
与JS一致。
字符类型
这里要注意,JS中有string类型,但是没有字符类型的。字符类型的关键字是'char'。 内存对char和string的处理方式是不同的。 rust声明字符是这样的:
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
注意:1. 它是单引号的。'z'和"z"虽然都只有一个z,但是前者是char,后者是string。是两种不同的数据类型
2. char只能是单个字符,'ws'这种样式的赋值是无效的
3. char表示的是Unicode标量值,它能表示中文,拼音,韩文,emoji等不同类型的内容
函数
函数的写法类似TS,比较易懂。
fn add(i: i32, j: i32) -> i32 {
i + j
}
这段代码声明了一个函数,对于函数的参数和返回值都需要明确标识类型。
但是我们发现,这个函数没有return;
实际上rust引入了一个表达式的概念,它的作用等同于 retrun i+j;
我们比较一下
return i+j; //正常的return 语句,有;
i+j //表达式,没有;
表达式可以搭配作用域,实现一种奇特的效果:
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
匿名函数
rust中对匿名函数有另外的叫法,叫闭包。它与js的箭头函数异曲同工:
// js箭头函数
const a =1;
const add =()=>{
return a+1;
}
//rust匿名函数
let x = 3;
let add = |a| a + x ; //注意看语法,使用|a|达到了js中(a)=>的效果
let res =add(1)
综述
以上就是rust中比较常见的基础类型了,看起来并不算难。 下一章,我们会讨论rust中的一个复杂的概念“可变和不可变”