Tip: After the article is written, the table of contents can be automatically generated. How to generate it can refer to the help document on the right
Article directory
foreword
Rust is not a traditional object-oriented programming language, and all of its features make it unique. Therefore, learning Rust-specific design patterns is necessary. This series of articles is the author's study notes for learning "Rust Design Patterns" and his own insights. Therefore, the structure of this series of articles is also the same as that of this book (the structure may be adjusted later), basically divided into three parts
- idiomatic grammar
- Design Patterns
- Anti-patterns (anti_patterns)
Idioms are common styles, guidelines, and patterns for programming in Rust that are widely agreed upon by the community. Writing idiomatic code allows other developers to better understand the code you write.
This article is the beginning of this series of articles, starting from Rust's customary programming techniques to get started, corresponding to the book on design patterns, that is, the theme of this issue is
使用借用类型作为参数
。格式化字符串
构造函数
1. Use borrowed types as parameters
Using a borrowed type as a parameter can avoid the indirection layer in the process of function parameter passing. For example, String has one indirection layer, &String has two indirection layers. We can avoid this situation by using &str, and let &String be called when the function Cast to &str.
In this example, we use &String as a function parameter to compare with &str.
This also applies to
&Vec<T>
with or&[T]
with .&Box<T>
&T
Borrowing an example from the book, for example, if we want to determine whether there are three vowels in a word, we use &String as a parameter to pass in, the code should be written as follows
Original sound: aeiou
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"));
}
This situation is also very common in C++, because the parameter type passed in the function signature should be &String. If "Ferris" is directly passed in, it is &str. When it is passed into the function, it will not be forced to convert to &String, friends who learn C++ must be very familiar with this point, if you want to make it possible to pass parameters like that, then the function signature has to be changed like this
fn three_vowels(word: &str) -> bool {
In this way, no matter what kind of parameter we pass, the normal result will be output
Ferris: false
Curious: true
Now we try such an example, given a sentence, to determine whether each word has three connected vowels, for this purpose, we change the code to the following
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);
}
}
}
It can be seen that we have not changed the three_vowels function, but at the place where the function is called, use the split method to split the string into multiple words, and pass them into the function in turn. At this time, the benefits of &str are reflected, and the value returned by split The elements in the array are all of type &str. It is very simple to convert String type to &str.
2. Format splicing string
Generally speaking, when we are developing, we will use the push and push_str methods of the String method for string splicing, or directly use + to achieve string splicing, but sometimes it may be more convenient to use format!, especially A string of mixed types of stuff.
First we manually construct a string
let mut hello = "Hello ".to_owned();
hello.push_str(name);
hello.push('!');
This is a fairly common usage, now we use formatting to build this string
fn say_hello(name: &str) -> String {
format!("Hello {}!", name)
}
format! returns a formatted string, {} is used as a placeholder, and then the parameters are passed in later, and the processed string can be returned. It is difficult to see its benefits in such a simple place, but when inserted When there are many things, you will know the benefits of formatting strings.
This piece has similar things in both python and js. I think it is easy to use in python and js. In js, only
let hello = `hello ${name}`
In Rust, using format! to format concatenated strings is the most concise and readable way.
Third, use the constructor
In Rust, there is actually no concept of a constructor, but a conventional associated function new can be used to create an object. like
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
}
}
We created a Second structure and implemented the new method to construct a new object.
When in use, we can create an object by calling the new method
let s = Second::new(42);
The Default trait can also be implemented in Rust to implement the default constructor
impl Default for Second {
fn default() -> Self {
Self {
value: 0 }
}
}
Using derive to derive Default can also achieve the same effect, for example, you can write
#[derive(Default)]
pub struct Second {
value: u64
}
impl Second {
pub fn value(&self) -> u64 {
self.value
}
}
At this point we don't need the new method, we can directly call the default method to build the instance
let s = Second::default();
注意:
It is recommended that you use new to create the constructor. In community development, new is usually used to implement the constructor, because it is more reasonable than default and more in line with reading habits. Its function is the same as default.
Summarize
This section is the first issue of the Rust design pattern. It mainly introduces some idiomatic syntax of the Rust design pattern.
- Use borrowed types as function arguments
- format concatenated string
- Constructor
I believe that through this series of articles, you can have a deeper understanding of Rust.
I created 一起学Rust
a community, and welcome friends who are interested in rust to join .
http://t.csdn.cn/AsEZ9