zoukankan      html  css  js  c++  java
  • [易学易懂系列|rustlang语言|零基础|快速入门|(7)|函数Functions与闭包Closure]

    [易学易懂系列|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
    --> srcmain.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



  • 相关阅读:
    【BZOJ】【2661】【Beijing WC2012】连连看
    【BZOJ】【2424】【HAOI2010】订货
    【BZOJ】【1061】【NOI2008】志愿者招募
    【POJ】【3680】Intervals
    网络流建模
    【BZOJ】【1221】【HNOI2001】软件开发
    【BZOJ】【1877】【SDOI2009】晨跑
    bzoj2054: 疯狂的馒头(并查集)
    浴谷金秋线上集训营 T11738 伪神(树链剖分)
    51nod1967 路径定向(欧拉回路+结论题)
  • 原文地址:https://www.cnblogs.com/gyc567/p/11921637.html
Copyright © 2011-2022 走看看