我可以在循环的多次迭代中传递相同的可变特征对象而不添加间接吗?

She*_*ter 4 rust

我正在编写一些可以输出到标准输出或文件的代码。根据某些外部条件,我实例化文件或标准输出,然后从对适当项目的引用创建一个特征对象:

use std::{io,fs};

fn write_it<W>(mut w: W) where W: io::Write { }

fn main() {
    let mut stdout;
    let mut file;

    let t: &mut io::Write = if true {
        stdout = io::stdout();
        &mut stdout
    } else {
        file = fs::File::create("/tmp/output").unwrap();
        &mut file
    };

    for _ in 0..10 {
        write_it(t);
    }
}
Run Code Online (Sandbox Code Playgroud)

write_it这工作正常,直到我尝试多次调用。这将失败,因为t被移入write_it,因此在循环的后续迭代中不可用:

<anon>:18:18: 18:19 error: use of moved value: `t`
<anon>:18         write_it(t);
                           ^
note: `t` was previously moved here because it has type `&mut std::io::Write`, which is non-copyable
Run Code Online (Sandbox Code Playgroud)

我可以通过添加另一层间接来解决这个问题:

let mut t: &mut io::Write;
write_it(&mut t);
Run Code Online (Sandbox Code Playgroud)

但这似乎可能效率低下。实际上效率低下吗?有没有更干净的方式来编写这段代码?

huo*_*uon 5

您需要明确地重新借用:

for _ in 0..10 {
    write_it(&mut *t);
}
Run Code Online (Sandbox Code Playgroud)

人们经常看到这种情况隐式发生,但在这种情况下并非如此,因为write_it采用原始泛型 ,W并且编译器仅&mut在需要 的地方使用时才隐式重新借用 a &mut。例如,如果是

fn write_it<W: ?Sized + Write>(w: &mut W) { ... }
Run Code Online (Sandbox Code Playgroud)

您的代码工作正常,因为&mut参数类型中的显式将确保编译器将隐式重新借用较短的生命周期(即 )&mut*

像这样的案例表明,&mut实际上确实转移了所有权,隐性的重新借用通常会掩盖它以支持改进的人体工程学。

至于具有额外引用的版本的性能: a 的速度&mut (&mut Write)可能与普通的 没有区别&mut Write:虚拟调用通常比取消引用&mut.

此外, 的别名保证&mut意味着编译器对于如何与 a 交互非常自由:例如,根据内部原理,它可以在 a 开始时将指针中&mut的两个字加载到寄存器中,然后将任何更改写回结束。这是合法的,因为成为一个意味着没有其他东西可以改变该记忆。&mut Writewrite_it&mut

最后,目前,像 a 这样的“大”值&mut Write是通过指针传递的;&mut &mut Write与机器上的基本相同。&mut *t和版本的程序集&mut t都会启动(实际上我能看到的唯一区别是标签的名称Ltmp...):

_ZN8write_it20h2919620193267806634E:
    .cfi_startproc
    cmpq    %fs:112, %rsp
    ja  .LBB4_2
    movabsq $72, %r10
    movabsq $0, %r11
    callq   __morestack
    retq
.LBB4_2:
    pushq   %r14
.Ltmp116:
    .cfi_def_cfa_offset 16
    pushq   %rbx
.Ltmp117:
    .cfi_def_cfa_offset 24
    subq    $56, %rsp
.Ltmp118:
    .cfi_def_cfa_offset 80
.Ltmp119:
    .cfi_offset %rbx, -24
.Ltmp120:
    .cfi_offset %r14, -16
    movq    (%rdi), %rsi
    movq    8(%rdi), %rax
    ...
Run Code Online (Sandbox Code Playgroud)

末尾的两个movqs 将特征对象的两个字加载&mut Write到寄存器中。