前言
上一篇,介绍了上一篇,介绍了Rust语言的安装与编译
基本数据类型
变量声明和不可变绑定
这其中已经蕴含着Rust精华的成分了。本篇内容将涉及常规函数
闭包
字符串
函数(Function)
一般而言,编程语言中的函数,将过程性指令抽象为专名的功能实体,以重复调用,简化代码。在上一篇中,我们已经看过了main函数,这里看下如何定义一个常规函数。
//function.rs
fn add(a: u64, b: u64) -> u64 {
a + b
}
fn main() {
let a: u64 = 17;
let b = 3;
let result = add(a, b);
println!("Result {}", result);
}
解释代码
从以上代码第2行可见,这个add 是一个简单的加法函数,
fn是创建函数的关键字,
参数 a,b在函数名的括号中,各自的数据类型显式的声明在冒号之后,
返回类型也得到显式声明->u64, 如果没有返回值,这一步可以不写
如第一篇所提及,函数是有类型的,这里为 fn(u64, u64) -> u64显然,函数也可以作为变量和其他函数的参数
第3行现已不需要return关键词进行函数值返回
再看一下main函数,第5-10行:第6行,声明了一个变量a,及其类型,并初始化
第7,8行,声明变量b,result,并初始化,这里没有显式声明b的类型,表明是可以省略的,因为Rust能够在大多数情况对代码中的变量类型进行正确推断,不难得知:b和result 都是u64
对类型的显式声明在Rust中是有其目的:避免类型标识上可能出现的冲突
提升代码的可读性,尤其当多种类型嵌套一起时
Rust的类型推理基于Hindly Milner类型系统,是一整套支持编程语言中类型推断的规则和算法,可在线性时间内执行,不但高效,而且在大型程序的类型检查中很为实用。
上述代码结果
我们再看一个改变函数参数的代码实例。
// function_mut.rs
fn increase_by(mut val: u32, how_much: u32) {
val += how_much;
println!("You made {} points", val);
}
fn main() {
let score = 2048;
increase_by(score, 30);
}
解释代码
代码不长,大致说下:在main函数中,声明score,初始化为2048;调用increase_by函数,30为该函数的第2个参数。
需要说明一下,在increas_by中,明确了第1个参数为mut val,表明这一参数可以在该函数内部改变赋值,这显然是可变绑定在函数体中的一次体现。
上述代码结果
闭包(Closure)
到这里,谈一下Rust中也同样支持的闭包,其与函数相似,但在声明之时,附及更多的环境(environment)或范围(scope)信息;
再者,定义时,函数有名(关键字fn),而闭包无名,虽然无名,但又能够赋值
同函数一样,可以作为值存储,并被其他的函数体调用。
闭包的主体,可以单行,也可以多行
举例而言,一个简单的闭包可能形如: let my_closure = || (); ,可见这个闭包既无参数,又没有任何功能,可以直接调用my_closure(),;而||是用来装载参数的,比如|a,b|, 如果感到类型不太好推断,当然最好明确类型,如|a:u32|。
我们看一下这个代码,里面有两个闭包doubler 和big_closure
// closures.rs
fn main() {
let doubler = |x| x * 2;
let value = 5;
let twice = doubler(value);
println!("{} doubled is {}", value, twice);
let big_closure = |b, c| {
let z = b + c;
z * twice
};
let some_number = big_closure(1, 2);
println!("Result from closure: {}", some_number);
}
可见:第3行,一定不要看成是绝对值,这是一个单行闭包--doubler,参数是x,功能是返回x的2倍;
第4行,初始化变量value=5
第5行,初始化变量twice=doubler(value),同时也是调用闭包doubler
第6行,打印相关值
第8-11,第2个闭包big_closure ,是一个多行闭包,参数为b,c; 函数内定义初始化变量z,最终返回z*twice的值
第13行,初始化变量some_number,调用闭包big_closure
第14行,打印相关值
上述代码结果
有关闭包的用途,主要是作为高阶函数(higher-order function)的参数,原因在于闭包可以提供相对便利的抽象(convenient abstraction)。
相关的实例,比如:thread::spawn或者迭代器Iterator特性中的filter方法,但这些东西,在本文中尚非重点,而且需要很多前提知识才能讲清楚,会在后续有关Rust高级概念的篇章中详细介绍。
字符串(String)
字符串是编程语言中颇为常用的数据类型,在Rust中,通常有两种形式:字符串,String
字符串指针,&str,与C语言一样,&就是一个指针符号
Rust字符串是有效的UTF-8编码的字节序列;像C字符串那样以null结尾,之间可以包含null字节。
我们看一下代码实例
// strings.rs
fn main() {
let question = "How are you ?"; // a &str type
let person: String = "Bob".to_string();
let namaste = String::from("नमस्ते"); // unicodes yay!
println!("{}! {} {}", namaste, question, person);
}
依然是不难的代码,希望我们这可以这样循序渐进的学下去。第1行,初始化一个字符串指针&str类型的变量question
第2-3行,初始化String类型变量person 和namaste
上述代码结果
显然存在多种方式创建String类型。
字符串变量分配在堆(heap)上,而字符串指针类型&str,指向一个现有字符串,既可以在堆,也可在栈(stack)上。
这里依然还是一个简单介绍,关于字符串的进一步讨论会在之后篇章中继续。
结语
本文介绍了Rust中的函数,闭包和字符串,都是编程中的基本元素,请读者注意下在函数定义中有关类型方面的特点。
下一篇会介绍一下Rust中的分支与循环。
主要参考和建议读者进一步阅读的文献The Rust Programming Languagedoc.rust-lang.org
Rust编程之道,2019, 张汉东
The Complete Rust Programming Reference Guide,2019, Rahul Sharma,Vesa Kaihlavirta,Claus Matzinger
Hands-On Data Structures and Algorithms with Rust,2018,Claus Matzinger
Beginning Rust ,2018,Carlo Milanesi
Rust Cookbook,2017,Vigneshwer Dhinakaran