[易学易懂系列|rustlang语言|零基础|快速入门|(7)函数Functions与闭包Closure]

有意思的基础知识

函数Functions与闭包Closure


我们今天再来看看函数。

在Rust,函数由关键词:fn来定义。

如果有参数,必须定义参数的数据类型。

一般情况下,函数返回元组( tuple )类型,如果要返回特定的类型,一般要用符号:

-> 来定义。

请看代码如下:

1.main函数:

fn main() {
   println!("Hello, world!");
}

2.传递参数:

fn print_sum(a: i8, b: i8) {
   println!("sum is: {}", a + b);
}

3.有返回值:

/ 01. Without the return keyword. Only last expression returns.
fn plus_one(a: i32) -> i32 {
   a + 1
   // There is no ending ; in the above line. It means this is an expression which equals to `return a+1;`
}

// 02. With the return keyword.
fn plus_two(a: i32) -> i32 {
   return a + 2; // Returns a+2. But, this's a bad practice.
   // Should use only on conditional returns, except in the last expression
}

4.函数指针,作为数据类型:

// 01. Without type declarations
let b = plus_one;
let c = b(5); //6

// 02. With type declarations
let b: fn(i32) -> i32 = plus_one;
let c = b(5); //6

闭包:

1.闭包,即匿名函数或lambda函数。

2.参数类型与返回值,是可选的。

闭包,一句话来说,就是特殊的函数。

首先我们来看看正常函数:

fn main() {
 let x = 2;
 println!("{}", get_square_value(x));
}

fn get_square_value(x: i32) -> i32 {
   x * x
}

然后,我们用闭包来改写:

fn main() {
   let x = 2;
   let square = |x: i32| -> i32 { // Input parameters are passed inside | | and expression body is wrapped within { }
       x * x
  };
   println!("{}", square(x));
}

进一步简写:

fn main() {
   let x = 2;
   let square = |x| x * x; // { } are optional for single-lined closures
   println!("{}", square(x));
}

我们来简单对比一下函数与闭包,请看下面程序 :

fn main() {
   // 函数形式:累加1.
   fn function(i: i32) -> i32 {
       i + 1
  }

   //闭包形式:完整定义
   let closure_annotated = |i: i32| -> i32 { i + 1 };
   //闭包形式:简化定义,利用rust的类型推导功能自动进行类型推导,这个更常用
   let closure_inferred = |i| i + 1;
   let i = 1;

   // 调用函数与闭包
   println!("function: {}", function(i));
   println!("closure_annotated: {}", closure_annotated(i));
   println!("closure_inferred: {}", closure_inferred(i));

   //简单的闭包,只返回一个integer值,返回值 类型将如系统自动推导,即系统对1这个数字,自动判断,并推导出它是integer
   let one = || 1;
   println!("closure returning one: {}", one());
}

我们看到,函数定义更复杂。

我们再来看看下面的程序,比对一下:

fn main() {
   
   let x = 4;//定义一个integer变量
   // 函数形式:累加.
   fn function(i: i32) -> i32 {
       i + x//!!!!!这里编译报错!!!,函数不能从运行环境上获取其他变量!!!!
  }

   //闭包形式:完整定义
   let closure_annotated = |i: i32| -> i32 { i + x };//用闭包可以从环境中获取x变量
   //闭包形式:简化定义,这个更常用
   let closure_inferred = |i| i + x;
   let i = 1;

   // 调用函数与闭包
   println!("function: {}", function(i));
   println!("closure_annotated: {}", closure_annotated(i));
   println!("closure_inferred: {}", closure_inferred(i));

   //简单的闭包,只返回一个integer值,返回值 类型将如系统自动推导,即系统对1这个数字,自动判断,并推导出它是integer
   let one = || 1;
   println!("closure returning one: {}", one());
}

编译器报错!

编译器详细错误信息为:

error[E0434]: can't capture dynamic environment in a fn item
--> src\main.rs:5:13
|
5 |         i + x
|             ^
|
= help: use the `|| { ... }` closure form instead

