Rust那些容易混淆概念和表达式-基础篇

227 阅读3分钟

1. 字符串

String、&str、&String、Box<str> 或 Box<&str>这些能区分吗?

1.1 String

String:这是一个动态的,可增长的字符串类型。它被分配在堆上并且可以修改。常用于需要创建或修改字符串的场合

let mut s = String::new();
s.push_str("apache mxsm");
println!("{}", s);

1.2 &str

&str 是一个不可变的字符串 slice,它存储在栈上。常用于固定的、不需要修改的字符串。

let s: &str = "apache mxsm";
let ss: &'static str = "apache mxsm";
const SSS: &str = "apache mxsm";

1.3 &String

&String:这是一个指向 String 的引用,常用于函数参数,以便可以接受 String 类型也可以接受 &str 类型。

fn main() {
    mxsm(&"apache mxsm".to_string(),"apache")
}
fn mxsm(name:&String, address:&str){

}

1.4 Box<str>和Box<&str>

Box<str>和Box<&str>不常用

let s: Box<str> = Box::from("apache mxsm");
let ss: Box<&str> = Box::from("apache mxsm");

2.fn,Fn,FnMut,FnOnce

首先我们了解一下Rust的闭包:Rust 的 闭包(closures)是可以保存在一个变量中或作为参数传递给其他函数的匿名函数。

  • 将函数或者说代码和其环境一起存储的一种数据结构
  • 闭包引用的上下文中的自由变量,会被捕获到闭包的结构中,成为闭包类型的一部分

2.1 fn函数指针

fn 被称为 函数指针function pointer)。通过函数指针允许我们使用函数作为另一个函数的参数。

fn add_one(x: i32) -> i32 {
    x + 1
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}
fn main() {
    let answer = do_twice(add_one, 5);
    println!("The answer is: {}", answer);
}

fn不要与闭包 trait 的 Fn 相混淆。

2.2 FnOnce

FnOnce 适用于能被调用一次的闭包,所有闭包都至少实现了这个 trait,因为所有闭包都能被调用。一个会将捕获的值移出闭包体的闭包只实现 FnOnce trait,这是因为它只能被调用一次。 看一下FnOnce的源码:

pub trait FnOnce<Args: Tuple> {
    #[lang = "fn_once_output"]
    #[stable(feature = "fn_once_output", since = "1.12.0")]
    type Output;

    #[unstable(feature = "fn_traits", issue = "29625")]
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}

2.3 FnMut

  1. FnMut 适用于不会将捕获的值移出闭包体的闭包,但它可能会修改被捕获的值。这类闭包可以被调用多次。
pub trait FnMut<Args: Tuple>: FnOnce<Args> {
    /// Performs the call operation.
    #[unstable(feature = "fn_traits", issue = "29625")]
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}

从源码可以看出来FnMut继承了FnOnce

2.4 Fn

Fn 适用于既不将被捕获的值移出闭包体也不修改被捕获的值的闭包,当然也包括不从环境中捕获值的闭包。这类闭包可以被调用多次而不改变它们的环境,这在会多次并发调用闭包的场景中十分重要。

pub trait Fn<Args: Tuple>: FnMut<Args> {
    /// Performs the call operation.
    #[unstable(feature = "fn_traits", issue = "29625")]
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

通过上述的源码可以发现:

  • FnOnce参数使用的 self ,而 FnMut 使用的是 &mut self , Fn使用的是**&sel**f 这也就说明了上面说的三者的捕获值的特点
  • 三者之间是存在继承关系

3. 表达式是否带分号(;)

首先我们需要有一个最基本的认识: Rust是基于表达式的语言,几乎所有代码都可以看作是表达式。 我们看一下下面的代码

fn main() {
    4+5;
}

fn main() {
    4+5 // 编译不正确,因为main的返回值为()
}

上面包含了两段代码,代码也仅仅是有无**分号(;)**的区别,为什么第一段代码正常,第二段代码就报错了。 这里就和上面说的Rust是基于表达式的语言。表达式处理完成后就会有一个返回值,而main没有返回值或者说返回值为()。但是表达式返回值为9i32的类型所以报错。而第一段代码也看看成

fn main() {
    4+5;
    ()
}

我是蚂蚁背大象,文章对你有帮助给项目点个❤关注我GitHub:mxsm,文章有不正确的地方请您斧正,创建ISSUE提交PR~谢谢! Emal:mxsm@apache.com