Take a look at Rust and take note 4

Today, I'm free to take a look at chapter 4 - functions, closures and iterators Chapter 4: functions, closures and itera...
function

Today, I'm free to take a look at chapter 4 - functions, closures and iterators

Chapter 4: functions, closures and iterators

Rust is a language that supports functional programming. Function, as a first-class citizen, is itself a type. Function types can be used as parameters or return values of other functions, can also be assigned to other variables, and can also be called and executed directly.

function

Function name: snakecase is separated by specification. All letters are lowercase and words are separated by underscores

fn add(x: i32, y: i32) -> i32 { x + y } fn main() { let x = 5; let y = { let x = 2; x + 1 }; let sum = add(x , y); println!("{} + {} = {}", x, y, sum) }

notes:

  1. The parameter of a function must specify the parameter type, and the default value of the parameter cannot be specified
  2. If the statement without semicolon is an expression, the value of the expression will be returned automatically, but the statement does not return a value
  3. Parameters are divided into variable and immutable parameters, which are immutable by default. When variable operations are required, use the mut keyword.
  4. Each function of Rust has a return value. Even functions that do not display the return value will implicitly return a cell system ()

Methods and functions

Well, I know everything

#[derive(Debug, PartialEq)] pub struct Student { name: &'static str, score: i32, } impl Student { // Inside the structure, but not with & self as the first parameter, is called a correlation function // The following is equivalent to a constructor pub fn new(name: &'static str, score: i32) -> Self { Student { name, score } } // Difference between method and function: the first parameter must be & self, indicating the reference of this instance object pub fn get_name(&self) -> &str { self.name } pub fn set_name(&mut self, name: &'static str) { self.name = name } pub fn get_score(&self) -> i32 { self.score } pub fn set_score(&mut self, score: i32) { self.score = score } } fn main() { let mut student: Student = Student::new("zhangsan", 59); println!("name : {},score : {}", student.get_name(), student.get_score()); student.set_score(60); println!("{:?}",student) }

Higher order function

High order function refers to the function with function as parameter or return value. It is the most basic feature of functional programming language.

Then there will be function pointers and functions as parameters

