Rust: Erro errado: não é possível sair da variável externa capturada em um fechamento `FnMut`

Criado em 29 jan. 2018  ·  3Comentários  ·  Fonte: rust-lang/rust

erro: não é possível sair da variável externa capturada em um fechamento FnMut

struct Foo {
    a: i32,
    b: i32,
    bar: Bar,
}

struct Bar;

impl Bar {
    fn f<F: FnMut()>(&self, mut f: F) {
        f();
    }
}

fn main() {
    let mut foo = Foo { a: 1, b: 2, bar: Bar };
    let a = &mut foo.a;
    let b = &mut foo.b;
    (|| { // works
        *if true {a} else {b} = 42;
    })();

    let mut foo = Foo { a: 1, b: 2, bar: Bar };
    let a = &mut foo.a;
    let b = &mut foo.b;
    foo.bar.f(|| { // doesn't work
        *if true {a} else {b} = 42;
    });
}

https://play.rust-lang.org/?gist=4ce6948a92c2fcb281b3cade8574691d&version=nightly

Mas o segundo caso também deve funcionar!

A-borrow-checker A-diagnostics C-enhancement T-compiler

Comentários muito úteis

@Boscop porque você só pode mover coisas uma vez - um fechamento FnMut pode ser invocado mais de uma vez.

(Btw, o erro deveria dizer que tem que ser FnOnce então.)

Não concordo inteiramente, mas também não discordo . Geralmente, é difícil decidir como diagnosticar esse tipo de erro. Há um tipo de tensão aqui: o fechamento quer realizar uma ação (um movimento), mas a assinatura de f não o permite (porque requer um fechamento que pode ser invocado mais de uma vez). Pode ser que a assinatura esteja errada (deve ser generalizada para aceitar um FnOnce ) ou que o fechamento esteja errado (ele está tentando fazer coisas que não deveria). No mínimo, claramente não estamos fazendo um bom trabalho em capturar essa 'tensão' no diagnóstico.

Curiosamente, se o fechamento não for dado diretamente como um argumento para f , daremos um erro um pouco melhor :

error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
  --> src/main.rs:19:19
   |
19 |       let closure = || { // doesn't work
   |  ___________________^
20 | |         *if true {a} else {b} = 42;
21 | |     };
   | |_____^
22 |       foo.bar.f(closure);
   |               - the requirement to implement `FnMut` derives from here
   |
note: closure is `FnOnce` because it moves the variable `a` out of its environment
  --> src/main.rs:20:19
   |
20 |         *if true {a} else {b} = 42;
   |                

Isso ocorre por causa do rastreamento que @estebank (eu acho? Ou foi outra pessoa ...) colocou para rastrear porque decidimos a "característica definidora" de um fechamento. No momento, essa verificação não está entrando em jogo porque estamos decidindo que o fechamento deve ser FnMut unicamente por causa da assinatura de f .

Isso acontece neste código:

https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/closure.rs#L151 -L155

Quando seguimos por esse caminho, nunca acabamos armazenando a "origem" do tipo. Em contraste, a inferência upvar que, de outra forma, teria efeito:

https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/upvar.rs#L182 -L188

Portanto, provavelmente precisamos armazenar algum tipo de informação em closure_kind_origins_mut e, em seguida, melhorar o cheque de empréstimo. Então, poderíamos dar um erro como:

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
  --> src/main.rs:20:19
   |
17 |     let a = &mut foo.a;
   |         - captured outer variable
...
20 |         *if true {a} else {b} = 42;
   |                   ^ cannot move out of captured outer variable in an `FnMut` closure
note: closure is `FnMut` because of the requirements of `f()`
  --> src/main.rs:19:19
   |
19 |         foo.bar.f(||
   |         ^^^^^^^^^
   |            

Todos 3 comentários

Funciona se você alterar o FnMut para FnOnce.

Mas por que também não funciona com o FnMut?

(Btw, o erro deveria dizer que tem que ser FnOnce então.)

@Boscop porque você só pode mover coisas uma vez - um fechamento FnMut pode ser invocado mais de uma vez.

(Btw, o erro deveria dizer que tem que ser FnOnce então.)

Não concordo inteiramente, mas também não discordo . Geralmente, é difícil decidir como diagnosticar esse tipo de erro. Há um tipo de tensão aqui: o fechamento quer realizar uma ação (um movimento), mas a assinatura de f não o permite (porque requer um fechamento que pode ser invocado mais de uma vez). Pode ser que a assinatura esteja errada (deve ser generalizada para aceitar um FnOnce ) ou que o fechamento esteja errado (ele está tentando fazer coisas que não deveria). No mínimo, claramente não estamos fazendo um bom trabalho em capturar essa 'tensão' no diagnóstico.

Curiosamente, se o fechamento não for dado diretamente como um argumento para f , daremos um erro um pouco melhor :

error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
  --> src/main.rs:19:19
   |
19 |       let closure = || { // doesn't work
   |  ___________________^
20 | |         *if true {a} else {b} = 42;
21 | |     };
   | |_____^
22 |       foo.bar.f(closure);
   |               - the requirement to implement `FnMut` derives from here
   |
note: closure is `FnOnce` because it moves the variable `a` out of its environment
  --> src/main.rs:20:19
   |
20 |         *if true {a} else {b} = 42;
   |                

Isso ocorre por causa do rastreamento que @estebank (eu acho? Ou foi outra pessoa ...) colocou para rastrear porque decidimos a "característica definidora" de um fechamento. No momento, essa verificação não está entrando em jogo porque estamos decidindo que o fechamento deve ser FnMut unicamente por causa da assinatura de f .

Isso acontece neste código:

https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/closure.rs#L151 -L155

Quando seguimos por esse caminho, nunca acabamos armazenando a "origem" do tipo. Em contraste, a inferência upvar que, de outra forma, teria efeito:

https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/upvar.rs#L182 -L188

Portanto, provavelmente precisamos armazenar algum tipo de informação em closure_kind_origins_mut e, em seguida, melhorar o cheque de empréstimo. Então, poderíamos dar um erro como:

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
  --> src/main.rs:20:19
   |
17 |     let a = &mut foo.a;
   |         - captured outer variable
...
20 |         *if true {a} else {b} = 42;
   |                   ^ cannot move out of captured outer variable in an `FnMut` closure
note: closure is `FnMut` because of the requirements of `f()`
  --> src/main.rs:19:19
   |
19 |         foo.bar.f(||
   |         ^^^^^^^^^
   |            
Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

SharplEr picture SharplEr  ·  3Comentários

behnam picture behnam  ·  3Comentários

modsec picture modsec  ·  3Comentários

drewcrawford picture drewcrawford  ·  3Comentários

cuviper picture cuviper  ·  3Comentários