这里的编译器详细指出:函数不能动态从环境(当前运行环境或上下文)获得x,并提示用闭包。

我们再来看看如下代码:

fn main() {
   use std::mem;
   let color = "green";

   // A closure to print `color` which immediately borrows (`&`) `color` and
   // stores the borrow and closure in the `print` variable. It will remain
   // borrowed until `print` is used the last time.
   //
   // `println!` only requires arguments by immutable reference so it doesn't
   // impose anything more restrictive.
   //定义一个简单的打印闭包,直接共享借用color变量,并把闭包绑定到print变量
   let print = || println!("`color`: {}", color);

   // Call the closure using the borrow.
   //直接调用这个闭包(借用color)
   print();

   // `color` can be borrowed immutably again, because the closure only holds
   // an immutable reference to `color`.
   //color变量可以再次共享借用_reborrow,因为闭包print只是用了共享借用color
   let _reborrow = &color;
   print();

   // A move or reborrow is allowed after the final use of `print`
   //上面调用 了print()闭包后,这个color可以移动move(即转移其数据所有权)
   let _color_moved = color;

   let mut count = 0;
   // A closure to increment `count` could take either `&mut count` or `count`
   // but `&mut count` is less restrictive so it takes that. Immediately
   // borrows `count`.
   //
   // A `mut` is required on `inc` because a `&mut` is stored inside. Thus,
   // calling the closure mutates the closure which requires a `mut`.
   //这里用可变借用变量count, 所以闭包也定义为可变的引用
   let mut inc = || {
       count += 1;
       println!("`count`: {}", count);
  };

   // Call the closure using a mutable borrow.
   //通过可变借用调用闭包
   inc();

   // The closure still mutably borrows `count` because it is called later.
   // An attempt to reborrow will lead to an error.
   // let _reborrow = &count;//这里如果共享借用将报错,因为count已经可变借用给闭包,再借用将通不过编译器
   // ^ TODO: try uncommenting this line.
   inc();

   // The closure no longer needs to borrow `&mut count`. Therefore, it is
   // possible to reborrow without an error
   //这个语句下面,没有再调用inc()闭包的代码,即现在闭包没有再可变借用变更count,现在就可以用可变借用来借用count
   let _count_reborrowed = &mut count;

   // A non-copy type.
   //定义一个引用变更或智能指针变量(非复制类型)
   let movable = Box::new(3);

   // `mem::drop` requires `T` so this must take by value. A copy type
   // would copy into the closure leaving the original untouched.
   // A non-copy must move and so `movable` immediately moves into
   // the closure.
   //定义一个闭包,把变量movable移动到闭包里,用方法mem::drop直接把变量内存释放
   let consume = || {
       println!("`movable`: {:?}", movable);
       mem::drop(movable);
  };

   // `consume` consumes the variable so this can only be called once.
   //调用闭包方consume直接把变量释放掉,所以这个闭包只能调用一次
   consume();
   // consume();//第二次调用会报错!
   // ^ TODO: Try uncommenting this lines.
}

上面的代码用来以下两个标准库

Boxstd::mem::drop

以上代码打印结果为:

`color`: green
`color`: green
`count`: 1
`count`: 2
`movable`:

我们再来看看更复杂的例子,把闭包当作一个输入参数:

// A function which takes a closure as an argument and calls it.
// <F> denotes that F is a "Generic type parameter"
//定义一个函数apply,其参数为:F类型的闭包
fn apply<F>(f: F)
where
  // The closure takes no input and returns nothing.
  //这里指定F类型的闭包为FnOnce类型,即它只能调用一次
  //这个闭包没有参数与没有返回值
  F: FnOnce(),
{
  // ^ TODO: Try changing this to `Fn` or `FnMut`.
  //可以尝试改变这个F类型为 Fn(不可变函数)或FnMut(可变函数)
  f();
}

// A function which takes a closure and returns an `i32`.
//定义一个函数apply_to_3,其参数为闭包,返回值为i32
fn apply_to_3<F>(f: F) -> i32
where
  // The closure takes an `i32` and returns an `i32`.
  //定义这个闭包类型为Fn(不可变函数),返回一个i32值
  F: Fn(i32) -> i32,
{
  f(3)
}

