Rust: 잘λͺ»λœ 였λ₯˜: 'FnMut' ν΄λ‘œμ €μ—μ„œ 캑처된 μ™ΈλΆ€ λ³€μˆ˜ λ°–μœΌλ‘œ 이동할 수 μ—†μŠ΅λ‹ˆλ‹€.

에 λ§Œλ“  2018λ…„ 01μ›” 29일  Β·  3μ½”λ©˜νŠΈ  Β·  좜처: rust-lang/rust

였λ₯˜: 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

κ·ΈλŸ¬λ‚˜ 두 번째 κ²½μš°λ„ μž‘λ™ν•΄μ•Ό ν•©λ‹ˆλ‹€!

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

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

@Boscop 은 ν•œ 번만 이동할 수 있기 λ•Œλ¬Έμ— FnMut ν΄λ‘œμ €λŠ” 두 번 이상 호좜될 수 μžˆμŠ΅λ‹ˆλ‹€.

(Btw, 였λ₯˜λŠ” FnOnceμ—¬μ•Ό ν•œλ‹€κ³  말해야 ν•©λ‹ˆλ‹€.)

μ „μ μœΌλ‘œ λ™μ˜ν•˜μ§€λŠ” μ•Šμ§€λ§Œ λ™μ˜ ν•˜μ§€λ„ f 의 μ„œλͺ…은 이λ₯Ό ν—ˆμš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€(두 번 이상 ν˜ΈμΆœν•  수 μžˆλŠ” ν΄λ‘œμ €κ°€ ν•„μš”ν•˜κΈ° λ•Œλ¬Έμ—). μ„œλͺ…에 였λ₯˜κ°€ μžˆκ±°λ‚˜( FnOnce ν—ˆμš©ν•˜λ„λ‘ μΌλ°˜ν™”λ˜μ–΄μ•Ό 함) ν΄λ‘œμ €κ°€ 잘λͺ»λ˜μ—ˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€(ν•˜μ§€ 말아야 ν•  일을 ν•˜λ €κ³  함). μ΅œμ†Œν•œ μš°λ¦¬λŠ” μ§„λ‹¨μ—μ„œ 이 'κΈ΄μž₯'을 잘 ν¬μ°©ν•˜μ§€ λͺ»ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

ν₯λ―Έλ‘­κ²Œλ„ ν΄λ‘œμ €κ°€ f λŒ€ν•œ 인수둜 직접 μ œκ³΅λ˜μ§€ μ•ŠμœΌλ©΄ μ•½κ°„ 더 λ‚˜μ€ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€ .

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;
   |                

