From 75056657841a7c2c75ad10e7619daa76baef34f3 Mon Sep 17 00:00:00 2001 From: danny <540021730@qq.com> Date: Tue, 27 Aug 2024 22:36:31 +0800 Subject: [PATCH] =?UTF-8?q?error=20=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/advanced_rust.iml | 3 + Cargo.lock | 35 ++++++ Cargo.toml | 7 +- README.md | 14 +++ chapter09-hash/Cargo.toml | 19 +++ chapter09-hash/hashmap.md | 99 +++++++++++++++ chapter09-hash/src/hash1-cap.rs | 33 +++++ chapter09-hash/src/hash2-hashset.rs | 38 ++++++ chapter09-hash/src/hash3-btreemap.rs | 31 +++++ chapter10-error/Cargo.toml | 14 +++ chapter10-error/error-situation.png | Bin 0 -> 27430 bytes chapter10-error/error.md | 153 +++++++++++++++++++++++ chapter10-error/option_n_result.png | Bin 0 -> 17846 bytes chapter10-error/src/error1-transfer.rs | 13 ++ chapter11-closure/Cargo.toml | 17 +++ chapter11-closure/closure.md | 46 +++++++ chapter11-closure/src/closure1-size.rs | 35 ++++++ chapter11-closure/src/closure2-fnonce.rs | 28 +++++ 18 files changed, 582 insertions(+), 3 deletions(-) create mode 100644 chapter09-hash/Cargo.toml create mode 100644 chapter09-hash/hashmap.md create mode 100644 chapter09-hash/src/hash1-cap.rs create mode 100644 chapter09-hash/src/hash2-hashset.rs create mode 100644 chapter09-hash/src/hash3-btreemap.rs create mode 100644 chapter10-error/Cargo.toml create mode 100644 chapter10-error/error-situation.png create mode 100644 chapter10-error/error.md create mode 100644 chapter10-error/option_n_result.png create mode 100644 chapter10-error/src/error1-transfer.rs create mode 100644 chapter11-closure/Cargo.toml create mode 100644 chapter11-closure/closure.md create mode 100644 chapter11-closure/src/closure1-size.rs create mode 100644 chapter11-closure/src/closure2-fnonce.rs diff --git a/.idea/advanced_rust.iml b/.idea/advanced_rust.iml index dda0920..c2bc827 100644 --- a/.idea/advanced_rust.iml +++ b/.idea/advanced_rust.iml @@ -14,6 +14,9 @@ + + + diff --git a/Cargo.lock b/Cargo.lock index d197f98..ef0f8d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,6 +75,21 @@ dependencies = [ name = "chapter08-vec" version = "0.1.0" +[[package]] +name = "chapter09-hashmap" +version = "0.1.0" + +[[package]] +name = "chapter10-error" +version = "0.1.0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "chapter11-closure" +version = "0.1.0" + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -359,6 +374,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" diff --git a/Cargo.toml b/Cargo.toml index faa48c7..da630c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ - - [workspace] -members = [ "app", "chapter01-log", "chapter02-feature", "chapter03-ownership", "chapter04-lifecycle", "chapter05-memory", "chapter06-type", "chapter07-pointer", "chapter08-vec"] +members = ["app", "chapter01-log", + "chapter02-feature", "chapter03-ownership", + "chapter04-lifecycle", "chapter05-memory", "chapter06-type", + "chapter07-pointer", "chapter08-vec", "chapter09-hash", "chapter10-error", "chapter11-closure"] [workspace.dependencies] diff --git a/README.md b/README.md index ed46d44..e33373e 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,20 @@ - [2 动态数组 Vector 和 数组 [T; n] 如何转化成 &[T]](chapter08-vec/src/vec2-slice.rs) - [3 String、&String 转换成 &str](chapter08-vec/src/vec3-string-slice.rs) +## [第九章:hash](chapter09-hash/hashmap.md) + +- 1 hashmap cap 扩容和缩容 +- 2 HashSet +- 3 BTreeMap + +## [第十章:error 错误处理](chapter10-error/error.md) + +- [1 使用 ? 进行传播错误](chapter10-error/src/error1-transfer.rs) + +## [第十一章:闭包](chapter11-closure/closure.md) + +- 1 闭包的大小跟参数、局部变量都无关,只跟捕获的变量有关 + ## 参考 - [Rust语言圣经](https://github.com/sunface/rust-course) diff --git a/chapter09-hash/Cargo.toml b/chapter09-hash/Cargo.toml new file mode 100644 index 0000000..0282939 --- /dev/null +++ b/chapter09-hash/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "chapter09-hashmap" +version.workspace = true +edition.workspace = true +authors.workspace = true + +[[bin]] +name = "hash1" +path = "src/hash1-cap.rs" + + +[[bin]] +name = "hash2" +path = "src/hash2-hashset.rs" + + +[[bin]] +name = "hash3" +path = "src/hash3-btreemap.rs" \ No newline at end of file diff --git a/chapter09-hash/hashmap.md b/chapter09-hash/hashmap.md new file mode 100644 index 0000000..5cecd1b --- /dev/null +++ b/chapter09-hash/hashmap.md @@ -0,0 +1,99 @@ +# hash + +## hashMap + +### 结构 + +```rust +pub struct HashMap { + pub(crate) hash_builder: S, + pub(crate) table: RawTable<(K, V), A>, +} +``` + +```rust +pub struct RawTable { + table: RawTableInner, + alloc: A, + // Tell dropck that we own instances of T. + marker: PhantomData, +} + +/// Non-generic part of `RawTable` which allows functions to be instantiated only once regardless +/// of how many different key-value types are used. +struct RawTableInner { + // 哈希表中哈希桶的数量减一 + bucket_mask: usize, + + // [Padding], T1, T2, ..., Tlast, C1, C2, ... + // ^ points here + // 指向堆内存哈希表末端的 ctrl 区 + ctrl: NonNull, + + // Number of elements that can be inserted before we need to grow the table + growth_left: usize, + + // Number of elements in the table, only really used by len() + items: usize, +} +``` + +### 扩容和缩容 + +当 HashMap::new() 时,它并没有分配空间,容量为零,随着哈希表不断插入数据,它会以 2 的幂减一的方式增长,最小是3。 + +以 2 的 n 次幂的方式进行扩容,0,3 (22 - 1),7 (23 - 1),14 (24 - 24 * 12.5%),28 (25 - 25 * 12.5%) + +```rust +pub struct HashMap { + base: base::HashMap, +} + +impl HashMap +where + K: Eq + Hash, + S: BuildHasher, +{ + pub fn insert(&mut self, k: K, v: V) -> Option { + self.base.insert(k, v) + } +} +``` + +```rust +impl HashMap +where + K: Eq + Hash, + S: BuildHasher, + A: Allocator, +{ + pub fn insert(&mut self, k: K, v: V) -> Option { + // 哈希,得到一个哈希值 hash + let hash = make_hash::(&self.hash_builder, &k); + let hasher = make_hasher::<_, V, S>(&self.hash_builder); + match self + .table + .find_or_find_insert_slot(hash, equivalent_key(&k), hasher) + { + Ok(bucket) => Some(mem::replace(unsafe { &mut bucket.as_mut().1 }, v)), + Err(slot) => { + unsafe { + self.table.insert_in_slot(hash, slot, (k, v)); + } + None + } + } + } +} +``` + +当删除表中的数据时,原有的表大小不变,只有显式地调用 shrink_to_fit,才会让哈希表变小。 + +## HashSet + +```rust +pub struct HashSet { + pub(crate) map: HashMap, +} + +``` \ No newline at end of file diff --git a/chapter09-hash/src/hash1-cap.rs b/chapter09-hash/src/hash1-cap.rs new file mode 100644 index 0000000..4cc1a57 --- /dev/null +++ b/chapter09-hash/src/hash1-cap.rs @@ -0,0 +1,33 @@ +use std::collections::HashMap; + +fn main() { + let mut map = HashMap::new(); + explain("empty", &map); + + map.insert("a", 1); + explain("added 1", &map); + + map.insert("b", 2); + map.insert("c", 3); + explain("added 3", &map); + + map.insert("d", 4); + explain("added 4", &map); + + // assert_eq!(map.get("a"), Some(1).as_ref()); + assert_eq!(map.get(&"a"), Some(&1)); + assert_eq!(map.get_key_value(&"b"), Some((&"b", &2))); + + map.remove(&"a"); // 删除后就找不到了 + assert_eq!(map.contains_key(&"a"), false); + assert_eq!(map.get(&"a"), None); + explain("removed", &map); + + // shrink 后哈希表变小 + map.shrink_to_fit(); + explain("shrinked", &map); +} + +fn explain(name: &str, map: &HashMap) { + println!("{}:len:{},cap:{}", name, map.len(), map.capacity()) +} diff --git a/chapter09-hash/src/hash2-hashset.rs b/chapter09-hash/src/hash2-hashset.rs new file mode 100644 index 0000000..3a93bda --- /dev/null +++ b/chapter09-hash/src/hash2-hashset.rs @@ -0,0 +1,38 @@ +use std::collections::HashSet; + +fn main() { + let mut a: HashSet = vec![1i32, 2, 3].into_iter().collect(); + let mut b: HashSet = vec![2i32, 3, 4].into_iter().collect(); + + assert!(a.insert(4)); + assert!(a.contains(&4)); + + // 如果值已经存在,那么 `HashSet::insert()` 返回 false。 + assert!(b.insert(4), "Value 4 is already in set B!"); + // 改正 ^ 将此行注释掉。 + + b.insert(5); + + // 若一个集合(collection)的元素类型实现了 `Debug`,那么该集合也就实现了 `Debug`。 + // 这通常将元素打印成这样的格式 `[elem1, elem2, ...] + println!("A: {:?}", a); + println!("B: {:?}", b); + + // 乱序打印 [1, 2, 3, 4, 5]。 + println!("Union: {:?}", a.union(&b).collect::>()); + + // 这将会打印出 [1] + println!("Difference: {:?}", a.difference(&b).collect::>()); + + // 乱序打印 [2, 3, 4]。 + println!( + "Intersection: {:?}", + a.intersection(&b).collect::>() + ); + + // 打印 [1, 5] + println!( + "Symmetric Difference: {:?}", + a.symmetric_difference(&b).collect::>() + ); +} diff --git a/chapter09-hash/src/hash3-btreemap.rs b/chapter09-hash/src/hash3-btreemap.rs new file mode 100644 index 0000000..5cc1d59 --- /dev/null +++ b/chapter09-hash/src/hash3-btreemap.rs @@ -0,0 +1,31 @@ +use std::collections::BTreeMap; + +fn main() { + let map = BTreeMap::new(); + let mut map = explain("empty", map); + + for i in 0..16usize { + map.insert(format!("Danny {}", i), i); + } + + let mut map = explain("added", map); + + map.remove("Danny 1"); + + let map = explain("remove 1", map); + + for item in map.iter() { + println!("{:?}", item); + } +} + +// BTreeMap 结构有 height,node 和 length +// 我们 transmute 打印之后,再 transmute 回去 +fn explain(name: &str, map: BTreeMap) -> BTreeMap { + let arr: [usize; 3] = unsafe { std::mem::transmute(map) }; + println!( + "{}: height: {}, root node: 0x{:x}, len: 0x{:x}", + name, arr[0], arr[1], arr[2] + ); + unsafe { std::mem::transmute(arr) } +} diff --git a/chapter10-error/Cargo.toml b/chapter10-error/Cargo.toml new file mode 100644 index 0000000..4ffcf22 --- /dev/null +++ b/chapter10-error/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "chapter10-error" +version.workspace = true +edition.workspace = true +authors.workspace = true + +[dependencies] +thiserror = "1.0" + + +[[bin]] +name = "error1" +path = "src/error1-transfer.rs" + diff --git a/chapter10-error/error-situation.png b/chapter10-error/error-situation.png new file mode 100644 index 0000000000000000000000000000000000000000..319eac9f922ecc54aae0163fc188f6396eb63d03 GIT binary patch literal 27430 zcmd>l19xOy6K-s0VtZoSwkDo9nb@{%+qNbX+Y{SPCN?|H?Y!T;zv8adtJhiGefm`G zy-Uwi6``aci3o=a2Lb|uC@m%S0|W$22zcEI0|k7KS3~sy0kOQ0786!=*E{b5XryST z9j$*3zwbU`w@AiSdUaGvsx8;pzt)_>*>x?d+3}1TD`2AvLxM`vl!t?2P?(V)(MgYL z2|G@4AJKtr^B%2Piz(cG_e427%BINb0^FU4y;ooBI!&=J`vnri0AKw``+_#!A}A2T zC=iAnWMEQXSHT2fFvP$Qgi&1aN&UaR5?*l#2SNMqMcQ}a3ER-wAvBV;^F zf4j|Ec!?qIv$$n!43nX1xz+Q+?w#OD3u0Fn6_gyNA9tG|pUuQ0WBk@_3@xpsbZk7lfxmxYLPNut znb?B61AOlv07We=$eWw|$LHq~l4e+IJUlZ?TLDW;u56qw1q}^24>9uPR_0>zKH!$8 zr^;$7VPlm)Rn?dB3xDI9V5q6msCkT*RD)4eCKvrP;nqt|-5rWb`|hh3*}sSN{kvyu zXQ9!*Z{J90TR={<+N8$+ij2+6k(U+kOW(4R3rLH48^IZ&C!!MM1s}JHi49W{lk$M> zCfvZpgo{eKvx)SRqJBdQ@ZVv2f)3ep5Q2Go3)&GK8s|jG6#%`yehlj}}WsS)=Re7}w`DqaTx(N_f9BZ;?g?IRecxf)6Y<&`y}?2A!r!xC&9 zO@(bG1T{9QVLUt)0xm^WWmsK4@7TpWoGd({4L7iI@`}cxr&P--YGxW|^`+*g&CR(^ zUCVz!<=0nM^tO_(npx$aZ95h)ci&Nf2OtmC4q~(tqyWh&gIAt{jm^k7fmiP``Us0j zkTZ_^G+#^1UXq`eH!1^%fi*ohw{KyAjgpdzS*Fm)-W>b=9sHOSwQsK=Bv@2MB?=9Z z-6e+n+o_TSX+d!{+O(`*WgmH2K6b{chm>z-02l7g0O>voHa?O*ZaLrk+wSQDwt}sm z`|&>o@J4vsZ9j;Pb%3F<6|$@qZ@QLV&}_*#6BFBj1vuGpHg}jP)qOqSFSWPR4tDp7 z6NKN}Bg#kX5lVM)p8h$1G~-EIU4?hkRXNS}`C@V%;LHFk4V>pxlVQ8Eu*mmPK=QoJY0OUSmOJdFeW-Mtr?bi(*9;yy<~z!}$I;CY*teN|w%@w$ z0De#ZwA!i6jq@tO7(oD9~?OssXnUF+mGxIvr(sJ`S;pwY+VR6>b8e%yr3y=H~6#`Aj>(> z#B3<&XkynC&=Tk85^@uQvOB^4VPO`Pw?Z(=jNSb1`~iEvw&uBriIk#;ur+TclHNw}w~BZ&(0xDst;V9a zT0%VyUV-ax6wBpygf0<-gZ+j{nKZV>EKRW1ibgd+R)md>Eyg)1qLs}yi_p|u3wnUD zXvvYK|Aw!{{VYX7f`$ixATgZ1euNKhj0(%NSYO!;IZ)G1PuCyv5Oq4v?FuI=yI%z9 z-3thTfQN}u;;MxLuK`){OOBkfU=PPHMEZb%O)g@qH-~b^wZ5O)P5yv9y^pbo?-mP6 z!j6WTwMzfFx-)lR#nj_Tkjl~A>NttC#A&;qc|ov|f7an}L}+C#$63p3Z` z7g-d9maSd^u9#+V7jeU5qL{Xa2}Wh5RmwvcbYkhD`ky)Cxvf?4##h4o`-g%!2!rij z|Cd_mFbO8H?N=s5Ix=S>cl(U=^dEq*p@04Tg_)y_ckgNdj|aWGeR-M*0Dp_|%G1c_ z#P7%W509=t(U6vQ-dB`mWy}(Q)?iJ89T3Ve(i4L#gnf}3qL_yuRV5%oSk^6QlG*!bLWRM%}6 zzWAaarqvA$_Wz6!#4JCOuE&jEF%EDGh0GC%Fz$k%nJwWEw~;;i$Bj?H>=P{BG=+n2 z)ZmjWzFxsT+nfb~*Rrbn8mEl6)R)!~SN`a=Hv!4h1KIS9SKsBOueG$|iHVgL9bL`0 zs9z6R-P|lJ@2n~2K*TB_P;kl#khE?vvRZJ(lmw;)yZi=DeGQBswu zj_~mD1^VKOwHX__$vMeZId-Y4FMm&ibE{r$dzO-&7VA$^HQ~NNL{K*M^TK^w!V^zrwxjj$j6>R3J$DSoY9mv_Fevhg|U*u#_VYMJb<0)mk`%~W?K}*MC zo7mfGg50r=Y#+sA@%pOZejUGwL}>4PR~pNw)TU2bgr5AmLk$b-iuY}fE*Uy-tP*!& zcBhvip!QOL0*R8cY_Zx=9;6!c&}+D`MGq@DXJYs<-$!mU@>D~!xFO2Mz(DGO=)Ak8 zvQkvqf*USMkC>_lCS$d2%<>ySmcM~z$2rYGL_e7sGP_5s4}he0yICU%f%X(yj=qlC z3Qv1!C2;s1O40N&q1@=~SU7X%`s`fb02Zp{^EA`b*%e@rObN;-tX*uit`H~%5l`%m zyz#e<%;;?5rx_bpaYOGPxBTV;(2|1`8mM4hA;lkgiQy4y%G4T3WgD&=y64D%2kvhR2-cWoCB7#K#w@j`od?PSc=XHe{2|Rxi%s;ZT>| zi$|!?v}a2C0L-Xr91>mjV5_0swyc^C-~Dvw7!J#g(fx%_!oVK8a33u`HMd}c1>bso zX{kUU3KCOWskPinO9Pml3b1bg?3vuWB(dgLopvPA8IjbFq;a)lxQ0hZH^V3m+sf$(^*&ijoy?D4^vDZpZD_G3KhzO*1=y~?Vh`V6(x?04#r0#Y0@hj|3De93-@T_P&Ejtu$Yw9prB5HT?*r=QU$Y-}@ay;QbyH|41haiF`-H08IaP-wmUX zN7y8Wh6jc(W8ny3sFVj~&$%~8)Wv~VpHvDO* z4ol*>b#$e-s+)f<)u%W5rMs~gM?bZ5WTvq_z1*R7T^QflPuf)d(b)hgLT0S0@aG_< z1_O^As6CYVW2L37jfSwti$t3JLuSs{#+o2g{mmq>I?5U*ec;ELr2kv;VU~*yypzte z%xp&@so^pLc4CGV;N-|$jX|lZ*;~xUMq2qBx3Nw?$YhLIerN9t4@G07#6j0WXmchW zO-|=`X66a2Hgh*q9fTqc0ju1IBTvjiC?yx>w;lF~nB{w%(axUL-kr6FTPJjQc)nM}3y)M4FW7+Lf&&!!T2R?>YD0Wo?6d_}L+&lFOJp5=BHx2({>I+-1Oqii*E|4&yCj z3P_sk5`0Wef_F2H$FD9F2gAYVi=k2JKSB3(JV8?GclD14ketKC1ZzofFqj=w80BUJ zi6%6_cxuIl98ggB_tRG9V`YKl&9VHEm)q0PAq5+wNr`XIN(u4<_*7TRUnC{15MT$s z)!Le0h>4O^%w4= z8~Q$HqKWcRZ_@ZQ!VvE^iq{C`5C1zoh$1VS>hBvRpjha$KFH@4MrlUoi*6h`fJej8 zhYs8Q<%}Ouz@|e2T7dTn;dihXvCn+NB{}`hNrjv(E%gJ;#9|SHt*bTsy3+C-9#&G% zj5E^n)t-wEZQAQqnU$@2?Ct|pGlA=doP>Sa%W2lZXMP@rx~~5wT=SosE6ML>!Z0bW zrr6khbxL?*zlk}oI)QP20o{?6{{Bq%vKWZbigdB;2(Q4IO5B$>+y|A{l_ar6LNu|4 zMRpl`pYLyNoppxaU^xb4)x%}wesHfe2SNa?z&o}6Y1hZ{{G6yfC(lEX9#a%}0P`Ye zpFK9FM}tgGRerTJ6_ppEGkSR{t!~G)hDy8z1WU=uBi_Ee+#my!aVrba&E9xNw+0Xc zZq~aOcDR$)#~TL<|BnGqkT|%=T|J%3F;O&uuz{i-t$b7=op#EyCgE1^@7xqn%`=R%}BC?6`bYiS=QFnG8n;YB|Fn84T3TRy0LRwQ(f+kYc50)w^9b7Ifc=+^_ z3;jIR36AbMA*hOs95^^Ydv0z<^tn8Q@(BI#@Sz%me@#?3MmB=X#mELrJL1ks$9L)e zMAgzLtCr59^=Er`^)W`Tw%ZD2jp*}53+!Pd^$(tgKXtY|!!>X{z?G}1Ru+#@O>Ke? zysj`j6TdtDZd5M#dva=s2wcRTm=CU&+?p`HzAZZ=O4b6Wer%#IrJ66jv~-^Z7RQQ) zmCewYbG7ZC>Kg|z7hI zZFZt)H@1O=@A40$p{W@BGAd~%+S9S=A^*|_+@w#pZ)Fg|cAZyfd*)k;+9Ihhgz%Gz z<}QZ8I87{79>hpQxu!|PNk-<6zL5fU7kfH}05X0+IGmtsVcw*%v1N!l;0eMKI;=iT zY`&-v3p*<##LJf?A_@C=rj4ug*zH4@-1!*r7aJpcgR4;bh`H$+?xyKM%EoqxoS%ad z^p{XzWw{@S+5njqMP-9HY<>eGm=xzFEM1<-j=18Ln+$G5`Tp)~mMy4}CYz)B2k#^D z?%rigK%ekp-RBQr3>QEykD+6vAD|+J35&~-Aerk|NAB*BdtWfL)7Wr zAjpQ6mNcJ?H^IaV``S;YX26za%zX2N#VCj{Ic!qIx7NJ z5gFXq)#;s1NuD5uD{*#Q9{G@=eKeLyysIJ%W|=(&vJq2MGk4VLEgqD@%%3K0_=_fy1t+* z7q8e6YKt<-_HW9oy~GGoQpteFzHo^6(RsNAMC76<0a@+2pX*yN#7o$!lv4gB?3@E( zgJE`B+&bng9<+5Jwu~~nl0B~}YQ0G>Yq)5Jcu8!waL!|)p{AO8T>(;P7Obp2I{FO2 zU_`g0H2_Yf!EUufu+}0&2n$3K8M4RCd~evtUZCs+K41%JtJDPY9!IHWK^zE4Ue9%~$tjC$tnsn(iudxogBDI}=b2$Evq>HP@MhBsRWS?Qd+d|@ ziqKZ7o-2XC=ATh3Cd@5Flidqoq1k5RJ0xzMN#$M16h^aZ`5|Cp>h&$oBXeGEh?b2h z-;WamGljM9IgNqYZz4c+Ld?^&8Lk)|n5Cg95vd9C1x7_kyZ_KG`%T9gqbNj02ljE0 z^8NBcg(3<9N!$75XbUFa!t<&x9NQZMCn#xm7CT2!}G*m=;8HH*J=MCsGu3)}Ed| zND!8IoN5%i)GEB9W+-6BJ~&xzM_Lq}@Y^?IJ9{ppldIk6nXJpg!ug@O?IFx}v=_Op zo}09<4Dw)icvq~E#D|QPjlCuEK@ugn2K+%X1zePC9NhB_-5c*491-pUa;O+_X*cM@ z{=PY}@O|RgcI!(gg})}RWFL|n`^qYPCyFL3Wbcjx3 z(2{CF=W;T67(l3aVY{DF+kiX9%3?NH^yGBa`6Pb!WJ#-?P+RXXrd~q4N6cWUDL#3D z3O8G{|8GY$A?kEu2R2F4GCx;L@vg2`l)UirN*cbuw7t1AQmQwEdRfPC`>YN`dMYZ= zWR2tQi7yCu&YL|DKy#`=Y)Wm8MbgyV7IOT_gIPZCg<#WM8xa1uMf*&mwD`P4_iz1) z$&b#3Mi|p$?>M60;Z8k1{b~Qu>*s2})Y_X2=f5_7Ci_Voyu3tG(T(bNa4d0jEvYIW zgT_K<%R@zFe_+A$ks2FUT`d8hqbxJmOjig0QQ%MO0fpX7*83ZYnqrF@l#0T zH1DLfVn`gL&Y6S+wizem6cB!j`&JS~<%@+9s7t=o4GTL`*Xe!zIuh`=Ou#K%Y_#hf zzw`WLk^INTFH@=WO^+QI#vYL9m>4_n=cKQbIA?E+6cIjLu;2c@04G7y52)SG>hFMs z?6or4_sD`(j}0lG(9;i4I;FfJg;#2t_5|iJnIa6KxBUmWD>!5}dAa_MdkV_M(8?;t z%1c}E9NvyD@D!hScX6ZQ)<#)Kb*tBb5%ONrl#Ww&T9)M5quWS^q+-?1#|bV5MrOG&zIf6KXn+I5}K%O2$DT&_or{h1YVvNLlgUp3qOS zxIoqBcH6rj2Y&djo{FFZPp8EdQwS(Zfr^U_Rm_C8hf-n`-B$n+bZZMw&lmfFOTr(y zrel<4z?@+nqM6~~ALh=42YOS^?A%=0)wVbYtaj#~dxUHrtMJZ1QU^cSKw@k3R>8U& zt3gN=+FzzeyNm6=V)EdCxkxG{6BmL+8eK)ff}OWC@H9L{?;Xw^ z!~uDYmBjiQvzv#+pCmgcc>W2q4Ka48UuC5+SJCGK?;{Kh(#RlrScKnCG2pXP3wBXA zTL*xH(84_d>Fsnn`p&*ifBB!LDZ z;O_rQDLeCUzhtA}r-i0^Ku8c4qyYH|q+z`Jy4oAVkge^uMYns)6rD)F1hXG7SYC$R zg#XJ(llq49wIT?Y%bPxL4y%Who}RB9-EXsB3n$%b7%V>2+k1>QMoYulO$QXq8>;>x zH?4J=kz%;dPy=#MMd*PiUb8{HuSJ9Ni*T*IxSsMOU;il zjPPH{8Yp^F(%(^9Aq8Jl)5AlKZUp^L18lwDB9dmy>x?tR*PX@DddNR$2L(d0zI+OW z*3X|JN>UKno1IKtTz_s%zRE{d18Z-5U(mk#HjB8_C0FYWlz$Ee-w_xxi@J*LYK~Ta z{_;8kbyherz9pki!1_|d)a+>I059CXmq||ECwq+XOAj4)NIL~zk|`W*Vs3gLm+>f0 z(!Y5x?mlEre{4{Mii-BR+|Q%Oz*}ZrnzewbK{I&~n-vff5)pIe)00L55?aC2m`KL0 zwr*ILdfnJWN8W;hr2gt`Bq*a6)7OiaqUCnxI$l?%WM9jTfP}QaMW)e&7QJZ*T!sFl z(T$Dlwdc*-Q$$tu#ZHsi=U5O@^;8Y}gG_p$9)`BQz8BAv5?>2&a(X!=?*%TLu@GfLq!`|MHY{oPkM;uNEYks{A#Zx5~Za_!nDHJ;rxtCXJhMo&rkeyXcN;8Fj@_a0>sdS&XF;{Znqnwtu!!lz}Qu8NYBZEl;rbr zA{EO#cXfIAuB^Jf5)3{vwPPXiD7g*(<4Xx@8iqa`aAQ7cg;(?ctHQ{_0s@~8nEm<| zC_xT^0qDO2JdnF8&Y8{4Ds{hSKBRt@qy{Wz`e|F?ZWX~`58Gw-K3nXFxI2!^1SK(E@%aNdq+uaFRJLt+TIaCn8xi!i_?++ z3`g1{GuF5aF+L%s@rL3*t1$`rz{4RL*?f)LIlO>wq~*N+yNF((%slq4rmp-Ty`lf# zekZ0U2kw*mJKn-^bd!JC$#6Z!cQzJ@3$SUtTpq5Dfwx?d*~=&kLNR8mm^jQ&rw@xI0&b2K52mdnDXm?RUR ze}-9y)!R==PmUBFdAMF@1nm5dp@)JF=i3ppPgw;-*C6q6YsHbaFjl(oSvWho~^9C zNHnEwW&f1FD~FnSuBkSHeF9RCh+Vo(~h6s(E7@;PQ_&YdS@1d7Kn^HP+@s< z52!V?c<_G97^l4wj5;vZpJ(qmI)LquD_lc}NG~ZlSo|G)p6!jT!zPLji}#^R`|D50 z(BJF7FpR6pj+ z?FMc2I3tvhCcZf`*H|nrHrXkRIB2i><#uPKy1cNP5XDQ#(^0W^Mid?v%V0S$R<&%` z_xGG7cE&}g%@i8`43EpoJg^qAP!>f%66gU0;oyTWZybu**$V7Df8OCo5uu@oFTipj z#C|~X-_K>O3W*((46#nyO@AM=rn2OpxF}wNakCbD>f`z4}fb~Zt8TWH; zXM9gAIYY^w^-Y&vA84~ds?y%HU}ZjnZ5gn?dbIxPhFDlv4Zo}{V!~Ir$jM8Hc{%kX zs*=lam^zCwgm8!MGuLpgM*bZhz>A{sVA0o-lJX$M))Jnwj?-3N2iI;MuGu7~sO9G$ zLslQZ0qya^ABc!a8VV>aJ|Fo`WJ7X2xM#vuO=Cq0<0C^y`7&WFaN%Y}&vxqhw# z#EuL@gQ4d6IATAq??b!{nM*5*e8f%M%pvZ7%o)6~p<{VS$cO`^X>5NT7 zNTHEK-jteLj;o^&8O`eps)`!o_=1@^L6#=39tsC|F5AMwQk7f_=z{HAP(PcXXg?}8 zT7x?9S`8KkNfKdEMc@*nDIj-Cpb2`5+t}&Xk5?lrWNnM~cF&o`P5ABJcuuh6-F2|? zf(jfUx4SD6Na??ZOO@H!>WDZ_UaJXmqR*z1S`kyaW|;Z+2?CA~JIjOu*WP-C)jXe@qZqjdDSvIR#4ufU zbT*FJ-&@5H?$D%8y;s}WWOJNBU4A2x=jz=*7TG%ML{vwoazryS?2a|ckvBCP>@5~L z#!zcuru=(>+L#(066EqC{_`6OlMViJj8TaoZE-G=RoMZCj$V`vmiX?EGf-s7psHV@ zD)21j@AbViq2yMw(iV_Eq?5}HOlo`I1mE!s0Rr~ULGVp&8LQkF^XOuG5g{`xPp!Yn zhmu;C;qs5Fz`}KP<0|-nYHknq?$p0*4qtNURJ=Dt#X!z<7nLQi;O3?4bfA1p^z9nD zzb1hc?mv5SsUHVcP~a1`^&ghLG@=RkS@+)1Ejl3O2L1B!-=J-l<6+}7aw9~$IADzqme3>2MGuf{5fbuAYWK8 zGTN1AfUcdQzW3BoOFHowk+um{GFE&v#p@Xd==|@2i zuCDqqJAf4mE8Uuu6J^ZS(VdVG@_N4l1h4fRvLQRhF!jk2f~V{%=C9IoLiA18W5Xm3 z-5nC9)@gx2Me)A`|0Dj`m6Si#F35G}gJUioyg*OZ?dW5XmzSFpkd`O-NGJe`x2}DS z2-82=fo!1IZ){)yV{D8Inq038GTVTU{>yz74r{Wm@;!N&iqd{>g8Lh0O8LPsoj(%c z@(}%N*gX#D>AN=pU9}FskLsZdIu8d4GtSmMPsbKbl~sb=h;Bz(Nl!jw9!)ZodbKB#0VMe3Ne#EP(;S#c#x-^Q^5I2_f&~{%F$I z_3KrCT97kpY_99d2WS6k>d5#;3Y~7aL5B7-QA5U!kInLNgra%c>DYjiTAjsj%mh=M z7GRY(@?)TfMUnH-<(kx_L{U%gXe!C|pI}5_XR^;*vt>f4e2e(Dop!K-jus&ZA-Q8G zz3@goDK$+=K2UZ~K`Ol=rYub={Fn)q;ESfjIoD`UN(uToFp8+Yu`4OI?>Fnz3tG<3&IQLF)C#A$1K%SkKjbilj-u(6mS@6W zV{2QTu1GA)eO%)~9PjRR(S^L_eLsZ97G0vY1_F;(GT>#nbbEY|fkclKh)cz24Ds8y zPGSRla~NO+up<$8RCz&w&s&1rNV~kdi{}rmiWR4#4Gc_?VGDLn6Kij8Wzyk5!t6OIVgv)7#$R=hkR3tc@O?S_Z_5WloP=GH9HpbEYa~>Ndy50&QR0(KIzJVSY6P4`;PCH6tiLljj^h zrnjjYU(@VgFUy)?r@&_Pa(lQ{BaPfd%gNdNJ2oY1=^2WfkAN_F%|-mpLXkBgNyGmn!(x=2 z{Ed8mLC~o(6wmES6v&fmp`j8ny8`m!1t5{k>yV@TseT4Hekc;$x%F>l@4K z-rQ{N+P#z=rN|3x{+l@0*~kwFH2WLEW>`*g<9qkW&!xhKZz}~WoT+03Ih_;`rno9Th1gesL-#Y}CE0&sF(XOPqyl)XA0AZP$O>w}) zb{855{$sFN9bgmWq(hvO?(PX$3bYiVlaPBpCb)Bd!!4jDK_xfszX=W=&Hni~MMsva z`ccQ(BPo+paxvL=3!6HaM(+tc-)urL#TBvyu9DZlI}co zKQ>i-T>1qcQ?fFslnPH8`(W08rcZWXqee(0Y5v+u!_HY=-Z0j*8CA^=Vg~=6U#sSV zDYB@Cf*5;gnq^k@2n&K|cUYtt5aLX)GScK?qw-311Yp zVT`y;&s(phicML*ZxQ=16$R#ph3@X|Ba0ftKP95v-amw0(0gsPIw{sj?5~h?*PgkZ zr)Wo`Q`Sy#L+tWiTS_S8k6!+mVqTn|uR`6s0_kwNgNwA^`73S0eGoA+BJvd)uTLvU z(a0pGS*{Ty!uB7`RH|s=v3PR~UPp8b z!=`DAN9KF6>d8^D&@`@DC)sl&8BW7A%Sz}G<(ciCswy6GMiUETKebYy+A?246}v4; zt~2TjQ5URF+QbUCm!^E_lb)vo06}uziGjMWE~+%)F&DoYfz6w5#B`*kSo#GSV&L#- zu^lJ5nZryjnRNailk-@TK?C;IO>Z?((6ITLS1|d04uB#29NP9$C|hv~`+Vabgoxfx zVb*oOhua{P8>f_IsuTzLa=ZT7V|SVeRW$o}!q2f+<_|{P?2j76Tn9e{}JrU+Fi zEcUE&QAcnQ=T04~6GI$j@x}n{g^t*rVp-Ho(G$Nk5!LZ6F3yiQP=rhk7Aa00CpY8d z1P5cl*x*-e)v|5EwNzNfB72IWc{Ie-TYsmc{LNv48zEQWlgH?3{(MXafUuI{Y-m~* z6%`}S$r*^ZxPC9>;NW8kY>s6(n414psP`i<$_e3S&Ry^Kws4`fqvy|v(jf|Y%#$*l zj8=zn!X|QGw$&PToZ4k*JNUpYI@c|cAPdnTVDv-<$e==|2V&IjDZZ=Pd+G&8n%90 z0IC1tO(eyoJxlKlhe(nU<02K$VIhMkHhQ=KhUWgx;1|M&n-I*$*bw|HY2k4bwx>X- z?=b0~LeHEIg_T(QXnDv8M?D`SAczJve8}b0_bp5@cL<1^CWngzk&bR)5n#)v)+I3DPY0XgCTpw>%#DzW zqAZx zmYRXZ$+)AOoI-`%ti&Pa*uiiud$a1z4}j#u`rsk5I~OUfZyemmJyKj3WWCmUTa7LIkX%7`=aT+)LM=2)1^ z(|-)k5_}$?28}Mu9v3P+!xQ;=-*KVju+2wT)(l~N^o18GXyE;ws7JA+TC__`i20Nz zXtk53hBB9kUtNj-H%wTOG^+Bp$A+(5Ucw(>jls#BMVdqSW&Trzi<+#Ci5G{kWJ5pb z^ce?1e$pT^cJ=^Nd*Qi|%W_IqF0MN6+(o2qL@@iRjAAPhM_z=dcKr z%&cY3@Qk|1lmWM#Q?{)+xdGi7I87pFv}jU(imc)910VE*DmCrwXxjZwvly@aW(Q(u5JeHviVWuNMpf^LoQ&o zMJP*4nI!hy935p4pEA?aTXuU7I{^3%`bU4({G!|xj#iHZa#Walyx+Y-`bYdB$6tDn zAIgP3F(z%V>-4R3l+VCrx0e@zU0IN3G)F)e`S6$7%Bpc55PatBEs_`+basKa@^lML z2F%c<-LGnte{2`kVJ?hSa-X{E-reqK-Q$D9-_h8bC7Z?^krt)6?*f=Mh#Il?`V)^^ za`npp;czk*>GE@OBF7GlS^zyNknlLpUupN$nI4mP_zP2dqN(86O6Y=sh(>mwk^TMV zmwEAvPqOJuc0r~_VHdCfw-jd8o%S40s&`T{3OaT4Hlk6;Wgc99jVni#qbj4UxXT^F z0ViwnpzcTm@&Xp_E<4<0tXv%)KJ~=Qa39?tzGy-t35xrZUl_JKqb8fRGtmvh8D5?? za^2WVfyUy5>x1UU=t0<42jPV!BmJ7h-IU2@TQC9GwsSjRlHR8NcXxo`IZuA7H{ge4 zm3oMBo5wn#{)IsB;nGr!B(P5f{QeQSTr6~VwgFGy(~gVz$TvqtN?LyVY3^&)fO9|K zbvl_D8OpCH4O;Kl()<`O)hD}rosaAv0^l-bY+OYAku$1~l{eH7QJOGXyEGV3J z^2neByMB{dk&1)T8@mpGOB zw8g5U=#9sb8F7v#CF}HhLQYOrU5yr$kDq}8m z)eaA_1n7nx9}K1fhHwhBwb{K+Y$(47i52zwjwnFy9>H?mW_#5hv^z56mibKw|1Df; zY3a@1MewqFM76`8qjqF4SI|cbdl0!2X$(aG{^nlysKw^foqvHvRMRtqCU-Tz|NA>k zd%scM6epy-{QB=0;{)mhQ?+lRg`t3@$mocXj5|sxDH%0d1qfkxK`w)~(q=+WECy2c zi;Okf15pEwdu7WJHTbZ9pEs^#cw*zKE-Bx6Ht_tOIzUh+63pQ6M_fZSDLQ zXm)^?@A?x9Vs`-8{6FZj-cjG}pl!qQ)@`2^D=XDs`z|@Nve0>RATRv2oq6m|jZR~m z2RrAnWK>f-xkd0}G*!NAR2cJ%i->sIyuc>)@Q;tlX%<=-UevA8uKHuhd&J2FOQx8! z(~ktwGD{EKr3+=Mpj|kYwS0Et{ zWcJ`Lp*)(TU2Q?-YOL)a17SOfpv%dv*ATX^B7cEn1;>FjRlU4O;Pbkuw-u@6ni!;u z&BnEGOfIo?qVz4C;zmy}l092MIXd2H%>Q^>X}J1t0!{!A6HC9S_@08#`>o;gn3}8I zz5HJ&(YP1`O#gIPO+AiQ8<&ki@Lg#tSAko=nzg^9TAZSsB_;LQ{Y2G}_yRi{DLX%S z`%#Rcu^Se}{Z<-A%9H0bNhe)hv71eIiaOAHKNsT89!|pnIhkpEZD0?aQgLlJN$pl= zOnJKd#Dw+7oFemugaes#VBCsQ*E37E^c#upr zYK6D|BG~Hr(l}6el&rc8ThIY#ul!O?0J^IrVBsCpEf`0V`(zvByRQ{i=J}z?!G%YR z3mnk6ca`3sc^FnLXJJ>H_xFDy$?Dr&Bwh03z(p4%O4nKmE)oPaTTf`0(;wS7vv%PE z>!1HLH}3~Vl*mZ-sjp|gNI-;0%v>edQv^62tkGw88NhWpQ8G2uhXy6_f&~qzHJjxw zm}gObc6tCn6;5y}@kO&Ho6&`~mn&*qT&oCnB~5bF_Iw1+d}3ECzG3o@SiHkmF%t*C zz{U-a|Bh^mp%1BvE7h$XOI&mvBRD)>;c~Z%jEuBwf{w2ZK=tjhVpPaw4{q^82^=UY z7b7K?o3DTh6<2{C7bM%FshnR-WfGv2Swv^Z{yfr>K%%L#oZ+0QQ?D~;l!Qnm?j$;o zHBC#|EgAprp;eH(pNxKvx!>*KZ3KoGGx;?pSGb3^_yJ_8G}gD*ZDReVY3YVrpq-PW ztQ%L=!NQ$9|6yQuM?mLaIXYUxeNm*@+5HecH`&=0ja6P3*uIQzf9Pv$tfp@!Gjno= z*r=7&i_YNPrp_{QvNT@vnBF{pIMUG2RHgi^sjd~)H6GBu(x3i&Tt24KpAuL{_Fw@; zeE{|F+^@*W%v`Z2=w1bmg-mqL?XFXUe|`NRX)eZgikFp~B7|s)hx`G#V`X7!F|;!? zqg@pX zNm+TZ=I2>uxELntw%=5okZQ9Q*_##;QtE1I_m1GCyqD&xV*#t4caMm9@95WVGjbOU zOyu~!AAQ#(h65r?lV{B51UjmOsK%#a9*=;7+<~WoTJV)RCpfdItp1gLC=CltkNsHw zOCByDn+>jTDPUi)dZ0Ztk34nX*%ibJoF&*PTvcnL815!3tGD`OKF((86~C*h?>Nhs zyW08)xQ2%b^fUOLsFGL7^G9gALmj2YXXNU`46c95P@EM>Vu%7bKy+rQoq=rPXo(x9 zcuEaFYuac7fzlyFSGPvWk_sJ1A+I4vfDx6n-~MuH=4Qvpu&yI1ZL#`Ol0&d3-!#41 z^|8FVS~=(T4y(l8_3lT`VON+P+^3%Kl@^ix=!g6z*RxLc%WGf+BnykvLOxnvI>K32 zi!`E@>b2(+{`qTkc!Ub6c>+}DI0xqqIrT+O+6^4;QwKgSuJYmhzp|54feU``K-?Yn z171_mjip&>z+)OgllwtpRvBYN1dGe$+Ny%9Eg@aCoq?wlij=Bl=VEj6_Vaw6;CMOWEZfwwD?&p zv<_nyLr=0hlohwO)eTL>X=gGN;g=Zs?+-$;z_`#)Pf8%cogg$JtgeuYrpMP&U1KGt z;zYZ-jN}ZsuZ(r|H%$;7AXAmtrU?=qkO8?R4{KaVovUWctL0_ojho7;5eU-A8XOcX zxSGK_OtV=yjOzT8dlnxQ#jA7D?ePLZOv53ZBkJYHF{KGJVwKg|0>Z}2iw%VVkY-gIonrLgf>bdnnf)2ide_)x z66b}CzbId6XRXc*XJbQPNqlmAt{yapah6qHUha98*aq&ia~v68|DFBpXtifG<&1_a zT71>BIL^oI$;*snETYvNrOVU}89FZa$KF#x6!hM4E)oLLJ6Pir28D>inSSuEY3-6rQG4Zqp-FBlbY}==i2tc44pP5=4o1K@Z5%ig2)qMJ{%eHe@nwv_V zu7&+Co@uuqYlsp3Es03WB5tGN0pv>h>fsk~@U)R3&+0nHSpY zMffwI8GZ?tl#&3!y#F zBfD0{4$E_M5;i5WCXR=zge|h&z6_nlyzt9$6l_nObePYLPhNzjQ&~>%H{{`v`s%g< zunlq){s;(+743uS721!;i~P! zyd}}|(En{~f;D*zc$E>hlC_%-%R+U--AP5<*Jdl_8?=+uLpyxaE#baeyYgHstfw zPW$*$v?dx2aC%ops)sV6H{@>TYXWeCF9u|4ht6MC!7au||&O>iVdv&O%%U=hzMa(Jr?xt)PaMR!wkR2f$dZ?wQ+_%#L%J zewn2C$4%G1mW`+=x{U0&>bVwN)59y$6qoNBNLbuS|KJ-<#PaK@mm|OOw)ZE-sU0p=@iNgHsJeW{Ew$Nywv-{px>CTXrp`*{eX~nd;)P}@=WzTVI zFc2!mXi=+yUBVa^TZlU%J!nLsD4OTu$7wZVrd1+s1BO2&Y#Q@vd29W8WSlYRAb284 zM20LTM`e1ZwlN^Ucxk8w~XmT^3J`D~TeC zl%vY*@L%Pb53su&RM?PaI3NV412j@rreaR#hpm7A5u+*+gw6B4tj~5qG6qv zadcTZ1%;xfB6xjYFO;PD`BN8@Ay8ynxZ)U}NH^?v;7Bq039(Oq7QFInBd`QI0yF>TAn3oP)|=8lWSp78>*B^Z|j_ z4Z963?WKA#5sc=)>$U1Ng84bmDR%ACCXIlG*SxUN1%MgG)JU1$%6@;0%T@y`WyO-a z8PSDKY13Qo#n!(Ptdx7-P`0-Qb^8{YR&8MGBPyQdB*@IdQklAXOI+X0#@773pm@_b zoL-17kc)?(ss#nw*%@`BujT%Axc@ct+T;1}5nB>xft5)M#Fa$XpUz@i8n<)@*ip-R z_5ah|SvJMdKj9h@2(AePcemgkAh-l)aVNODF0R2{gS)%CE$;5_?y|ty=YP(pIB)jN zRP9tv^>qLG?!K=fvuW^>?ySmNd&+3sTyUyfxuFtLLBT8&>#)5i;Kw(4++JJzR9IYh zK%yNLzK!#(vD?|jB|gPkZGmTkOjB98NIQfedz2|mxK*&lJ;<^Kf21@S{O$VgpBM;& zFrY)t*;b@0ey|r}{?9(#<5zv53$YVNy+&)>g`b4+AYlAcHU;SZ-2?)mAXfNt)$i96 z+YKlvf-NC)lR@L%22MJ`$qb42#}KnzdF67?=%ikY@$x`_Z|Z4fY$}${tnNOxk?YqY zzr!aS7uR)HCovtPJWMXD*kY=l-k>0)@7~%n{>D~b!oW@Krom$tA8b%%wPy&NIWSLV zmanF#8j_;Oyu6-A&EsFG zy-=)L93LO@UMl1t*Rf?Sk3Vr9vcYVF-0r30p<;FP2_~Cy?Z)?;bzLTj3lMba=db@4 z8K@CEBy}c) zHW&a)m+YE-Rp zI{-wSP0VbdVb5AQvmY25h>%fZlN+Vg-|{!=LKX)gitk^uhw(F@l@Fl-Lfv@ zqIZo4e-z8}Nm+*ua8y<9hRwZZDx+^;Wm42wu8qwEQ%MW{pt=GP{E-m)rDSS&W4w)$8~iVgrFd16MWjJA+2oB_ni3>I^^qMUTm zfHF5-%m|D7jPKq!XyToz;X38GzTRP3BckaxKSqfOuQx+t={|t%N__Q~<@;LaA-s{J zB}7=Gx5RK2xJ9}hF$CypMCR9ux`I62Ajtk?8-nTyCkf)jV#23Z{&xz2rOZ?3!AHlAK9R;Q`E zKR3$8I~%FRL{qC;rVZb^xrxL4$+>7l&qdp2KZ&1SW+;9iwqECT>CU|G>w&-8M&Ub> z&{h)gF87YA(OjL_lNg8s+JT@>=*nWLNYspi>RQaJ$}?Y79yP!O{N8wl2XjowvXWZ_ zO1bw0NXnQ648Bi%2i@jT_&Wu=1o00Em8<_wx~IP~2*}ft4wK!&hbdy`cCrsyoHk$S zY^aBssQC0bCyFWl7LKc}KF{skWp=*s2rjfR&g>&s+t*a4*G#l~cE7R*s%*MD6IYq- zN+H8FbxpQFlsf}@wkneYz`Sh*?>dZ|(qR10i+J|<3bd?Z8zks{cR&Q>_~!e^1=99< zVZudas#cbegYFg7RdSPh9JjNTZ_5_vZ*&<&MEWd0g|4$Ky9d!VAy-}KFG9g_i4Xi# zEE{=xhBS{2QGx5cln@Cf_{(Qq^L`fGa{pz537N!YQ(OYPngdmX`4PhHtEtexpLa%j z2Ovfz6dF*D$Y(4q|E2vNrc}$OGr{GUUZ1a8r79ETn9eIWwAgM8$V&yi3!J))eMtzJ z+YV9RDv$WL9JVNb9Ga3Aj9ce3@`rx7x$kn$j`<6=>#rLTxvA&ZG($KjYK-x&ZhN`* zxRH@%UB3u#VA32GGHFkl6bMhmEp#w4Oe*(`jX^lhAew?JS%rrD6 z+i|M9q}Po~%a%Hny}q-hRyv;0a)Yt+IX1*+3+OQUZs#HgeK(;;=Jz3~QT@bINuF-a z)fQYdc#>qS|Y#KdjBup1`H%TW%7&Of9*dH=~BDsQ=h7P^?i+~dLUA5 z`5X0*Qvr#KyE$!l4kDm7Yj!E?>_6JQ?sq@fq7eXo<$FVumTe|I7<#|(`(IUF;f#J| zlzY5pa=tsP7IMx4De1h09~~dBKizoNFv6g_wy#0oGCZRr5V_!9`Eqdzlq5~Kd%TiU zF)5;8wrh^1n6!&QHYJoYb6N|bT_E#ELM!=W{GIfl5{dX*cN*kJ$`btr#m!FRi2a7! z>>Zb_>(<6STiu$de-7FyGB(m@-Ri{5$+dahruLWllW8%37pg9V{*_X&$7f(qSX$a^ zhvP%QHmErNH}co@Y6*8Aarfzaqe(%e8qCKAX!h@MHe(3?z>xU#qFUA(v+aqp!bZ7$yqAM+7LKWFl-IC#sh%uT`~5EvH)~fsoTM8U z`)6e80c{}#^?J;odS0ID8VdyGQ%A$3Nz>C6Rsmq?K0NH)|D_TziCfX<3X85>c6XX_9V!U#oUB~t`E8@F+F9Kk zSj0YED>K=@S6b5LZ3oYF^iDO;(aut7Wd=TGWMs}i#mm3#EKGLus1vfy$a|EX-c;h? zM4U`6;3mY64ry_B8&48Aaa{T|8c!$J*HaU!ceWzYn{crXP3U~dqiAVuw3~Js9~6lx zUY0fa;Xr&_TPv-RjH*aHU}N=O=`AgU(A(AxL=EAy(>8V1c^gdTg1zvG_|1?I>mT$M zQdG2uMS&_b7fzxp+%-n<<^?Lb;9@3$@*Kfl;8O{goG{eP(*9T7hF2)?dj9EHc1nK_ND zB7%-)m`)H`Kjk9HojYVGPbzT6`q7z# z3rB9m%*=uEk!<{gPt65x3SoL;gEIwZJrW+@5?td~ftoY5d{4G9oGb888TGzw%4A*@ z82{;sp>k%(Hw$2AD!%_cB2+_`5r~cjVKH+1yyoiYwu;T@Tq|jS2=c1 zFj^*V2>eA}OIFQaZu6&R4{J`?c(nILAS!xs_7%^#rhM5GY9|7L*~qJi{TN75=W|2a z(Ud)~;lvDxjP42%3at`Tv*4&E;@U2cXrE$ncK(>GxnH?px99HMplETtGI{7q2TWtY zd{`+gk*rS~yT}LzH`7v;Vhj$TSt;SVB=2Sz-{tWH%SH9y3SaTa72kB&k zff91NL&J`zx@-!SUv#OM%8kUn){m=pN=#%fc^+|huDQarp%JV3t?YhH^WxM(sFYxc z;ZZ{aeLA)?tSaUPm~v+hPA|*AW^CrS*1nRob+dm8jW*hzuor1J#Y#{88mBaG!7Ufe z&08AQv&}H6t;YQW4atHCtPZwEMmxMQc*KKSn+R0P_&3!dw7H$|7p=C_Tc&_W zf2p>YN%P{5n=MBa;WpM^(@8zi5oLko!Fx;syr_7OU^+dlws}s}1>$6^KaDP1@{;K9 z1(50g$TZ4_@FplM#SQF=`RzYf^}E0Bt0N}PN22!!65RK-(UF1b5sms>(P$$B7H|3Z zZ;hzyqx6BWVQKHjxb2#{xwR4Iben;qF;J!ISQzvcWQp?e<(siI?QxlY>vtU=|I(@7 zBl^c6+XVw3GLKguj*lw)TpQ7flAGtJ`&eT!lE`Hhpw3V;_IUWz!by26b3YO9oLb&D z&@L4Jem_Lp=Eflievp#o4aoA}?DID>`vW62JUQpWe}?z=;v1!j;r{ew&h7b98b%7b zwnrv@)MyD3E1jI-0jkPkKYN#P*1fnQ_VS6$8>jUnRI%;@x`~YrU4koqx4V}z$vWMZ zoO{!(N<&>MxlK6n$>j#Uon@qDF>^5`M3;k<=0Sj6!jkAmwgyKWI5Nl?LOLkuuTyk|rHI z_z&6iZePFBm1)l4<{sfR`!zN`y$akza5xY~g`d8XfTU0VH7h3HKWSMhkAacno;epa zV0S?KCwsck-1qm7^MB9EiKa=QkCaqmk5-dXkHYxllhm{V3)Xp#V>wH*e%Wz(3!HS!y!0!!cM%1ywEUA4WVBY}E0ru^bncqpqxTXakJwV*K%LQ2qtW#4G z`L0v8`-a>JxnT?Q!~)#ZxaAo#at~BHTep0Fm_8ZTyC*FYaTOtwPwZTbIupgkyj5hk zUZzdoAXJH;SdwzkflrFb0&C?;7)LpawU_TdTPhbzYMay+O!h-vul6;d(>h(VWd_EI+9^j5!QY_}-^`jZuBtY3=ARa(Y08 zMR!NZ%x=Dk0VZu(KXCr+L4OXkh5q@8BU+ZxkeujW$3|>Bj+H{2djRtXJap zWYn*iT9s=sSi}9iXII}Kk48M7rMN;MxR^N-vlQM?E3jJ+Rty@y4Ctl6=i#H1&zV1D^pfvbu@2VdIixwnZXKGbCjQLCg4z;0H+2BDZBQDNh-c0Ar@ZMEOHK9=PBJkG*VBz%bAOUdyQjLy(lcZ0W;U|gdJmX( z*<3k?Ln9QiktdR|(&_MWW7x=*NVPs^A;H3 z09O?!E%`GrxWJ-^z68kJQLfIeGM;U-2wm&Jt!e36<07+l0 zY6#4*X*GgnQY2f6K9d0?rzGa?+CaepxZ{=%;IE=jS(}^cv!~Bsf#EVg(l^aFFLJDc zqM}8D9hUkoy)GHE;<#!{y_ho;_*fvEa04NGo{o9|3E~C+B@K#k112$V?ovYo%FXk4 z$FZc{?EncQl*JJ%>dc=Ji~j#bW!;{ZI$&;2T1cY>9KWFeR0+)IWS5R|0sFv5Cq(BN z{*lwpZRk4s=n9pO%Pm_RT#)EDl^Q`9wziHpz>;MQ5Shu&EmmEnz@oLB+?UkR%VM;| z!%5g~d(1z5AS_Yt*-!jem3!D8R&r8llUgQhAtTqBae0)0nH(HC9pi#*hPBxCXL)$` zCfd({RVLbe@Y?MevZT=NaQjCBVu8ruWz@R48EDE@D5NeJ(F1rHbLMichTC4F!&IvX zWs)*rHeMoB`%lguGBC@aKP4LsBCi!FPJG$ zvg=%MUw3e3X}fFZD$;qh!%E2I#2oFuh^?14(8%vjt zMasnJUH&A|D>hrsh^4i^zrRs?^T~HP!7(gV#P|wOrk1IXXPAI4EQNh=L{CQOm+6BfyJE?h_i3WZBo3M1Q9Rq}zzoBU!yKe4lKcG&Xho zQ-;_QefLDpq+Kt1o~)QBzt&by?&k<@9q%+ThT4_?<{8{VZxRI1EoMP0Pj8hE#YeXO zMbQ3Ry#szPON=NoY216Yzt6EE)m|9>*=E%|ULpqOw8bN!MV*}GU1*3jox&g3gZ>bj zsXV6~NC*=w0FgZ__7E*bd#VtfPp1keH1#R{wZC3$V+($iK`>JZy}z#iBw+a&&`958 z3ss$N25NJ2TkfVXD`QaVDSX2X3TDZ3?O0ST9xOY zb+X^xOB?xgjSHPVyND{`{TLO-Qd0!Xvxd&FL=z8pcX&F0mGXXv$=8-VxNTlK2`>4( z?3+AC2Vh_-=u%42Srk&{OU?ydcD0ay3Z#L{3}pF?CGs29{ua!)iHwdzPRC!r69Qu7)VXyvCMvAhz(KB@H#fRa3IGqtuPCO_z%du6U1oSUckHNY2#2D` zH8e7r4GS`zKUJ0ZDz(WHw%*Kb{3q>K%wUQ1M6J*?sf_j+U7}U^+wvj zgN(o=fMjSRN2&&^+CWqplm!5~o|#8>K)e)7;9XC=fj zrjI%^QMm?y%4s{Fzb7j#{j=L*C1u%65&JTCYJXD?lbE!c;I4+?OL0VdJxt|mQ>_1d z{;sc4xML;jQHZiTKE11vnv?_b`JG#8sl5O`_FW^OQ1C2QH+naV2Gr}jl}Ud@gVQjv z?1?fr+nEJFlfe@b1uvsRujC3-aaB!~3ry+H9km%nyUX*yqW2>Q&%LwmBS|ShJ2Nn8 z957r}yXu`YgW*1)?{>5^6fjSAm&meuHnq|0ug;!hA~}21!H@Pk=ws%h)TeG6Ajq z5F>>T(NikjYT6_U2L?w?r)x0PHc1XuxUI)V8V$;{DbUbRhsm&nWaLjg)T&nOzCXbA z)OkC>BkM4Fd5`e%ITHu5(vym9%p0YqrjTJB?MaA1ZMdo}3Ti4-$M0Z*V5az>Ngb+l zm2!V9Xv{R&D%OaqsA_yWDWda@(T=1+4kl4hL*qDST9_XdMw?`@58OLB%lO2mb=K^? z9$dwY^3kq~pFk=c1s|6aJ6q#5{$+Wms}Gz%CES-+o;jB@RICUc7VZRfAty(RDh9)Ewb8O`KO|>?FiAp;xJlG(krYyDR8rf_Qv3USXmq0(j^J0{-8lCX9cGGt3q;k%xEb5@_4qA*&D5xK0osgt}OXB>=d9;8m}kAJULe2`iW)Ge<_e(Ql_ zJOo*mWo1k|h8qc43sGpm6|0NcTg|PEP6O@W&&KsX4XrL>j&9>cCuOD@HzE~>`hpqi zQ+C7XeEmUj**%JHmE#{4EvbY=yae~X!8s~w+w2%LzVM`imNpjD8ZH&H+S!T@pK{3$ zgc}mHyOxDS|N zXVxFRIPzv5OPXCf7;3OLe*#L{o`q6TmK2|~!jkt<(-jj*TgXPjlyHyCELLCgJrYY+ zBUTTl_c^>x45_!}c2UnQrl-8nh7n&qO&gVzS2k*A(W_~WLlcKBVa;>U`C> zuAOJ~dW{k@jhX$t9k^X_65g=jZa^oH%WY0QGvM<|RMKuY%`5UPn+QkrnbbZ*-hS-H zKb?bjGFsrD1sli^pwD!zZ7pIm1Qf<(4^~D0CoDId=}R&iDQthz9k9)6JM4gO!B=F6 z6SYXQ=o=@ILTXBB?B6%m<3>j=WD65YfNcAGzDBReV!sem*I;%`hcM-MuHERdj@|L@ znKkig);R$1WVL7+IvFJ!;v?lL06KHd`LB1#?Vh{CSR>IfQQ+zDxpL&c(p~f)nEb4` z=Y_?kL&0I>`Qy{;uW{x@YeV8jt2@`zH2?vBmmWCke78f8W2?hIE$q*3I;*BCbk(2$V2NTV8P;wz~>nH&OVFS6$C4nFQC%3y7DC0#f+ZUFD<2 zzC;m($Mb~qTYbT>L(HyNbxEa0mnOn0o+41_If=l2w%)W3;{SvlUBjNe6hf*+}tq=0>*{}XrqSyo$S)q4=X#Yy=80~)bpe zgkF?~I2H?YjxJPOqNeNo>jr5;>vm;_x(@qCong@VDB96yOF^INxOL57Sh&)ULEXPwgAs?|_=K~XNRvGNxLHD1us z(Px507|FwQo9s5ynH?}Q3+yAv{MofklK$4XYPaIIC6XkH@(WgZp!7uZR7ElO^X!F| z=E~l}`D$x46_4{vIm#wwL1ulF$KaY6Rc4$MZqoPD>zFX;o&e#tSP7&2s;ZCF#Yz_^ zb#_#$w1I+slL+`KYEd+nRWy-;RhP3GB}&1;tMA#6)PR2Tc4Xw`^Wkf?+Ns5=B1+N) z_e^kgBPHm#kh#|ZY}6=0NN5xaz}*nHB~IrF&S50(`geeXV((9Th0czyNUv(koJGu@ zkgs0r6)6@bEsa5nhk-W9;XwCucElWtVn~>|$CQeSX*yVIp?uZwlu5I7#>6eBPmC#s z4G|jBX^h>1v^Fo#yGEt;yK8XRhP=S8r?u-;{zLtKkC=aNS~FesnKsdgmK>U0cQp+| zXEpW-`?6@9!tZ?;{;M8=?U+a9ynI!wb55qHsJu+zC>pfP_igCe{2iD92}QEcia0oU zxmIu|LOB6-sZK9*XrzG#ET(&Kxr5UL12QZCA`!zB)ySiG`lT(JK#HmhJS*P#e-EsdH_3-w5|GR>|{{IIOXb-Re literal 0 HcmV?d00001 diff --git a/chapter10-error/error.md b/chapter10-error/error.md new file mode 100644 index 0000000..733e21f --- /dev/null +++ b/chapter10-error/error.md @@ -0,0 +1,153 @@ +# error + +![img.png](error-situation.png) + +Rust 偷师 Haskell,构建了对标 Maybe 的 Option 类型和 对标 Either 的 Result 类型。 + +![img.png](option_n_result.png) + +## ? 操作符 + +早期 Rust 提供了 try! 宏来简化错误的显式处理,后来为了进一步提升用户体验,try! 被进化成 ? 操作符 + +如果你只想传播错误,不想就地处理,可以用 ? 操作符 + +## Error trait 和错误类型的转换 + +```rust +// intellij-rust/stdlib-local-copy/1.78.0-ba6645f9113d6a36b36e041b03064c99e1ae9e85/library/core/src/error.rs +pub trait Error: Debug + Display { + #[stable(feature = "error_source", since = "1.30.0")] + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + /// Gets the `TypeId` of `self`. + #[doc(hidden)] + #[unstable( + feature = "error_type_id", + reason = "this is memory-unsafe to override in user code", + issue = "60784" + )] + fn type_id(&self, _: private::Internal) -> TypeId + where + Self: 'static, + { + TypeId::of::() + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[deprecated(since = "1.42.0", note = "use the Display impl or to_string()")] + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[deprecated( + since = "1.33.0", + note = "replaced by Error::source, which can support downcasting" + )] + #[allow(missing_docs)] + fn cause(&self) -> Option<&dyn Error> { + self.source() + } + + /// ``` + #[unstable(feature = "error_generic_member_access", issue = "99301")] + #[allow(unused_variables)] + fn provide<'a>(&'a self, request: &mut Request<'a>) {} +} +``` + +## thiserror + +```rust +use thiserror::Error; +#[derive(Error, Debug)] +#[non_exhaustive] +pub enum DataStoreError { + #[error("data store disconnected")] + Disconnect(#[from] io::Error), + #[error("the data for key `{0}` is not available")] + Redaction(String), + #[error("invalid header (expected {expected:?}, found {found:?})")] + InvalidHeader { + expected: String, + found: String, + }, + #[error("unknown data store error")] + Unknown, +} +``` + +不使用 thiserror + +```rust +use std::fmt; + +#[derive(Debug)] +enum DataFetchError { + HttpError(u16), + Timeout, + InvalidPayload, +} + +impl fmt::Display for DataFetchError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::HttpError(code) => write!(f, "HTTP error with code: {}", code), + Self::Timeout => write!(f, "Data fetching timed out"), + Self::InvalidPayload => write!(f, "Invalid payload received"), + } + } +} + +impl std::error::Error for DataFetchError {} + +#[derive(Debug)] +enum DatabaseError { + ConnectionFailed, + WriteFailed(String), +} + +impl fmt::Display for DatabaseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ConnectionFailed => write!(f, "Failed to connect to database"), + Self::WriteFailed(reason) => write!(f, "Failed to write to database: {}", reason), + } + } +} + +impl std::error::Error for DatabaseError {} +``` + +使用 thiserror + +```rust +use thiserror::Error; + +#[derive(Debug, Error)] +enum DataFetchError { + #[error("HTTP error with code: {0}")] + HttpError(u16), + #[error("Data fetching timed out")] + Timeout, + #[error("Invalid payload received")] + InvalidPayload, +} + +#[derive(Debug, Error)] +enum DatabaseError { + #[error("Failed to connect to database")] + ConnectionFailed, + #[error("Failed to write to database: {0}")] + WriteFailed(String), +} + +``` + +- 代码减少: 对于每种错误类型,我们都不再需要单独的Display和Error trait实现。这大大减少了样板代码,并提高了代码的可读性。 +- 错误消息与定义在一起: 使用thiserror,我们可以直接在错误定义旁边写出错误消息。这使得代码更加组织化,方便查找和修改。 +- 可维护性增加: 如果我们要添加或删除错误类型,只需要修改枚举定义并更新错误消息即可,而不需要在其他地方进行更改 + diff --git a/chapter10-error/option_n_result.png b/chapter10-error/option_n_result.png new file mode 100644 index 0000000000000000000000000000000000000000..156e2ee4430c7950c49b2333fdb3545f1ffa9248 GIT binary patch literal 17846 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 Result { + let mut f = File::open(name)?; + let mut contents = String::new(); + f.read_to_string(&mut contents)?; + Ok(contents) +} + +fn main() { + read_file("text1.txt").expect("file does not exist"); +} diff --git a/chapter11-closure/Cargo.toml b/chapter11-closure/Cargo.toml new file mode 100644 index 0000000..8aa7c20 --- /dev/null +++ b/chapter11-closure/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "chapter11-closure" +version.workspace = true +edition.workspace = true +authors.workspace = true + +[dependencies] + + +[[bin]] +name = "closure1" +path = "src/closure1-size.rs" + + +[[bin]] +name = "closure2" +path = "src/closure2-fnonce.rs" \ No newline at end of file diff --git a/chapter11-closure/closure.md b/chapter11-closure/closure.md new file mode 100644 index 0000000..467e56c --- /dev/null +++ b/chapter11-closure/closure.md @@ -0,0 +1,46 @@ +# 闭包 + +> A closure expression produces a closure value with a unique, anonymous type that cannot be written out. +> A closure type is approximately equivalent to a struct which contains the captured variables. + +闭包是一种匿名类型,一旦声明,就会产生一个新的类型,但这个类型无法被其它地方使用。这个类型就像一个结构体,会包含所有捕获的变量。 + +## 案例 + +```rust +#[stable(feature = "rust1", since = "1.0.0")] +pub fn spawn(f: F) -> JoinHandle +where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, +{ + Builder::new().spawn(f).expect("failed to spawn thread") +} +``` + +- F: FnOnce() → T,表明 F 是一个接受 0 个参数、返回 T 的闭包。 +- F: Send + 'static,说明闭包 F 这个数据结构,需要静态生命周期或者拥有所有权,并且它还能被发送给另一个线程。 +- T: Send + 'static,说明闭包 F 返回的数据结构 T,需要静态生命周期或者拥有所有权,并且它还能被发送给另一个线程。 + +## Rust 的闭包类型 + +### FnOnce 只能被调用一次 + +```rust +#[fundamental] // so that regex can rely that `&str: !FnMut` +#[must_use = "closures are lazy and do nothing unless called"] +// FIXME(effects) #[const_trait] +pub trait FnOnce { + /// The returned type after the call operator is used. + #[lang = "fn_once_output"] + #[stable(feature = "fn_once_output", since = "1.12.0")] + type Output; + + /// Performs the call operation. + #[unstable(feature = "fn_traits", issue = "29625")] + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; +} +``` + +call_once 第一个参数是 self,它会转移 self 的所有权到 call_once 函数中 \ No newline at end of file diff --git a/chapter11-closure/src/closure1-size.rs b/chapter11-closure/src/closure1-size.rs new file mode 100644 index 0000000..b70f9d8 --- /dev/null +++ b/chapter11-closure/src/closure1-size.rs @@ -0,0 +1,35 @@ +mod r#closure2 + +use std::{collections::HashMap, mem::size_of_val}; +fn main() { + // 长度为 0 + let c1 = || println!("hello world!"); + // 和参数无关,长度也为 0 + let c2 = |i: i32| println!("hello: {}", i); + let name = String::from("Danny"); + let name1 = name.clone(); + let mut table = HashMap::new(); + table.insert("hello", "world"); + // 如果捕获一个对变量 name 的引用,这个引用是 &String,长度为 8 + let c3 = || println!("hello: {}", name); + // 捕获移动的数据 name1(长度 24) + table(长度 48),closure 长度 72 + let c4 = move || println!("hello: {}, {:?}", name1, table); + let name2 = name.clone(); + // 和局部变量无关,捕获了一个 String name2,closure 长度 24 + let c5 = move || { + let x = 1; + let name3 = String::from("lindsey"); + println!("hello: {}, {:?}, {:?}", x, name2, name3); + }; + + // 不带 move 时,闭包捕获的是对应自由变量的引用;带 move 时,对应自由变量的所有权会被移动到闭包结构中 + println!( + "c1: {}, c2: {}, c3: {}, c4: {}, c5: {}, main: {}", + size_of_val(&c1), + size_of_val(&c2), + size_of_val(&c3), + size_of_val(&c4), + size_of_val(&c5), + size_of_val(&main), + ) +} diff --git a/chapter11-closure/src/closure2-fnonce.rs b/chapter11-closure/src/closure2-fnonce.rs new file mode 100644 index 0000000..d15cd4a --- /dev/null +++ b/chapter11-closure/src/closure2-fnonce.rs @@ -0,0 +1,28 @@ +fn main() { + let name = String::from("Danny"); + + // 这个闭包会 clone 内部的数据返回,所以它不是 FnOnce + let c = move |greeting: String| (greeting, name.clone()); + + // 所以 c1 可以被调用多次 + + println!("c1 call once: {:?}", c("您好".into())); + println!("c1 call twice: {:?}", c("bonjour".into())); + + // 然而一旦它被当成 FnOnce 被调用,就无法被再次调用 + println!("result: {:?}", call_once("hi".into(), c)); + + // 无法再次调用 + // let result = c("hi".to_string()); + + // Fn 也可以被当成 FnOnce 调用,只要接口一致就可以 + println!("result: {:?}", call_once("hola".into(), not_closure)); +} + +fn call_once(arg: String, c: impl FnOnce(String) -> (String, String)) -> (String, String) { + c(arg) +} + +fn not_closure(arg: String) -> (String, String) { + (arg, "Rosie".into()) +}