クロージャについて

 クロージャについて

Rust の LT 会! Rust 入門者の集い 2017/03/01

E4a2e149630d63b81292f6e4fddb7a3b?s=128

KINOSHITA Minoru

March 01, 2017
Tweet

Transcript

  1. Ϋϩʔδϟʹ͍ͭͯ Rust ͷ LT ձʂ Rust ೖ໳ऀͷू͍ थԼູ 2017/03/01 1

    / 22
  2. ࣗݾ঺հ ͳ·͑ थԼູ twitter @trigott ॴଐ KLab inc. ޷͖ͳݴޠ Agda,

    OCaml 2 / 22
  3. ▶ Rust ྺ͸΄Μͷগ͠ ▶ ΋ͱ΋ͱܕͷڧ͍ؔ਺ܕݴޠ͕޷͖Ͱɺܕਪ࿦΍ύλʔϯ Ϛονͷ࢖͑Δ Rust ͸ͳΜͱͳ͘࢖͑ͨ ▶ ҰํͰ

    Rust ͷγεςϜϓϩάϥϛϯάݴޠͱͯ͠ͷʁ੍໿ ʹͿ͔ͭΔ͜ͱ΋͋ͬͨ ▶ Ϋϩʔδϟ͸ؔ਺ܕݴޠͰ͸ೃછΈͷਂ͍֓೦͕ͩɺRust Ͱ ࢖͏ࡍʹ͸஫ҙ͕ඞཁͱ͍͏యܕతͳྫͩͱײͨ͡ͷͰɺΫ ϩʔδϟͷ࿩Λ͠·͢ ▶ Rust ͸ૉਓͳͷͰ࿩͸͍͚ٙͬͯͨͩΔͱ 3 / 22
  4. Ϋϩʔδϟͱ͸ ▶ ؔ਺ͱؔ਺தʹݱΕΔม਺ΛධՁ͢ΔͨΊͷ؀ڥͷ૊ ▶ SICP ͷۜߦޱ࠲ͷྫ (define (make-withdraw balance) (lambda

    (amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient␣funds")))) (define W1 (make-withdraw 100)) (define W2 (make-withdraw 100)) (W1 10) ; => 90 (W1 20) ; => 70 (W1 80) ; => "Insufficient funds" (W2 40) ; => 60 4 / 22
  5. ▶ OCaml ͷ৔߹ɺࢀর͚ͩѻ͍Λม͑ͯޙ͸΄΅ͦͷ·· let make_withdraw (balance : int ref) :

    int -> result = fun amount -> if !balance >= amount then (balance := !balance - amount; Ok !balance) else Err "Insufficient␣funds" let () = let w1 = make_withdraw (ref 100) and w2 = make_withdraw (ref 100) in print_result (w1 10); (* Ok (90) *) print_result (w1 20); (* Ok (70) *) print_result (w1 80); (* Err(" Insufficient funds ") * print_result (w2 40) (* Ok (60) *) 5 / 22
  6. ▶ Rust ͷ৔߹ fn make_withdraw(mut balance: i32) -> Box <FnMut(i32)

    -> Result <i32 , &’static str >> { Box::new(move |amount: i32| { if balance >= amount { balance -= amount; Ok(balance) } else { Err("Insufficient␣funds") } }) } let mut w1 = make_withdraw (100); let mut w2 = make_withdraw (100); println !("{:?}", w1 (10)); // Ok (90) println !("{:?}", w1 (20)); // Ok (70) println !("{:?}", w1 (80)); // Err(" Insufficient funds ") println !("{:?}", w2 (40)); // Ok (60) 6 / 22
  7. ▶ mut, FnMut, move, Box ͬͯԿ ▶ ؆୯ͳύʔπ͔Β࢝Ίͯɺઆ໌͍͖ͯ͠·͢ 7 /

    22
  8. ▶ ؆୯ͳؔ਺Λฦͯ͠ΈΔ fn id_i32 () -> i32 -> i32 {

    |x: i32| { x } } ▶ ΤϥʔʹͳΔ error: expected one of ‘!‘, ‘(‘, ‘::‘, ‘<‘, ‘where‘, or ‘{‘, found ‘->‘ --> account1.rs:2:24 | 2 | fn id_i32() -> i32 -> i32 { | ^^ ▶ ??? 8 / 22
  9. ▶ υΩϡϝϯτΛಡΉͱɺΫϩʔδϟͷܕ͸ Fn ͱ͍͏ trait Λ ࣮૷͍ͯ͠ͳ͍ͱ͍͚ͳ͍Β͍͠ ▶ ͳΜ͔Τϥʔϝοηʔδෆ਌੾ͳؾ͕ ▶

    ͱΓ͋͑ͣงғؾͰԼͷΑ͏ʹॻ͍ͯΈΔ fn id_i32 () -> Fn(i32) -> i32 { |x: i32| { x } } ▶ ΤϥʔʹͳΔ error[E0308]: mismatched types --> account1.rs:7:9 | 7 | |x: i32| { x } | ^^^^^^^^^^^^^^ expected trait std::ops::Fn, found closure | = note: expected type ‘std::ops::Fn(i32) -> i32 + ’static‘ = note: found type ‘[closure@account1.rs:7:9: 7:23]‘ ▶ ??? 9 / 22
  10. ▶ Ϋϩʔδϟͷܕ͸ͦΕͧΕݻ༗ͷܕ໊͕ͭͨ͘Ίɺγάχ νϟͷܕͱਪ࿦͞ΕͨܕͱͰϛεϚον͕ى͖͍ͯΔͱࢥΘ ΕΔ ▶ ྫ͑͹ɺҾ਺ͱͯ͠ΫϩʔδϟΛड͚औΔͱ͖͸ɺ generics ʹ trait ੍໿Λ͚ͭΔܗͰܕΛॻ͔ͳ͍ͱ͍͚

    ͳ͍ ▶ > Because each closure generates its own environment struct and implementation of Fn and friends, these types are anonymous. They exist solely for this closure. So Rust shows them as closure@<anon>, rather than some autogenerated name. ▶ ΫϩʔδϟΛฦ͍ͨ͠৔߹Ͳ͏ͨ͠Β͍͍͔෼͔Βͣ... 10 / 22
  11. ▶ ࣮͸΋͏ҰͭΤϥʔ͕ग़͍ͯͨͷͰͦͪΒʹ஫໨ͯ͠ΈΔ error[E0277]: the trait bound ‘std::ops::Fn(i32) -> i32 +

    ’static: std::marker::Sized‘ is not satisfied --> account1.rs:6:20 | 6 | fn id_i32() -> Fn(i32) -> i32{ | ^^^^^^^^^^^^^^ | the trait ‘std::marker::Sized‘ is not implemented for | ‘std::ops::Fn(i32) -> i32 + ’static‘ | = note: ‘std::ops::Fn(i32) -> i32 + ’static‘ does not have a constant size known at compile-time = note: the return type of a function must have a statically known size ▶ Fn(i32) -> i32 ͸ trait ͳͷͰɺෳ਺ͷܕͷީิ͕͋Γɺ ܕͷαΠζ͕෼͔Βͳ͍ ▶ ฦΓ஋ͷܕͷαΠζ͸ίϯύΠϧ࣌ʹ෼͔͍ͬͯΔඞཁ͕͋ ΔͨΊɺౖΒΕ͍ͯΔ 11 / 22
  12. ▶ Box Λ࢖͏ ▶ ώʔϓྖҬ΁ͷϙΠϯλܕ ▶ ࢀরͳͷͰܕͷαΠζ͸Ұఆ fn id_i32 ()

    -> Box <Fn(i32) -> i32 > { Box::new(|x: i32| { x }) } ▶ Ͱ͖ͨ! ▶ ͳ͔ͥܕνΣ οΫ௨Δ 12 / 22
  13. ▶ ͦΖͦΖ mut ͍ͨ͠ ▶ Rust ͷม਺͸جຊ immutableɺՄมʹ͍ͨ͠ͱ͖͸ mut Λ͚ͭΔ

    fn make_adder () -> Box <Fn(i32) -> i32 > { let mut acc = 0; Box::new(|x: i32| { acc += x; acc }) } 13 / 22
  14. ▶ ΤϥʔʹͳΔ error[E0373]: closure may outlive the current function, but

    it borrows ‘acc‘, which is owned by the current function --> account1.rs:16:18 | 16 | Box::new(|x: i32| { | ^^^^^^^^ may outlive borrowed value ‘acc‘ 17 | acc += x; | --- ‘acc‘ is borrowed here | ▶ ΢ΦΞΞΞΞΞΞΞ 14 / 22
  15. ▶ ߟ͑ͯΈΑ͏ fn make_adder () -> Box <Fn(i32) -> i32

    > { let mut acc = 0; Box::new(|x: i32| { acc += x; acc }) } let mut adder = make_adder () println !("{:?}", adder (10)) ▶ acc ͷ lifetime ͸ make_adder ͷத਎͚ͩ ▶ make_adder ͷฦ͢Ϋϩʔδϟ͸ make_adder ͷຊମΑΓ΋ ௕ੜ͖͢Δ ▶ make_adder ͷฦ͢Ϋϩʔδϟ͸ acc Λࢀর͍ͯ͠Δ͕ make_adder ͷ࣮ߦ͕ऴΘͬͨ͋ͱͩͱ͢Ͱʹ acc ͸ࢮΜͰ ͍ΔͨΊࢀরͰ͖ͳ͍ 15 / 22
  16. help: to force the closure to take ownership of ‘acc‘

    (and any other referenced variables), use the ‘move‘ keyword, as shown: | Box::new(move |x: i32| { ▶ ࣮͸ help ͕ग़͍ͯͨͷͰɺͦΕʹै͍ move Λ࢖͏ ▶ move Λ࢖͏ͱ؀ڥதͷม਺ͷॴ༗ݖΛऔಘ͢Δ ▶ Ϋϩʔδϟʹ৽͘͠؀ڥΛ༩͑Δͱ΋ߟ͑ΒΕΔ ▶ ؀ڥͷ࡞੒Λ໌ࣔ͢Δඞཁ͕͋Δ fn make_adder () -> Box <Fn(i32) -> i32 > { let mut acc = 0; Box::new(move |x: i32| { acc += x; acc }) } ▶ acc ͷॴ༗ݖ͕Ϋϩʔδϟͷத਎ʹҠৡ͞ΕΔͷͰɺΫϩʔ δϟͱ acc ͷ lifetime ͕Ұக͢Δ 16 / 22
  17. ▶ ͱ͜ΖͰΤϥʔͰ͢ error: cannot assign to captured outer variable in

    an ‘Fn‘ closure --> account1.rs:25:13 | 25 | acc += x; | ^^^^^^^^ | 17 / 22
  18. fn make_adder () -> Box <Fn(i32) -> i32 > {

    let mut acc = 0; Box::new(move |x: i32| { acc += x; acc }) } ▶ Fn ͱ͍͏ trait ͸؀ڥதͷม਺Λมߋ͢Δ͜ͱ͕Ͱ͖ͳ͍ ▶ มߋ͍ͨ͠৔߹ FnMut Λ࢖͏ඞཁ͕͋Δ 18 / 22
  19. Fn ʹ͍ͭͯগ͠ৄࡉ ▶ Fn ͱ͍͏ trait ͸ʮ࣮ߦ͢Δʯͱ͍͏ϝιουΛ࣮૷͢Δ͜ ͱΛཁٻ͍ͯ͠Δ pub trait

    Fn <Args > : FnMut <Args > { extern "rust -call" fn call (&self , args: Args) -> Self :: Output; } ▶ &self ͱͯ͠ΫϩʔδϟຊମΛड͚औ͍ͬͯΔ͜ͱʹ஫ҙ ▶ ͨͩͷࢀরͳͷͰɺมߋͰ͖ͳ͍ ▶ FnMut ͷ৔߹ɺ&mut self ͱͯ͠ΫϩʔδϟΛड͚औͬͯ ͍ΔͨΊɺ؀ڥΛมߋ͢Δ͜ͱ͕Մೳ pub trait FnMut <Args > : FnOnce <Args > { extern "rust -call" fn call_mut (&mut self , args: Args) -> Self :: Output; } 19 / 22
  20. ▶ ॳظ஋͸ͦͷ··ड͚औΔ ▶ make_adder ͷฦΓ஋͸ mut Ͱͳ͍ͱ͍͚ͳ͍͜ͱʹ஫ҙ fn make_adder(init: i32)

    -> Box <FnMut(i32) -> i32 > { let mut acc = init; Box::new(move |x: i32| { acc += x; acc }) } let mut adder = make_adder (10); println !("{:?}", adder (10)); 20 / 22
  21. ▶ ͱ͍͏Θ͚Ͱ׬੒ʢ࠶ܝʣ fn make_withdraw(mut balance: i32) -> Box <FnMut(i32) ->

    Result <i32 , &’static str >> { Box::new(move |amount: i32| { if balance >= amount { balance -= amount; Ok(balance) } else { Err("Insufficient␣funds") } }) } let mut w1 = make_withdraw (100); let mut w2 = make_withdraw (100); println !("{:?}", w1 (10)); // Ok (90) println !("{:?}", w1 (20)); // Ok (70) println !("{:?}", w1 (80)); // Err(" Insufficient funds ") println !("{:?}", w2 (40)); // Ok (60) 21 / 22
  22. ײ૝ ▶ Ϋϩʔδϟ+mut ͷ߹ΘٕͤͰϋϚΓͲ͜Ζ͕͔ͳΓ૿͑ͨ ҹ৅ ▶ ࢀߟʹ͢Δ΋ͷ͸ The Rust Programming

    Language ͚ͩͰ ΋ཧղͰ͖Δ ▶ help ͸ศར͚ͩͲͪΌΜͱߟ͑ͨ΄͏͕͍͍ ▶ ͦͷ΄͏͕໘ന͍ 22 / 22