fn main() {
  use std::mem;

  let greeting = "hello";
  // A non-copy type.
  // `to_owned` creates owned data from borrowed one
  //非复制类型
  //to_owned()方法从一个借用变量中得到数据所有权
  let mut farewell = "goodbye".to_owned();

  // Capture 2 variables: `greeting` by reference and
  // `farewell` by value.
  //闭包获取两个变量:
  //通过引用获取greeting
  //通过值 获取farewell
  let diary = || {
      // `greeting` is by reference: requires `Fn`.
      //greeting从引用获取值
      println!("I said {}.", greeting);

      // Mutation forces `farewell` to be captured by
      // mutable reference. Now requires `FnMut`.
      //farewell从可变引用得到数据值,所以是可修改的
      farewell.push_str("!!!");
      println!("Then I screamed {}.", farewell);
      println!("Now I can sleep. zzzzz");

      // Manually calling drop forces `farewell` to
      // be captured by value. Now requires `FnOnce`.
      //手动地释放farewell的资源
      mem::drop(farewell);
  };

  // Call the function which applies the closure.
  //把闭包diary当作一个参数传给函数apply
  apply(diary);

  // `double` satisfies `apply_to_3`'s trait bound
  //定义一个闭包,乘以2
  let double = |x| 2 * x;

  println!("3 doubled: {}", apply_to_3(double));
}

以上结果为:

I said hello.
Then I screamed goodbye!!!.
Now I can sleep. zzzzz
3 doubled: 6

我们看到有一个where关键词,我们这里简单介绍下where的用法 。

之前,我们再来讨论一下特征变量的绑定,如下:

// Define a function `printer` that takes a generic type `T` which
// must implement trait `Display`.
//定义一个printer的函数,这个函数的参数T,必须实现特征Display
fn printer<T: Display>(t: T) {
   println!("{}", t);
}

上面就是简单的特征变量的绑定,那T也可以绑定多个特征:

use std::fmt::{Debug, Display};

fn compare_prints<T: Debug + Display>(t: &T) {
   println!("Debug: `{:?}`", t);
   println!("Display: `{}`", t);
}

fn compare_types<T: Debug, U: Debug>(t: &T, u: &U) {
   println!("t: `{:?}`", t);
   println!("u: `{:?}`", u);
}

fn main() {
   let string = "words";
   let array = [1, 2, 3];
   let vec = vec![1, 2, 3];

   compare_prints(&string);
   //compare_prints(&array);
   // TODO ^ Try uncommenting this.

   compare_types(&array, &vec);
}

那这个多个特征绑定定义,就可以用where语句来表示,更加清晰,如下两种定义是一样的:

impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}

// Expressing bounds with a `where` clause
//用where子语句来定义多个特征绑定定义
impl <A, D> MyTrait<A, D> for YourType where
  A: TraitB + TraitC,
  D: TraitE + TraitF {}

我们来看看例子:

use std::fmt::Debug;

trait PrintInOption {
   fn print_in_option(self);
}

// Because we would otherwise have to express this as `T: Debug` or
// use another method of indirect approach, this requires a `where` clause:
impl<T> PrintInOption for T where
   Option<T>: Debug {
   // We want `Option<T>: Debug` as our bound because that is what's
   // being printed. Doing otherwise would be using the wrong bound.
   fn print_in_option(self) {
       println!("{:?}", Some(self));
  }
}

fn main() {
   let vec = vec![1, 2, 3];

   vec.print_in_option();
}

上面代码结果为:

Some([1, 2, 3])

以上就是Rust的函数与闭包的基本知识。

以上,希望对你有用。

如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust

