提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
Rust 不是传统的面向对象编程语言,它的所有特性,使其独一无二。因此,学习特定于Rust的设计模式是必要的。本系列文章为作者学习《Rust设计模式》的学习笔记以及自己的见解。因此,本系列文章的结构也与此书的结构相同(后续可能会调成结构),基本上分为三个部分
- 习惯语法
- 设计模式
- 反模式(anti_patterns)
习惯语法是社区广泛认同的Rust编程常用风格、指导方针和模式。编写惯用的代码可以让其他开发人员更好地理解你所写的代码。
本期文章是该系列文章的开篇内容,从Rust的惯用程序设计手法来入门学习,与设计模式的书所对应,即本期主题为
使用借用类型作为参数
。格式化字符串
构造函数
一、使用借用类型作为参数
使用借用类型作为参数可以避免函数传参过程中的间接层,就比如String有一个间接层,&String就有两个间接层,我们可以通过使用&str来避免这种情况,并让&String在调用函数时强制转换为&str。
在本例中,我们使用&String来作为函数参数与&str进行对比。
这种情况在
&Vec<T>
与&[T]
或者&Box<T>
与&T
上也适用。
借用书中的例子,比如说我们想要判断一个单词中是否有三个元音,我们使用&String作为参数传入,代码就应该写成下面这样
元音:a e i o u
fn three_vowels(word: &String) -> bool { let mut vowel_count = 0; for c in word.chars() { match c { 'a' | 'e' | 'i' | 'o' | 'u' => { vowel_count += 1; if vowel_count >= 3 { return true } } _ => vowel_count = 0 } } false } fn main() { let ferris = "Ferris".to_string(); let curious = "Curious".to_string(); println!("{}: {}", ferris, three_vowels(&ferris)); println!("{}: {}", curious, three_vowels(&curious)); // 上面这么写是没有问题的,但是如果写成下面注释里面的样子,就会报错 // println!("Ferris: {}", three_vowels("Ferris")); // println!("Curious: {}", three_vowels("Curious")); }
这种情况在C++里面也是十分常见的,因为函数签名里面传入的参数类型应该是&String,如果直接传入"Ferris",则是&str,它在传入函数中的时候,不会强制转化为&String,这点学习C++的朋友一定特别熟悉,如果要使之可以那样传参,那么函数签名就得这么改
fn three_vowels(word: &str) -> bool {
这样不论我们传的是哪种参数,就都会输出正常的结果
Ferris: false Curious: true
现在我们尝试这样一个例子,给出一个句子,判断其中每一个单词,是否有三个联系的元音字母,为此,我们把代码改成如下的样子
fn three_vowels(word: &str) -> bool { let mut vowel_count = 0; for c in word.chars() { match c { 'a' | 'e' | 'i' | 'o' | 'u' => { vowel_count += 1; if vowel_count >= 3 { return true } } _ => vowel_count = 0 } } false } fn main() { let sentence_string = "Once upon a time, there was a friendly curious crab named Ferris".to_string(); for word in sentence_string.split(' ') { if three_vowels(word) { println!("{} 有三个连续的元音!", word); } } }
可以看到,three_vowels函数我们并未改变,而是在调用函数的地方,使用split方法,将字符串分割为多个单词,依次传入函数,此时就体现出&str的好处了,split返回的数组中的元素都是&str类型的。而String类型转&str就很简便。
二、格式化拼接字符串
一般来说,我们在开发的时候,做字符串拼接会使用String方法的push和push_str方法,或者直接就用+来实现字符串的拼接,但是有时候使用format!可能会更加的方便,特别是混合了各种类型的东西的字符串。
首先我们手动的构建一个字符串
let mut hello = "Hello ".to_owned(); hello.push_str(name); hello.push('!');
这是相当常见的一种用法,现在我们使用格式化的方式来构建这个字符串
fn say_hello(name: &str) -> String { format!("Hello {}!", name) }
format!返回一个格式化的字符串,{}作为占位符,然后后面传入参数,就可以返回处理好的字符串了,在这么一个简单的地方难以看出他的好处,但是当插入的东西比较多的时候就知道格式化字符串的好处了。
这一块在python和js中都有类似的东西,我觉得还是python和js里面的好用在js里面只需要
let hello = `hello ${name}`
在Rust中,使用format!来格式化拼接字符串是最简洁、可读性最好的方法。
三、使用构造函数
在Rust中实际上是没有构造函数这个概念的,但是可以使用一个约定俗成的关联函数new来创建一个对象。就比如
pub struct Second { value: u64 } impl Second { // 构建一个Second实例 // 注意,这是个关联函数,参数里面没有self pub fn new(value: u64) -> Self { Self { value } } /// 返回秒数,即Second的value pub fn value(&self) -> u64 { self.value } }
我们创建了个Second 结构体,并且实现了new方法来构建一个新的对象。
在使用的时候,我们调用new方法就可以创建一个对象了
let s = Second::new(42);
Rust中还可以实现Default特质来实现默认的构造方法
impl Default for Second { fn default() -> Self { Self { value: 0 } } }
使用derive派生Default也能起到同样的效果,比如你可以这么写
#[derive(Default)] pub struct Second { value: u64 } impl Second { pub fn value(&self) -> u64 { self.value } }
此时我们不用new方法了,可以直接调用default方法来构建实例
let s = Second::default();
注意:
这里更推荐你使用new来创建构造方法,在社区的开发中,通常会使用new来实现构造方法,因为它比起default更加合理,也更加符合阅读习惯,他的功能的default是一样的。
总结
本节内容为Rust设计模式的第一期,主要为大家引入了Rust设计模式的一些惯用语法,学习了
- 使用借用类型作为函数参数
- 格式化拼接字符串
- 构造函数
相信通过本系列文章,你可以对Rust有更加深入的理解。
本人创建了一起学Rust
社区,欢迎各位对rust感兴趣的朋友加入。
http://t.csdn.cn/AsEZ9