返回介绍

5.2.2 返回值

发布于 2024-10-13 11:25:29 字数 3669 浏览 0 评论 0 收藏 0

当闭包作为返回值时,依旧要用到特征。以告知接收方获取了哪种闭包,该如何使用。

fn test() -> impl FnOnce(i32) -> i32 {
  |x: i32| x + 1           // 是不是闭包,或者是不是 move,
}                    // 对 FnOnce 没有影响。

fn main() {
  let inc = test();          // 受影响的是 inc,必须遵守 FnOnce 约束。
  inc(1);

  // inc(1);
  // ^^^ value used here after move
}
fn test() -> impl FnMut(i32) -> i32 {
  |x: i32| x + 1
}

fn main() {
  let mut inc = test();  // FnMut,必须 mut !!!

  inc(1);
  inc(1);
}

如闭包引用局部变量,须转移所有权,确保能安全释放。

  • 复制环境变量。
  • 或强制转移(move)。
fn test() -> impl FnMut(char) {
  let mut s = String::from("abc"); 
  |c| { s.push(c); }    // FnMut,也就是说闭包生命周期超过 s。
}               // 要么把 s 复制,要么用 move 转移所有权。

fn main() {
  let mut c = test();
  
  c('d');
  c('e');
}
error[E0373]: closure may outlive the current function, 
        but it borrows `s`, which is owned by the current function
        
  |
3 |   |c| { s.push(c); }
  |   ^^^   - `s` is borrowed here
  |   |
  |   may outlive borrowed value `s`
  |
  
help: to force the closure to take ownership of `s` (and 
    any other referenced variables), use the `move` keyword
  |
3 |   move |c| { s.push(c); }
  |   ^^^^^^^^

实现

和普通所有权转移类似的操作。

fn test() -> impl Fn() {
  let s = [0x11i64, 0x22i64, 0x33i64, 0x44i64, 0x55i64];
  move || {
    println!("{:?}", s);
  }
}

fn main() {
  let c = test();
  c();
}
(gdb) b 3
(gdb) b 10
(gdb) r

(gdb) disass
Dump of assembler code for function demo::test:
   0x00005555555591f0 <+0>:    sub  rsp,0x38
   
   ; main 提供的返回值内存地址。
   0x00005555555591f4 <+4>:    mov  rax,rdi   
   
   ; 环境变量 s,栈分配。
   0x00005555555591f7 <+7>:    mov  QWORD PTR [rsp+0x10],0x11
   0x0000555555559200 <+16>:  mov  QWORD PTR [rsp+0x18],0x22
   0x0000555555559209 <+25>:  mov  QWORD PTR [rsp+0x20],0x33
   0x0000555555559212 <+34>:  mov  QWORD PTR [rsp+0x28],0x44
   0x000055555555921b <+43>:  mov  QWORD PTR [rsp+0x30],0x55
   
   ; 复制环境变量。
   ; void* memcpy( void *dest, const void *src, size_t count );
   ; rax        rdi         rsi      rdx
   ; 返回 main 提供的内存地址。
=> 0x0000555555559224 <+52>:  lea  rcx,[rsp+0x10]
   0x0000555555559229 <+57>:  mov  rsi,rcx
   0x000055555555922c <+60>:  mov  edx,0x28
   0x0000555555559231 <+65>:  mov  QWORD PTR [rsp+0x8],rax
   0x0000555555559236 <+70>:  call   0x555555559060 <memcpy@plt>
   0x000055555555923b <+75>:  mov  rax,QWORD PTR [rsp+0x8]
   
   0x0000555555559240 <+80>:  add  rsp,0x38
   0x0000555555559244 <+84>:  ret  
End of assembler dump.
(gdb) c
Breakpoint 2, demo::main  main.rs:10

(gdb) ptype c  # 查看闭包结构。
type = struct demo::test::closure-0 (
  [i64; 5],
)

(gdb) p c    # 查看闭包内容。
$1 = demo::test::closure-0 (
  [17, 34, 51, 68, 85]
)

(gdb) x/5xg &c   # 内存地址和值。
0x7fffffffe198:  0x0000000000000011  0x0000000000000022
0x7fffffffe1a8:  0x0000000000000033  0x0000000000000044
0x7fffffffe1b8:  0x0000000000000055

(gdb) p/x $rsp   # 复制到 main 栈帧。
$2 = 0x7fffffffe190

(gdb) disass
Dump of assembler code for function demo::main:
   0x0000555555559250 <+0>:    sub  rsp,0x38
   
   ; 由 main 提供内存,用于保存 test 返回的闭包对象。
   0x0000555555559254 <+4>:    lea  rdi,[rsp+0x8]
   0x0000555555559259 <+9>:    call   0x5555555591f0 <demo::test>
   
   ; 将闭包传递给匿名函数。
=> 0x000055555555925e <+14>:  lea  rdi,[rsp+0x8]
   0x0000555555559263 <+19>:  call   0x555555559820 <demo::test::{{closure}}>
   0x0000555555559268 <+24>:  add  rsp,0x38
   0x000055555555926c <+28>:  ret  
End of assembler dump.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文