이것은 @estebank (λ‚΄ 생각에? μ•„λ‹ˆλ©΄ λ‹€λ₯Έ μ‚¬λžŒ 이유 λ₯Ό μΆ”μ ν•˜κΈ° μœ„ν•΄ 넣은 좔적 λ•Œλ¬Έμž…λ‹ˆλ‹€. ν˜„μž¬ f 의 μ„œλͺ… λ•Œλ¬Έμ— ν΄λ‘œμ €κ°€ FnMut μ—¬μ•Ό ν•œλ‹€κ³  κ²°μ •ν–ˆκΈ° λ•Œλ¬Έμ— ν•΄λ‹Ή 검사가 μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

이 μ½”λ“œμ—μ„œ λ°œμƒν•©λ‹ˆλ‹€.

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

μš°λ¦¬κ°€ κ·Έ 길을 갈 λ•Œ μš°λ¦¬λŠ” κ·Έ μ’…λ₯˜μ˜ "원점"을 μ €μž₯ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λŒ€μ‘°μ μœΌλ‘œ, 그렇지 μ•ŠμœΌλ©΄ μ‹œμž‘λ  upvar 좔둠은 λ‹€μŒμ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

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

λ”°λΌμ„œ closure_kind_origins_mut 에 μ–΄λ–€ μ’…λ₯˜μ˜ 정보λ₯Ό μ €μž₯ν•œ λ‹€μŒ μ°¨μž… μˆ˜ν‘œλ₯Ό κ°œμ„ ν•΄μ•Ό ν•©λ‹ˆλ‹€. 그러면 λ‹€μŒκ³Ό 같은 였λ₯˜κ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

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(||
   |         ^^^^^^^^^
   |            

λͺ¨λ“  3 λŒ“κΈ€

FnMut을 FnOnce둜 λ³€κ²½ν•˜λ©΄ μž‘λ™ν•©λ‹ˆλ‹€.

ν•˜μ§€λ§Œ FnMutμ—μ„œλ„ μž‘λ™ν•˜μ§€ μ•ŠλŠ” μ΄μœ λŠ” λ¬΄μ—‡μž…λ‹ˆκΉŒ?

(Btw, 였λ₯˜λŠ” FnOnceμ—¬μ•Ό ν•œλ‹€κ³  말해야 ν•©λ‹ˆλ‹€.)

@Boscop 은 ν•œ 번만 이동할 수 있기 λ•Œλ¬Έμ— FnMut ν΄λ‘œμ €λŠ” 두 번 이상 호좜될 수 μžˆμŠ΅λ‹ˆλ‹€.

(Btw, 였λ₯˜λŠ” FnOnceμ—¬μ•Ό ν•œλ‹€κ³  말해야 ν•©λ‹ˆλ‹€.)

μ „μ μœΌλ‘œ λ™μ˜ν•˜μ§€λŠ” μ•Šμ§€λ§Œ λ™μ˜ ν•˜μ§€λ„ f 의 μ„œλͺ…은 이λ₯Ό ν—ˆμš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€(두 번 이상 ν˜ΈμΆœν•  수 μžˆλŠ” ν΄λ‘œμ €κ°€ ν•„μš”ν•˜κΈ° λ•Œλ¬Έμ—). μ„œλͺ…에 였λ₯˜κ°€ μžˆκ±°λ‚˜( FnOnce ν—ˆμš©ν•˜λ„λ‘ μΌλ°˜ν™”λ˜μ–΄μ•Ό 함) ν΄λ‘œμ €κ°€ 잘λͺ»λ˜μ—ˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€(ν•˜μ§€ 말아야 ν•  일을 ν•˜λ €κ³  함). μ΅œμ†Œν•œ μš°λ¦¬λŠ” μ§„λ‹¨μ—μ„œ 이 'κΈ΄μž₯'을 잘 ν¬μ°©ν•˜μ§€ λͺ»ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

ν₯λ―Έλ‘­κ²Œλ„ ν΄λ‘œμ €κ°€ f λŒ€ν•œ 인수둜 직접 μ œκ³΅λ˜μ§€ μ•ŠμœΌλ©΄ μ•½κ°„ 더 λ‚˜μ€ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€ .

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;
   |                

이것은 @estebank (λ‚΄ 생각에? μ•„λ‹ˆλ©΄ λ‹€λ₯Έ μ‚¬λžŒ 이유 λ₯Ό μΆ”μ ν•˜κΈ° μœ„ν•΄ 넣은 좔적 λ•Œλ¬Έμž…λ‹ˆλ‹€. ν˜„μž¬ f 의 μ„œλͺ… λ•Œλ¬Έμ— ν΄λ‘œμ €κ°€ FnMut μ—¬μ•Ό ν•œλ‹€κ³  κ²°μ •ν–ˆκΈ° λ•Œλ¬Έμ— ν•΄λ‹Ή 검사가 μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

이 μ½”λ“œμ—μ„œ λ°œμƒν•©λ‹ˆλ‹€.

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

μš°λ¦¬κ°€ κ·Έ 길을 갈 λ•Œ μš°λ¦¬λŠ” κ·Έ μ’…λ₯˜μ˜ "원점"을 μ €μž₯ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λŒ€μ‘°μ μœΌλ‘œ, 그렇지 μ•ŠμœΌλ©΄ μ‹œμž‘λ  upvar 좔둠은 λ‹€μŒμ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

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

λ”°λΌμ„œ closure_kind_origins_mut 에 μ–΄λ–€ μ’…λ₯˜μ˜ 정보λ₯Ό μ €μž₯ν•œ λ‹€μŒ μ°¨μž… μˆ˜ν‘œλ₯Ό κ°œμ„ ν•΄μ•Ό ν•©λ‹ˆλ‹€. 그러면 λ‹€μŒκ³Ό 같은 였λ₯˜κ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

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(||
   |         ^^^^^^^^^
   |            
이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