Function pointer
fn main() { // Here, only the function name is used, not the calling function // The specified type must be displayed let fn_ptr: fn() = hello; //Print out the pointer address and prove that it is a function pointer // Here my idea will display an error, but there is no error when running!!! println!("{:p}", fn_ptr); fn_ptr(); }
Function as parameter

You can use type to define aliases for function pointer types

type MathOp = fn(i32, i32) -> i32; fn math(op: MathOp, x: i32, y: i32) -> i32 { println!("{:p}", op); op(x, y) } fn subtract(x: i32, y: i32) -> i32 { x - y } fn main() { let (x, y) = (8, 3); println!("add operation result: {}", math(add, x, y)); println!("subtraction operation result: {}", math(subtract, x, y)); }
Function as return value

Function as the return value, you can also use the type keyword to define an alias for the function pointer type

fn math_op(op: &str) -> matpOP { match op { "add" => add, _ => subtract, } } fn main() { let (x, y) = (8, 3); let mut op = math_op("add"); println!("{}", op(x, y)) }

closure

It refers to creating an anonymous function in a function. Although there is no function name, the closure can be assigned to a variable.

Basic grammar

A closure is a combination of a pipe | | and braces {},

The parameter type of closure can be ignored,

The return value type can be specified after the pipe character, but it is not required.

If the closure body has only one line, you can ignore braces

fn main() { let add_one = |x: i32| -> i32 ; println!("{}", add_one(1)); }
Type inference

The compiler can reliably infer the types of parameters and return values.

fn main() { fn add_one_v1(x: u32) -> u32 { x + 1 } let add_one_v2 = |x: u32| -> u32{ x + 1 }; let add_one_v3 = |x| -> u32{ x + 1 }; // Although the compiler can infer parameters and return values. However, if the same closure is called multiple times but different types are passed, an error will be reported let add_one_v4 = |x| x + 1; } // The above compilation error and the following are correct. The compiler must let it specify the parameter type fn main7() { fn add_one_v1(x: u32) -> u32 { x + 1 } let add_one_v2 = |x: u32| -> u32{ x + 1 }; let add_one_v3 = |x: u32| { x + 1 }; // Although the compiler can infer parameters and return values. However, if the same closure is called multiple times but different types are passed, an error will be reported let add_one_v4 = |x:u32| x + 1; }
Capture variable

The biggest difference between closures and functions is that closures can capture and use variables in the scope in which they are defined

fn main (){ let i = 1; let add = |x| { x+ i }; println!("{}",add(7)) }

iterator

The iterator pattern abstracts the behavior of traversing the data set into a separate iterative object, so that when traversing the set, all elements in the set can be passed to the processing logic in order. Greatly simplify data operations

Iterator trait

It is an abstract interface of iterator pattern. There are two important interfaces:

  1. The iter method is used to return an iterator instance
  2. The next method returns the next element of the iterator. And encapsulate it in Some function. If you iterate to the end of the collection (after the last element), return None.
fn main() { let v = [1, 2, 3]; let mut iter = v.iter(); println!("{:?}", iter.next()); println!("{:?}", iter.next()); println!("{:?}", iter.next()); println!("{:?}", iter.next()); }
Consumer

Rust's iterators are lazy and do not automatically traverse

You can do this by consuming the elements in the iterator.

sum, any, collect, etc. std:iter::Iterator contains

sum

The consumer sum can perform a sum operation on the elements of the iterator.

fn main() { let v = [1, 2, 3]; let total: i32 = v.iter().sum(); println!("total :{}", totall) }
any

The consumer any can find out whether there are elements in the iterator that meet the condition.

fn main() { let v = [1, 3, 4, 5]; let result1 = v.iter().any(|&x| x == 2); let result2 = v.iter().any(|x| *x == 2); // ^^ no implementation for `& == ` // let result3 = v.iter().any(|x| x == 2); }

You can only use reference and dereference,

collect

The iterator can be converted to the specified container type, that is, the elements in the iterator can be collected into the specified container.

fn main() { let v1 = [1, 2, 3, 4, 5]; // Traverse into a map, and then into the Vec array container let v2: Vec<i32> = v1.iter().map(|x| x + 1).collect(); println!(":{:?}", v2) }
iterator adaptor

You can convert the current iterator to another type of iterator and support chained calls to multiple iterator adapters.

However, all iterators are lazy, and there must be a consumer to get the call result of the iterator.

map, take, filter, rev and zip. Other iterators are found in std:: iter

map

Call the closure on each element in the iterator and generate a new iterator

fn main() { let v = [1, 2, 3]; let result: Vec<i32> = v.iter() .map(|x| x + 3) .collect(); println!("{:?}", result) }
take

The adapter take generates a new iterator that iterates only the first n elements of the original iterator, which is often used to traverse the scene of the specified data elements

fn main() { let v = [1, 2, 3, 4, 5]; let result = v.iter().take(3); for i in result { println!("{}", i) } }
filter

The adapter filter calls a closure on each element in the iterator and generates a new iterator that filters the elements. Closure will return true or false

fn main() { let v = [1, 2, 3, 4, 5]; let result = v.iter() .map(|x| x + 3) .filter(|x| x % 3 == 0) .collect(); println!("{:?}",result) }
rev

rev can reverse the iteration direction of the iterator

fn main() { let v = [1, 2, 3, 4, 5]; let result = v.iter().rev(); for i in result { println!("{}", i) } }
zip

You can compress two iterators together to produce a new iterator.

It iterates over two iterators simultaneously and returns a primitive ancestor, where the first element comes from the first iterator and the second element comes from the second iterator.

If any iterator returns None, the adapter zip returns None

fn main() { let v1 = [1, 2, 3]; let v2 = [1, 2, 3, 4, 5]; let result: Vec<i32> = v1.iter().zip(v2.iter()) .map(|(a, b)| a + b) .filter(|x| x % 3 == 0) .collect(); println!("{:?}",result) }

19 November 2021, 22:30 | Views: 2670

Add new comment

For adding a comment, please log in
or create account

0 comments