[易学易懂系列|rustlang语言|零基础|快速入门|(7)|函数Functions与闭包Closure]的更多相关文章

  1. [易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具]

    [易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具] 项目实战 实战5:实现BTC价格转换工具 今天我们来开发一个简单的BTC实时价格转换工具. 我们首先 ...

  2. [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]

    [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链] 项目实战 实战4:从零实现BTC区块链 我们今天来开发我们的BTC区块链系统. 简单来说,从数据结构的 ...

  3. [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)] 项目实战 实战3:Http服务器 我们今天来进一步开发我们的Http服务器,用多线程实现. 我 ...

  4. [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)] 项目实战 实战2:命令行工具minigrep 我们继续开发我们的minigrep. 我们现 ...

  5. [易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)] 项目实战 实战2:命令行工具minigrep 有了昨天的基础,我们今天来开始另一个稍微有点 ...

  6. [易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏]

    [易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏] 项目实战 实战1:猜数字游戏 我们今天来来开始简单的项目实战. 第一个简单项目是猜数字游戏. 简单来说,系统给了 ...

  7. [易学易懂系列|rustlang语言|零基础|快速入门|(5)|生命周期Lifetime]

    [易学易懂系列|rustlang语言|零基础|快速入门|(5)] Lifetimes 我们继续谈谈生命周期(lifttime),我们还是拿代码来说话: fn main() { let mut a = ...

  8. [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro]

    [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro] 实用知识 宏Macro 我们今天来讲讲Rust中强大的宏Macro. Rust的宏macro是实现元编程的强大工具. ...

  9. [易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针]

    [易学易懂系列|rustlang语言|零基础|快速入门|(21)|智能指针] 实用知识 智能指针 我们今天来讲讲Rust中的智能指针. 什么是指针? 在Rust,指针(普通指针),就是保存内存地址的值 ...

  10. [易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理]

    [易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理] 实用知识 错误处理 我们今天来讲讲Rust中的错误处理. 很多语言都有自己的错误处理方式,比如,java是异常处理机制. ...

随机推荐

  1. 十二:jinja2模板中使用url_for

    在页面中,有点击跳转到另一个地址的时候,可以使用url_for来指定要跳转的视图函数:{{ url_for('view_function') }} 如果该视图需要接收参数

  2. Python实现打印螺旋矩阵功能的方法

    Python实现打印螺旋矩阵功能的方法 本文实例讲述了Python实现打印螺旋矩阵功能的方法.分享给大家供大家参考,具体如下: 一.问题描述 输入N, 打印 N*N 螺旋矩阵 比如 N = 3,打印: ...

  3. 依赖注入——angular

    在Angular中创建一个对象时,需要依赖另一个对象,这是代码层的一种依赖关系,当这种依赖被声明后,Angular通过injector注入器将所依赖的对象进行注入操作. 一.依赖注入的原理 看下面的示 ...

  4. 自动部署脚本-bash

    from here !/bin/bash Check if user is root if [ $(id -u) != "0" ]; then Echo_Red "Err ...

  5. for循环实现九九乘法表

    <!--for循环实现九九乘法表--> <table border="> <tbody> {% for x in range(1,10) %} <t ...

  6. [转帖]2016年的文章: 解读ASP.NET 5 & MVC6系列教程(1):ASP.NET 5简介

    解读ASP.NET 5 & MVC6系列教程(1):ASP.NET 5简介 更新时间:2016年06月23日 11:38:00   作者:汤姆大叔    我要评论 https://www.jb ...

  7. 浅谈React工作原理

    浅谈React工作原理:https://www.cnblogs.com/yikuu/p/9660932.html 转自:https://cloud.tencent.com/info/63f656e0b ...

  8. 移除list里面的值

    public class IteratorTest { public static void main(String[] args) { List<String> list = new A ...

  9. noip2013day1-货车运输

    题目描述 \(A\)国有\(n\)座城市,编号从 \(1\)到\(n\),城市之间有 \(m\) 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 \(q\) 辆货车在运输货物, 司机们想知 ...

  10. 从window 的cmd窗口中下载linux 服务器上文件

    下载linux 服务器上的文件 down.php 格式为  pscp linux服务器上用户名@linux 服务器ip  文件在windows系统上的绝对路径 如果是下载服务器上的某个目录,只要在ps ...