-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1035 lines (704 loc) · 141 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>canzhu's blog</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta property="og:type" content="website">
<meta property="og:title" content="canzhu's blog">
<meta property="og:url" content="https://liucanzhu.github.io/index.html">
<meta property="og:site_name" content="canzhu's blog">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="Liu Canzhu">
<meta name="twitter:card" content="summary">
<link rel="alternate" href="/atom.xml" title="canzhu's blog" type="application/atom+xml">
<link rel="shortcut icon" href="/favicon.png">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/fancybox/jquery.fancybox.min.css">
<meta name="generator" content="Hexo 7.3.0"></head>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/" id="logo">canzhu's blog</a>
</h1>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"><span class="fa fa-bars"></span></a>
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
</nav>
<nav id="sub-nav">
<a class="nav-icon" href="/atom.xml" title="RSS Feed"><span class="fa fa-rss"></span></a>
<a class="nav-icon nav-search-btn" title="Search"><span class="fa fa-search"></span></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="https://liucanzhu.github.io"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-solana3" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/11/21/solana3/" class="article-date">
<time class="dt-published" datetime="2024-11-21T15:07:48.483Z" itemprop="datePublished">2024-11-21</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/11/21/solana3/">solana 学习笔记三 Solana基础,账户与简单交互</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<h3 id="一-solana中的账户主要分为以下几种:"><a href="#一-solana中的账户主要分为以下几种:" class="headerlink" title="一. solana中的账户主要分为以下几种:"></a>一. solana中的账户主要分为以下几种:</h3><ol>
<li><p>数据账户,用来存储数据</p>
</li>
<li><p>系统所有账户</p>
</li>
<li><p>程序派生账户(PDA)</p>
</li>
<li><p>程序账户,用来存储可执行程序</p>
</li>
<li><p>原生账户,指Solana上的原生程序,例如”System”,”Stake”,以及”Vote”</p>
</li>
</ol>
<h3 id="二-账户结构如下:"><a href="#二-账户结构如下:" class="headerlink" title="二. 账户结构如下:"></a>二. 账户结构如下:</h3><h4 id="1-Account"><a href="#1-Account" class="headerlink" title="1. Account"></a>1. Account</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">#[derive(Deserialize, PartialEq, Eq, Eq, Clone, Default)]</span><br><span class="line">#[serde(rename_all = "camelCase")]</span><br><span class="line">pub struct Account {</span><br><span class="line">/// lamports in the account</span><br><span class="line">pub lamports: u64,</span><br><span class="line">/// data held in this account</span><br><span class="line">#[serde(with = "serde_bytes")]</span><br><span class="line">pub data: Vec<u8>,</span><br><span class="line">/// the program that owns this account. If executable, the program that loads this account.</span><br><span class="line">pub owner:Pubkey,</span><br><span class="line">/// this account's data contains a loaded program (and is now read-only)</span><br><span class="line">executable: bool,</span><br><span class="line">///the epoch at which this account will next owe rent</span><br><span class="line">pub rent_epoch:Epoch,</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-Accountlnfo"><a href="#2-Accountlnfo" class="headerlink" title="2. Accountlnfo"></a>2. Accountlnfo</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">/// Account information</span><br><span class="line">#[derive(Clone)]</span><br><span class="line">#[repr(C)]</span><br><span class="line">pub struct AccountInfo<'a> {</span><br><span class="line">/// Public key of the account</span><br><span class="line">pub key: &'a Pubkey,</span><br><span class="line">/// The lamports in the account. Modifiable by programs.</span><br><span class="line">pub lamports:Rc<RefCell<&'a mut u64>>,</span><br><span class="line">/// The data held in this account. Modifiable by programs.</span><br><span class="line">pub data: Rc<RefCell<&'a mut [u8]>>,</span><br><span class="line">/// Program that owns this account</span><br><span class="line">pub owner: & a Pubkey,</span><br><span class="line">/// The epoch at which this account will next owe rent</span><br><span class="line">pub rent_epoch:Epoch,</span><br><span class="line">/// Was the transaction signed by this account's publikey?</span><br><span class="line">pub is_signer:bool,</span><br><span class="line">/// Is the account writable?</span><br><span class="line">pub is_writable: bool,</span><br><span class="line">/// This account's data contains a loaded program (and is now read-only)</span><br><span class="line">pub executable: bool,</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="3-Account-AccountInfo对比"><a href="#3-Account-AccountInfo对比" class="headerlink" title="3. Account&AccountInfo对比"></a>3. Account&AccountInfo对比</h4><h5 id="Accountlnfo"><a href="#Accountlnfo" class="headerlink" title="Accountlnfo:"></a>Accountlnfo:</h5><p>更轻量级,包含对区块链上现有账户数据的引用。<br>用于在Solana程序(智能合约)内部处理账户。<br>适合在链上处理和操作账户数据。</p>
<h5 id="Account"><a href="#Account" class="headerlink" title="Account:"></a>Account:</h5><p>更完整的账户表示,包含账户的所有数据副本。<br>常用于客户端或测试环境中,用于模拟或获取完整的账户状态。<br>适合离线处理或全局管理账户数据。</p>
<h4 id="要点"><a href="#要点" class="headerlink" title="要点"></a>要点</h4><p>账户是用来存放数据的<br>每个账户都有一个独一无二的地址<br>每个账户大小不能超过10MB<br>账户大小是静态的<br>账户数据存储需要付租金<br>默认的账户所有者是”系统程序</p>
<h3 id="三-程序派生账户"><a href="#三-程序派生账户" class="headerlink" title="三. 程序派生账户"></a>三. 程序派生账户</h3><h4 id="1-PDA特性"><a href="#1-PDA特性" class="headerlink" title="1. PDA特性"></a>1. PDA特性</h4><h5 id="a-不能直接签名交易"><a href="#a-不能直接签名交易" class="headerlink" title="a.不能直接签名交易"></a>a.不能直接签名交易</h5><p>限制:PDA账户<strong>没有私钥,因此无法像普通账户那样签名交易。</strong><br>影响:这意味着PDA无法自主发起交易,它<strong>只能被相关的智能合约程序用作数据存储或执行操作</strong>。这确保了PDA只能在程序的控制下使用。</p>
<h5 id="b-地址碰撞的可能性"><a href="#b-地址碰撞的可能性" class="headerlink" title="b.地址碰撞的可能性"></a>b.地址碰撞的可能性</h5><p>限制:在理论上,虽然非常罕见,使用相同的程序ID和相同的种子值可以生成相同的PDA地址。<br>影响:这意味着在设计智能合约时,开发者必须谨慎选择种子值,以确保不会产生地址碰撞。一般来说,通过使用唯一的种子(比如用户的公钥和其他独特的数据),可以避免这种问题。</p>
<h5 id="c-PDA地址的最大长度"><a href="#c-PDA地址的最大长度" class="headerlink" title="c.PDA地址的最大长度"></a>c.PDA地址的最大长度</h5><p>限制:PDA的种子值组合在一起不能超过32字节(bytes)。<br>影响:如果你的数据太大,可能无法直接作为种子使用。你可能需要对数据进行哈希处理或其他方式来适应这个限制。</p>
<h5 id="d-生成PDA的计算成本"><a href="#d-生成PDA的计算成本" class="headerlink" title="d.生成PDA的计算成本"></a>d.生成PDA的计算成本</h5><p>限制:PDA是通过哈希函数计算生成的,这个过程消耗计算资源源。<br>影响:在性能敏感的应用中,频繁生成PDA可能增加链上计算的成本,影响程序的执行效率。因此,在设计程序时需要平衡性能和安全性。</p>
<h5 id="e-单一程序的访问"><a href="#e-单一程序的访问" class="headerlink" title="e.单一程序的访问"></a>e.单一程序的访问</h5><p>限制:PDA账户是<strong>由一个特定的程序生成和控制的,只有这个程序可以操作该PDA账户。</strong><br>影响:虽然这提供了很强的安全性,但也意味着你不能轻易地跨移序共享PDA账户。如果多个程序需要访问相同的数据,可能需要复杂的设计或数据复制。</p>
<h5 id="f-内存账户的使用"><a href="#f-内存账户的使用" class="headerlink" title="f.内存账户的使用"></a>f.内存账户的使用</h5><p>限制:如果PDA被用作Solana上的内存账户(即需要存储较多的数据),这些账户的大小是有限制的,<strong>超过一定大小需要支付更高的费用来增加内存租金。</strong><br>影响:你需要考虑PDA账户的数据量,避免不必要的存储开销,或者拆分数据存储到多个PDA账户中。</p>
<h4 id="2-PDA应用场景"><a href="#2-PDA应用场景" class="headerlink" title="2. PDA应用场景"></a>2. PDA应用场景</h4><p>1.用户状态管理<br>2.去中心化金融(DeFi)协议<br>3.NFT元数据存储<br>4.DAO(去中心化自治组织)投票系统<br>5.时间锁合约<br>6.多签(Multisig)钱包<br>7.去中心化身份验证</p>
<h3 id="四-Rust开发solana"><a href="#四-Rust开发solana" class="headerlink" title="四. Rust开发solana:"></a>四. Rust开发solana:</h3><h4 id="1-以下三个库在开发中经常使用"><a href="#1-以下三个库在开发中经常使用" class="headerlink" title="1.以下三个库在开发中经常使用"></a>1.以下三个库在开发中经常使用</h4><p>Solana_client</p>
<p>Solana_sdk</p>
<p>Solane_program</p>
<h4 id="2-实战"><a href="#2-实战" class="headerlink" title="2.实战"></a>2.实战</h4><p>启动本地环境</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">solana-test-validator --reset</span><br></pre></td></tr></table></figure>
<p>更改solana配置,链接到本地开发环境</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">solana config set --url http://127.0.0.1:8899</span><br></pre></td></tr></table></figure>
<p>创建本地账户</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">solana-keygen new -o ~/.config/solana/ad1.json</span><br></pre></td></tr></table></figure>
<p>此时公钥在:</p>
<p><img src="/../assets/img/28-1.jpg" alt="图28-1" title="28-1"></p>
<p>其中的pubkey</p>
<p>私钥:</p>
<p><img src="/../assets/img/28-4.jpg" alt="图28-4" title="28-4"></p>
<p>将本地账户切换到刚才创建的公钥:</p>
<p><img src="/../assets/img/28-2.jpg" alt="图28-2" title="28-2"></p>
<p>给新建账户空投sol</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Solana airdrop 10</span><br></pre></td></tr></table></figure>
<p>使用SDK</p>
<h5 id="a-空投sol"><a href="#a-空投sol" class="headerlink" title="a.空投sol"></a>a.空投sol</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">let amount = 2_000_000_000;</span><br><span class="line">match client.request_airdrop(&account_pubkey, amount) {</span><br><span class="line"> Ok(signature)=>println!("获取空投成功,交易签名:{:?}",signature),</span><br><span class="line"> Err(err) => println!("空投失败{:?}",err),</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h5 id="b-获取账号信息"><a href="#b-获取账号信息" class="headerlink" title="b.获取账号信息"></a>b.获取账号信息</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">let rpc_url = "http://127.0.0.1:8899";</span><br><span class="line"></span><br><span class="line">let client = RpcClient::new(rpc_url);</span><br><span class="line"></span><br><span class="line">//指定要查询的账户公钥,from_str返回值是result</span><br><span class="line">let account_pubkey = Pubkey::from_str("AEjMNU9eDwEsDh35JWjXtyFdSRGCN4utDaMMQSbwz4v3").unwrap();</span><br><span class="line"></span><br><span class="line">match client.get_balance(&account_pubkey) {</span><br><span class="line"> Ok(balance) => println!("账户余额{:?}",balance),</span><br><span class="line"> Err(_) => todo!(), </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h5 id="c-转移sol"><a href="#c-转移sol" class="headerlink" title="c.转移sol"></a>c.转移sol</h5><p>用之前的指令创建一个新的账号:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">solana-keygen new -o ~/.config/solana/ad1.json</span><br></pre></td></tr></table></figure>
<p>得到新的pubkey,作为转账操作的接收地址,先通过</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">solana config set –pubkey ./.config/solana/ad2.json</span><br></pre></td></tr></table></figure>
<p>查看余额发现当前余额为0,然后切回之前的账户,</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">let rpc_url = "http://127.0.0.1:8899";</span><br><span class="line"></span><br><span class="line"> let client = RpcClient::new(rpc_url);</span><br><span class="line"></span><br><span class="line"> //指定要查询的账户公钥,from_str返回值是result</span><br><span class="line"> let sender = read_keypair_file("/home/lcz/.config/solana/ad2.json").expect("failed");</span><br><span class="line"></span><br><span class="line"> let receiver_pubkey = Pubkey::from_str("AEjMNU9eDwEsDh35JWjXtyFdSRGCN4utDaMMQSbwz4v3").unwrap();</span><br><span class="line"> </span><br><span class="line"> //创建转账指令</span><br><span class="line"> let transaction_order = transfer(&sender.pubkey(), &receiver_pubkey, 1000000000);</span><br><span class="line"> </span><br><span class="line"> //创建交易</span><br><span class="line"> let recent_blockhash = client.get_latest_blockhash().unwrap();</span><br><span class="line"></span><br><span class="line"> let transaction = Transaction::new_signed_with_payer(&[transaction_order],Some( &sender.pubkey()), &[&sender], recent_blockhash);</span><br><span class="line"> </span><br><span class="line"> let result = client.send_and_confirm_transaction(&transaction);</span><br><span class="line"> </span><br><span class="line"> match result{</span><br><span class="line"> Ok(signature)=>println!("转账成功,交易签名:{:?}",signature),</span><br><span class="line"> Err(err) => println!("转账失败{:?}",err),</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>执行显示转账成功,此时查看新账号的余额,发现为1sol</p>
<h4 id="e-通过JsonRpc获取账户信息"><a href="#e-通过JsonRpc获取账户信息" class="headerlink" title="e.通过JsonRpc获取账户信息"></a>e.通过JsonRpc获取账户信息</h4><p>可以直接在终端向指定的网络rpc发起请求, 在请求中说明你需要调用的方法和参数:</p>
<p><img src="/../assets/img/28-3.jpg" alt="图28-3" title="28-3"></p>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/11/21/solana3/" data-id="cm3rgcatc0000lguyc2v6hptf" data-title="solana 学习笔记三 Solana基础,账户与简单交互" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-solana2" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/11/17/solana2/" class="article-date">
<time class="dt-published" datetime="2024-11-17T07:47:28.848Z" itemprop="datePublished">2024-11-17</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/11/17/solana2/">solana 学习笔记二 Solana开发入门</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<p>solana 的项目搭建可以使用以下三种方式:</p>
<ol>
<li><p>Native rust</p>
</li>
<li><p>Playground </p>
</li>
<li><p>Anchor</p>
</li>
</ol>
<p>下面分别对三种搭建方式进行阐述</p>
<h3 id="一-Native-rust"><a href="#一-Native-rust" class="headerlink" title="一. Native rust"></a>一. Native rust</h3><h4 id="1-首先通过以下指令初始化项目:"><a href="#1-首先通过以下指令初始化项目:" class="headerlink" title="1. 首先通过以下指令初始化项目:"></a>1. 首先通过以下指令初始化项目:</h4><p><code>cargo new --lib <project_name> # new project</code></p>
<p>进入项目目录执行:</p>
<p><code>cargo add solana-program # deps</code></p>
<p>此时wsl用户可能会发现toml文件依赖处有报错:Errors solana-program: Error: Failed to establish a socket connection to proxies: [“PROXY 127.0.0.1:xxxxx”],</p>
<p>这是因为由于我开启了系统代理,但是wsl并不能直接连接到我的系统代理IP地址,所以需要禁用。参考:</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_63211230/article/details/140205627">https://blog.csdn.net/weixin_63211230/article/details/140205627</a></p>
<p>修改编译配置</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[lib]</span><br><span class="line"></span><br><span class="line">crate-type = ["cdylib", "lib"]</span><br></pre></td></tr></table></figure>
<p>接下来对lib文件进行修改</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">use solana_program ::{</span><br><span class="line"> account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">//声明程序入口</span><br><span class="line">entrypoint!(process_instruction);</span><br><span class="line"></span><br><span class="line">//</span><br><span class="line">fn process_instruction(</span><br><span class="line"> program_id: &Pubkey,</span><br><span class="line"> _accounts: &[AccountInfo],</span><br><span class="line"> _instruction_data: &[u8],</span><br><span class="line">) -> ProgramResult{</span><br><span class="line"> msg!("Hello, Solana!");</span><br><span class="line"> msg!("Our program's Program ID:{}", &program_id);</span><br><span class="line"> msg!("program_id:{:?}",program_id);</span><br><span class="line"> msg!("accounts:{:?}",_accounts);</span><br><span class="line"> msg!("instruction_data: {:?}", _instruction_data);</span><br><span class="line"> Ok(())</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>其中的<code>process_instruction</code>函数被声明为程序入口,部署后所有对链上发起的交易信息都可以被这个函数读取</p>
<p>通过观察entrypoint!宏的源码可以得出:<br>入口函数的参数必须是:<code>program_id: &Pubkey, _accounts: &[AccountInfo], _instruction_data: &[u8],</code>, 这三个参数,参数中_instruction_data参数包含了执行特定操作所需的所有信息,比如转账的代币数量、转账地址等。这些数据会作为字节码的形式,被包含在交易的指令中,当交易被提交到Solana网络时,相应的程序会解析这些数据,并根据这些指令在指定的账户上进行操作。 而函数返回值必须是<strong>Result</strong>类型的</p>
<p>在solana开发中,之前在rust中写的println!宏打印已经不再支持,最后将代码封装成一个在链上执行的动态链接库,只能通过msg!()的格式代替println!()进行输出</p>
<p>现在我们尝试将代码进行打包与部署:</p>
<h4 id="2-Build-Deploy"><a href="#2-Build-Deploy" class="headerlink" title="2.Build & Deploy"></a>2.Build & Deploy</h4><p>先查看当前网络</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">solana config get</span><br></pre></td></tr></table></figure>
<p>之后build</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cargo build-sbf</span><br></pre></td></tr></table></figure>
<p>我是按照官方的教程操作的,但是在执行这一步时仍然报错:</p>
<p>error: package <code>solana-program v2.1.0</code> cannot be built because it requires rustc 1.79.0 or newer, while the currently active rustc version is 1.75.0-dev</p>
<p>看起来是外面安装的rust版本和程序中solana-program自带的rust版本不一样,且程序中使用的版本与当前的solana版本不兼容,将错误复制到官网上,发现有类似的问题,<a target="_blank" rel="noopener" href="https://solana.com/zh/developers/courses/onchain-development/local-setup#object-object-solana-program">https://solana.com/zh/developers/courses/onchain-development/local-setup#object-object-solana-program</a> v1.8.12</p>
<p>根据官网操作在程序中重新安装更新版本的solana-program得到解决</p>
<p>第一次执行可能需要较长时间,在这一步后,可以在target目录下发现多了sbf-solana-solana文件夹,其中有个release文件夹,其中so文件,这就是待我们部署的文件、</p>
<p>之后我们执行solana program deploy ./target/deploy/your_program.so, 就可以将其部署到测试网上,执行后可以在终端看到输出的program id 和 signature</p>
<p><img src="/../assets/img/27-1.jpg" alt="图27-1" title="27-1"></p>
<p>在solana浏览器的对应网络中搜索这个id可以看到部署细节以及msg输出, 在这之后,如果想取消部署,收回已经分发的代币,可执行</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">solana program close <program_id>#(此操作不可逆,需三思)</span><br></pre></td></tr></table></figure>
<h3 id="二-playground"><a href="#二-playground" class="headerlink" title="二. playground"></a>二. playground</h3><p>同样,以上操作可以放在<a target="_blank" rel="noopener" href="https://beta.solpg.io/">https://beta.solpg.io/</a> 执行,这是一个线上开发环境,也支持连接到测试网测试,直接点击左上角的+号,创建一个新项目,然后在下方终端输入connect,会自动帮你创建一个测试钱包,且里面带有5sol测试代币<br>之后界面大概如下所示:</p>
<p><img src="/../assets/img/27-2.jpg" alt="图27-2" title="27-2"></p>
<p>分为左边的菜单界面,中间的代码,下方的终端和右上角的钱包信息, 在修改完lib.rs中的内容后,可以点击左边菜单栏中的build.和 deploy,将其部署到测试网后,可以点击左下角在浏览器中查看,此时可以发现instruction中第一项为:</p>
<p>#1 BPF Upgradeable Loader: Upgrade</p>
<p><img src="/../assets/img/27-3.jpg" alt="图27-3" title="27-3"></p>
<p>这表示当前合约已经经过了第一次部署,现在是待升级状态,<code>BPF Upgradeable Loader</code>,这指的是一种机制,允许在不改变合约地址的情况下升级智能合约。这是通过使用一个代理合约(loader)来实现的,它指向实际的合约代码。当需要升级时,只需更新loader指向的新合约代码,而不需要替换整个合约。</p>
<p>而最后的Program Instruction Logs界面也显示了return success </p>
<p><img src="/../assets/img/27-4.jpg" alt="图27-4" title="27-4"></p>
<p>因为我们的代码中规定了最后返回时Result类型</p>
<p>在菜单中的client文件中,有一个client.ts文件,用于模拟前端与链上的合约进行交互,可以点击run运行,打印的交易细节或区块信息等可以在控制台看到</p>
<p><img src="/../assets/img/27-5.jpg" alt="图27-5" title="27-5"></p>
<h3 id="三-Anchor"><a href="#三-Anchor" class="headerlink" title="三. Anchor"></a>三. Anchor</h3><h4 id="1-首先通过-以下指令初始化Anchor项目"><a href="#1-首先通过-以下指令初始化Anchor项目" class="headerlink" title="1. 首先通过 以下指令初始化Anchor项目"></a>1. 首先通过 以下指令初始化Anchor项目</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">anchor init <project_name></span><br></pre></td></tr></table></figure>
<p>在执行这个之前,需要根据上面的环境配置教程,安装好node 和 yarn ,项目创建完后打开,观察目录发现这是一个前端与合约都有的项目:</p>
<p><img src="/../assets/img/27-6.jpg" alt="图27-6" title="27-6"></p>
<p>其中关于前端的部分,一目了然,不做过多赘述,合约的部分在programs文件夹中,在外层还有一个叫Anchor.tmol的文件,在里面可以修改Anchor的配置,也可以在其中指定部署的网络,在外层的tests文件夹中,是项目的测试文件,是用ts写的,在里面可以调取programs中写的智能合约中的方法,与链上进行交互测试</p>
<h4 id="2-此时先执行anchor-build"><a href="#2-此时先执行anchor-build" class="headerlink" title="2. 此时先执行anchor build"></a>2. 此时先执行anchor build</h4><p>这是anchor框架打包的方式,此时会将合约中写的方法集成输出到target文件夹中的types文件夹,供上面的测试文件调用</p>
<h4 id="3-anchor-test"><a href="#3-anchor-test" class="headerlink" title="3. anchor test"></a>3. anchor test</h4><p>可以发现终端已经输出了测试文件的执行结果</p>
<h4 id="4-anchor-deploy"><a href="#4-anchor-deploy" class="headerlink" title="4. anchor deploy"></a>4. anchor deploy</h4><p>最后调用deploy命令将其部署到Anchor.toml中指定的网络中,终端输出合约地址</p>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/11/17/solana2/" data-id="cm3laycby00001ouydzk97ihx" data-title="solana 学习笔记二 Solana开发入门" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-solana1" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/11/13/solana1/" class="article-date">
<time class="dt-published" datetime="2024-11-13T15:14:02.254Z" itemprop="datePublished">2024-11-13</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/11/13/solana1/">solana 学习笔记一 Solana基础与环境搭建</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<h3 id="一-区块链知识基础"><a href="#一-区块链知识基础" class="headerlink" title="一.区块链知识基础"></a>一.区块链知识基础</h3><h4 id="1-先看看区块链的定义"><a href="#1-先看看区块链的定义" class="headerlink" title="1.先看看区块链的定义"></a>1.先看看区块链的定义</h4><p>区块链是由一系列按时间顺序链接的”区块”组成的。每个区块包含三部分:</p>
<h5 id="a-区块头-Block-Header"><a href="#a-区块头-Block-Header" class="headerlink" title="a.区块头(Block Header)"></a>a.区块头(Block Header)</h5><p>前一个区块的哈希(Previous Block Hash):链接到链上前一个区区块的加密哈希值,确保数据不可篡改。</p>
<p>时间戳(Timestamp)记录创建区块的时间。</p>
<p>默克尔根(MerkleRoot):所有交易数据的哈希值,确保数据完整性。</p>
<h5 id="b-区块体-Block-Body"><a href="#b-区块体-Block-Body" class="headerlink" title="b.区块体(Block Body)"></a>b.区块体(Block Body)</h5><p>交易列表(Transactions):包含区块中所有的交易记录。</p>
<p>区块哈希(Block Hash):</p>
<p>当前区块的哈希值:通过对区块头数据进行哈希计算生成,作为下一个区块的”前一个区块哈希”。通过这些信息可以将多个区块连接成一条链表</p>
<p>结构如下所示:</p>
<p><img src="/../assets/img/26-1.jpg" alt="图26-1" title="26-1"></p>
<p>众所周知区块链的特性就是去中心化,那么是如何去中心化的存储数据的呢:</p>
<h4 id="2-去中心化网络-P2P"><a href="#2-去中心化网络-P2P" class="headerlink" title="2.去中心化网络(P2P)"></a>2.去中心化网络(P2P)</h4><p>区块链是由分布在全球的节点组成的去中心化网络,这些节点共同维护和更新区块链状态。每个节点都有一份完整的区块链副本。</p>
<p>区块链上的交易包含以下基础步骤:</p>
<p>a.交易创建:用户A创建一笔交易,将资金或数据发送给用户B。</p>
<p>b.交易广播:该交易被广播到区块链网络中,所有节点接收到交易信息。</p>
<p>c.交易验证:网络中的矿工或验证节点对交易进行验证,确保交易的合法性</p>
<p>d.交易打包:验证通过的交易被打包进区块中,并由矿工竞争挖矿(在Pow的情况下)</p>
<p>e.交易确认:新创建的区块被添加到区块链上,交易得到确认···</p>
<p>如下所示:</p>
<p><img src="/../assets/img/26-2.jpg" alt="图26-2" title="26-2"></p>
<p>在上面的流程中,最重要的一步就是交易验证,在去中心化的交易中,没有一个存储数据的中心数据库,所以不同的节点达成共识非常重要</p>
<h4 id="3-共识机制"><a href="#3-共识机制" class="headerlink" title="3.共识机制"></a>3.共识机制</h4><p>共识机制是区块链网络中所有节点就数据达成一致的方式。以常见的工作量证明(Proof of Work,PoW)为例:</p>
<p>工作量证明(PoW):矿工通过计算工作量来解决一个复杂的数学问题(寻找符合条件的hash),第一个解出问题的矿工可以将其区块添加到区块链中。</p>
<p>这些交易的逻辑来源于部署在区块链上的智能合约:</p>
<h4 id="4-智能合约"><a href="#4-智能合约" class="headerlink" title="4.智能合约"></a>4.智能合约</h4><p>智能合约是部署在区块链上的自执行代码,能根据预定条件自动执行交易</p>
<p>如下所示:</p>
<p><img src="/../assets/img/26-3.jpg" alt="图26-3" title="26-3"></p>
<p>那么和其他的链相比,Solana有什么优势呢</p>
<h4 id="5-Solana八大特性:"><a href="#5-Solana八大特性:" class="headerlink" title="5.Solana八大特性:"></a>5.Solana八大特性:</h4><p>高性能solana</p>
<ol>
<li><p>Proof of History(POH)-a clock before consensus;</p>
</li>
<li><p>Tower BFT-a PoH-optimized version of PBFT;</p>
</li>
<li><p>Turbine-a block propagation protocol;</p>
</li>
<li><p>Gulf Stream-Mempool-less transaction forwarding protocol:</p>
</li>
<li><p>Sealevel–Parallel smart contracts run-time;</p>
</li>
<li><p>Pipelining-a Transaction Processing Unit for validation optimization</p>
</li>
<li><p>Cloudbreak-Horizontally-Scaled Accounts Database; and</p>
</li>
<li><p>Archivers-Distributed ledger store</p>
</li>
</ol>
<h4 id="二-开发环境搭建"><a href="#二-开发环境搭建" class="headerlink" title="二.开发环境搭建"></a>二.开发环境搭建</h4><p>关于solana本地开发环境的一切配置,参考<a target="_blank" rel="noopener" href="https://solana.com/zh/docs/intro/installation%EF%BC%8C%E9%9D%9E%E5%B8%B8%E8%AF%A6%E7%BB%86">https://solana.com/zh/docs/intro/installation,非常详细</a></p>
<p>一切准备就绪之后,可以尝试在测试网络上领取一些代币:</p>
<p>但是我目前总是会报错:<code>error: error sending request for url (https://api.devnet.solana.com/): operation timed out</code></p>
<p>所以直接访问了solana的水龙头网站领取:<a target="_blank" rel="noopener" href="https://faucet.solana.com/">https://faucet.solana.com/</a></p>
<p>然后在solana 浏览器上查看账户信息和交易记录(记得切换到测试网devnet)</p>
<p>此时我们可以看到网络选项中有一个叫custom RPC url 的选项,但是直接点击, 暂时无法切换到这个网络,Solana CLI 内置了测试验证器。运行本地验证器将允许您在本地部署和测试您的程序。</p>
<p>在单独的终端中,运行以下命令来启动本地验证器:<code>solana-test-validator</code>,之后再solana浏览器切换到custom rps url就可以看到网络变成localhost:8899</p>
<p>此时我们可以在本地终端进行测试代币的获取和转移,这是我在本地测试网通过<code>solana airdrop number</code>获取测试代币后,通过<code>solana transfer <recipient_public_key> <amount> --from<sender_keypair_path></code>(<sender_keypair_path>表示你本地存储密钥对信息的id.json文件路径,可以通过ls ~/.config/solana/id.json 获取)<br>进行的一笔交易:</p>
<p><a target="_blank" rel="noopener" href="https://explorer.solana.com/tx/4vFFbCAq4PCjXJH6oDdrBCfBDzRcpZhnZmWjjyvhfDx9pVMthNkqfUqAJhnBkvfph7KQhgatw44N5YMVYRHFMgzX?cluster=custom&customUrl=http://localhost:8899">https://explorer.solana.com/tx/4vFFbCAq4PCjXJH6oDdrBCfBDzRcpZhnZmWjjyvhfDx9pVMthNkqfUqAJhnBkvfph7KQhgatw44N5YMVYRHFMgzX?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899</a></p>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/11/13/solana1/" data-id="cm3g0xx6x0000e4uy0283dv0t" data-title="solana 学习笔记一 Solana基础与环境搭建" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-rust25" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/11/09/rust25/" class="article-date">
<time class="dt-published" datetime="2024-11-08T18:04:04.652Z" itemprop="datePublished">2024-11-09</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/11/09/rust25/">rust 学习笔记二十五 Rust中的共享内存</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<p>Go语言的名言:不要用共享内存来通信,要用通信来共享内存</p>
<p>Rust支持通过共享状态来实现并发</p>
<p>Channel类似单所有权:一旦将值的所有权转移至Channel,就无法使用它了</p>
<p>共享内存并发类似多所有权:多个线程可以同时访问同一内存</p>
<h4 id="一-使用Mutex来每次只允许一个线程来访问数据"><a href="#一-使用Mutex来每次只允许一个线程来访问数据" class="headerlink" title="一.使用Mutex来每次只允许一个线程来访问数据"></a>一.使用Mutex来每次只允许一个线程来访问数据</h4><p>Mutex是mutualexclusion(互斥锁)的简写,在同一时刻,Mutex只允许一个线程来访问某些数据</p>
<p>想要访问数据:</p>
<ol>
<li><p>线程必须首先获取互斥锁(lock)</p>
</li>
<li><p>lock数据结构是mutex的一部分,它能跟踪谁对数据拥</p>
</li>
<li><p>有独立访问权</p>
</li>
<li><p>mutex通常被描述为:通过锁定系统来保护它所持有的数据</p>
</li>
</ol>
<h4 id="二-Mutex的两条规则"><a href="#二-Mutex的两条规则" class="headerlink" title="二.Mutex的两条规则"></a>二.Mutex的两条规则</h4><ol>
<li><p>在使用数据之前,必须尝试获取锁(lock)。</p>
</li>
<li><p>使用完mutex所保护的数据,必须对数据进行解锁,以便其它线程可以获取锁。</p>
</li>
</ol>
<h4 id="三-Mutex的API"><a href="#三-Mutex的API" class="headerlink" title="三.Mutex的API"></a>三.Mutex<T>的API</h4><p>通过Mutex::new(数据)来创建Mutex<T>, Mutex<T>是一个智能指针, 访问数据前,通过<code>lock</code>方法来获取锁,这会阻塞当前线程,lock可能会失败</p>
<p>返回的是MutexGuard(智能指针,实现了Deref和Drop))</p>
<p>如下所示:</p>
<p><img src="/../assets/img/25-1.jpg" alt="图25-1" title="25-1"></p>
<p>如果需要使用多个线程分别对上面的a值进行修改,在 创建线程进行修改后,对a值进行读取,不出所料,报错:</p>
<p><img src="/../assets/img/25-2.jpg" alt="图25-2" title="25-2"></p>
<p>根据以往的经验,使用Rc包裹变量可以通过引用计数,在修改变量后读取,但是报错显示:“Rx变量由于没实现Sent trait,无法在线程间共享“,</p>
<p>于是我们可以使用:</p>
<p>使用Arc<T>来进行原子引用记数,Arc<T>和RC<T>类似,它可以用于并发情景,Arc<T>和RC<T>的API是相同的:</p>
<p>如下所示:</p>
<p><img src="/../assets/img/25-3.jpg" alt="图25-3" title="25-3"></p>
<p>A:atomic,意为原子的,既然如此,为什么所有的基础类型都不是原子的,为什么标准库类型不默认使用Arc<T>呢,因为需要性能作为代价</p>
<h4 id="四-RefCell-Rc-vs-Mutex-Arc"><a href="#四-RefCell-Rc-vs-Mutex-Arc" class="headerlink" title="四.RefCell / Rc vs Mutex / Arc"></a>四.RefCell<T> / Rc<T> vs Mutex<T> / Arc<T></h4><p>Mutex<T>提供了内部可变性,和Cell家族一样,我们使用RefCell<T>来改变Rc<T>里面的内容,我们使用Mutex<T>来改变Arc<T>里面的内容</p>
<p>注意:Mutex<T>有死锁风险</p>
<h4 id="五-Send-和Sync-trait"><a href="#五-Send-和Sync-trait" class="headerlink" title="五.Send 和Sync trait"></a>五.Send 和Sync trait</h4><p>Rust语言的并发特性较少,目前讲的并发特新都来自标准库(而不是语言本身),但无需局限于标准库的并发,可以自己实现并发</p>
<p>但在Rust语言中有两个并发概念:</p>
<p>std::marker::Sync 和std::marker::Send 这两个 trait</p>
<h5 id="Send-允许线程间转移所有权"><a href="#Send-允许线程间转移所有权" class="headerlink" title="Send:允许线程间转移所有权"></a>Send:允许线程间转移所有权</h5><p>实现Send trait的类型可在线程间转移所有权,Rust中几乎所有的类型都实现了Send,但RC<T>没有实现Send,它<strong>只用于单线程情景</strong></p>
<p>任何完全由Send类型组成的类型也被标记为Send,除了原始指针之外,几乎所有的基础类型都是Send</p>
<h5 id="Sync-允许从多线程访问"><a href="#Sync-允许从多线程访问" class="headerlink" title="Sync:允许从多线程访问"></a>Sync:允许从多线程访问</h5><p>实现Sync的类型可以安全的被多个线程引用,也就是说:如果T是Sync,那么&T就是Send,引用可以被安全的送往另一个线程</p>
<p>基础类型都是Sync,完全由Sync类型组成的类型也是Sync</p>
<p>但RC<T>不是Sync的,RefCell<T>和Cell<T>家族也不是Sync的,而Mutex<T>是Sync的</p>
<h4 id="六-实现一个多线程任务调度器"><a href="#六-实现一个多线程任务调度器" class="headerlink" title="六.实现一个多线程任务调度器"></a>六.实现一个多线程任务调度器</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line">use std::sync::{Arc, Mutex};</span><br><span class="line">use std::thread::{self, JoinHandle};</span><br><span class="line">use std::sync::mpsc;</span><br><span class="line">use std::time::Duration;</span><br><span class="line"></span><br><span class="line">struct Task {</span><br><span class="line"> id: usize,</span><br><span class="line"> action: Arc<Mutex<Box<dyn Fn() -> String + Send + Sync>>>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">struct Scheduler {</span><br><span class="line"> task_queue: Vec<Arc<Task>>,</span><br><span class="line"> thread_pool: Vec<JoinHandle<()>>,</span><br><span class="line"> result_sender: mpsc::Sender<String>,</span><br><span class="line"> result_receiver: mpsc::Receiver<String>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">impl Scheduler {</span><br><span class="line"> fn new() -> Self {</span><br><span class="line"> let (result_sender, result_receiver) = mpsc::channel();</span><br><span class="line"> Scheduler {</span><br><span class="line"> task_queue: Vec::new(),</span><br><span class="line"> thread_pool: Vec::new(),</span><br><span class="line"> result_sender,</span><br><span class="line"> result_receiver</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fn add_task<F>(&mut self, id: usize, action: F)</span><br><span class="line"> where</span><br><span class="line"> F: Fn() -> String +'static +Send + Sync,</span><br><span class="line"> {</span><br><span class="line"> let task = Task {</span><br><span class="line"> id,</span><br><span class="line"> action: Arc::new(Mutex::new(Box::new(action))),</span><br><span class="line"> };</span><br><span class="line"> self.task_queue.push(task.into());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fn start(&mut self, num_threads: usize) {</span><br><span class="line"> let (sender, receiver) = mpsc::channel();</span><br><span class="line"> let receiver = Arc::new(Mutex::new(receiver));//Metux避免数据竞争,Arc实现引用计数</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> for task in self.task_queue.iter() {</span><br><span class="line"> sender.send(Arc::clone(task)).unwrap(); // 发送任务</span><br><span class="line"> thread::sleep(Duration::from_millis(10)); // 模拟任务间隔</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"> for _ in 0..num_threads {</span><br><span class="line"> let receiver_clone = Arc::clone(&receiver);</span><br><span class="line"> let sender_clone = self.result_sender.clone();</span><br><span class="line"> let handle = thread::spawn(move || loop {</span><br><span class="line"> match receiver_clone.lock().unwrap().recv() {</span><br><span class="line"> Ok(task) => {</span><br><span class="line"> let action_lock = task.action.lock().unwrap();</span><br><span class="line"> let result:String = (action_lock)(); // 执行闭包</span><br><span class="line"> sender_clone.send(result).unwrap();</span><br><span class="line"> },</span><br><span class="line"> Err(_) => break, // 通道已关闭</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> self.thread_pool.push(handle);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fn get_results(&mut self)-> Vec<String> {</span><br><span class="line"> //等待所有任务执行完成</span><br><span class="line"> self.thread_pool.drain(..).for_each(|handle| {</span><br><span class="line"> handle.join().unwrap(); // 等待每个线程完成</span><br><span class="line"> });</span><br><span class="line"> self.result_receiver.try_iter().collect()</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fn main() {</span><br><span class="line"> let mut scheduler = Scheduler::new();</span><br><span class="line"></span><br><span class="line"> scheduler.add_task(1, || "Hello".to_string());</span><br><span class="line"> scheduler.add_task(2, || "World".to_string());</span><br><span class="line"> scheduler.add_task(3, || "from Rust".to_string());</span><br><span class="line"> scheduler.add_task(4, || "lcz".to_string());</span><br><span class="line"></span><br><span class="line"> scheduler.start(4);</span><br><span class="line"></span><br><span class="line"> thread::sleep(Duration::from_secs(1));</span><br><span class="line"></span><br><span class="line"> let results = scheduler.get_results();</span><br><span class="line"> for result in results {</span><br><span class="line"> println!("Result: {}", result);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/11/09/rust25/" data-id="cm391q96u0000x4uyfuqgbv2i" data-title="rust 学习笔记二十五 Rust中的共享内存" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-rust24" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/11/03/rust24/" class="article-date">
<time class="dt-published" datetime="2024-11-03T11:06:36.056Z" itemprop="datePublished">2024-11-03</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/11/03/rust24/">rust 学习笔记二十四 Rust的并发</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<h3 id="(一)先明确以下概念"><a href="#(一)先明确以下概念" class="headerlink" title="(一)先明确以下概念"></a>(一)先明确以下概念</h3><h4 id="1-并发与并行的区别:"><a href="#1-并发与并行的区别:" class="headerlink" title="1.并发与并行的区别:"></a>1.并发与并行的区别:</h4><p>并发(Concurrency):多个任务在时间上<strong>交替执行</strong></p>
<p>并行(Parallelism):多个任务<strong>同时执行</strong></p>
<h4 id="2-进程与线程"><a href="#2-进程与线程" class="headerlink" title="2.进程与线程"></a>2.进程与线程</h4><p>在大部分OS里,代码运行在进程(process)中,OS同时管理多个进程;</p>
<p>在你的程序里,各独立部分可以同时运行,运行这些独立部分的就是线程(thread)</p>
<h4 id="3-多线程运行"><a href="#3-多线程运行" class="headerlink" title="3.多线程运行"></a>3.多线程运行</h4><p>好处:提升性能表现</p>
<p>坏处:增加复杂性:无法保障各线程的执行顺序</p>
<p>多线程可导致的问题:竞争状态,线程死锁,两个线程彼此等待对方使用完所持有的资源,线程无法继续;只在某些情况下发生的Bug,很难可靠地复制现象和修复</p>
<h4 id="4-实现线程的方式"><a href="#4-实现线程的方式" class="headerlink" title="4.实现线程的方式"></a>4.实现线程的方式</h4><p>通过调用OS的API来创建线程:1:1模型,需要较小的运行时</p>
<p>语言自己实现的线程(绿色线程):M:N模型,需要更大的运行时</p>
<p>Rust:需要权衡运行时的支持</p>
<p>Rust标准库仅提供1:1模型的线程</p>
<h3 id="(二)Rust如何创建线程"><a href="#(二)Rust如何创建线程" class="headerlink" title="(二)Rust如何创建线程"></a>(二)Rust如何创建线程</h3><h4 id="1-创建新线程"><a href="#1-创建新线程" class="headerlink" title="1.创建新线程"></a>1.创建新线程</h4><p>通过 thread::spawn 函数可以创建新线程。参数:一个闭包(在新线程里运行的代码)</p>
<p><img src="/../assets/img/24-1.jpg" alt="图24-1" title="24-1"></p>
<p>由上面执行结果可以看出:</p>
<p>位于主线程和副线程的代码循环交替执行,且在主线程执行完之后,副线程虽然没有执行完,但是也很快结束了,如果希望在主线程执行完后,可以继续执行完副线程,可以</p>
<h4 id="2-通过join-Handle来等待所有线程完成"><a href="#2-通过join-Handle来等待所有线程完成" class="headerlink" title="2.通过join Handle来等待所有线程完成"></a>2.通过join Handle来等待所有线程完成</h4><p>thread::spawn函数的返回值类型是Join Handle,Join Handle持有值的所有权,调用其join方法,可以等待对应的其它线程的完成</p>
<p>join方法:调用handle的join方法会阻止当前运行线程的执行,直到handle所表示的这些线程终结。</p>
<p><img src="/../assets/img/24-2.jpg" alt="图24-2" title="24-2"></p>
<p>注:如果将handle.join().unwrap();副线程执行的代码写在了主线程执行之前,那么将会在副线程执行完之后再执行主线程,无法达到解决时间的效果</p>
<h4 id="3-使用move闭包"><a href="#3-使用move闭包" class="headerlink" title="3.使用move闭包"></a>3.使用move闭包</h4><p>move闭包通常和thread::spawn函数一起使用,它允许你使用其它线程的数据,创建线程时,把值的所有权从一个线程转移到另一个线程</p>
<p>如下所示:</p>
<p><img src="/../assets/img/24-3.jpg" alt="图24-3" title="24-3"></p>
<p>当闭包借用外界变量v时,提示:closure may outlive the current function, but it borrows <code>v</code>, which is owned by the current function<br>may outlive borrowed value <code>v</code>,为了确保外界变量的存活时间和闭包一样,需使用move:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">let v = vec![1,2,3];</span><br><span class="line">let handle = thread::spawn(move ||{</span><br><span class="line"> println!("here is a vector:{:?}",v);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<h3 id="(三)多线程通信"><a href="#(三)多线程通信" class="headerlink" title="(三)多线程通信"></a>(三)多线程通信</h3><h4 id="1-消息传递"><a href="#1-消息传递" class="headerlink" title="1.消息传递"></a>1.消息传递</h4><p>一种很流行且能保证安全并发的技术就是:消息传递。线程(或Actor)通过彼此发送消息(数据)来进行通信</p>
<p>Go语言的名言:不要用共享内存来通信,要用通信来共享内存。</p>
<p>Rust:Channel(标准库提供)</p>
<h4 id="2-Channel"><a href="#2-Channel" class="headerlink" title="2.Channel"></a>2.Channel</h4><p>Channel包含:发送端、接收端</p>
<p>调用发送端的方法,发送数据</p>
<p>接收端会检查和接收到达的数据</p>
<p>如果发送端、接收端中任意一端被丢弃了,那么Channel就”关闭”了</p>
<h4 id="3-创建channel"><a href="#3-创建channel" class="headerlink" title="3.创建channel"></a>3.创建channel</h4><p>使用mpsc::channel函数来创建Channel,mpsc表示multiple producer,single,consumer(多个生产者、一个消费者)</p>
<p>返回一个tuple(元组):里面元素分别是发送端、接收端</p>
<p>使用mpsc::sync_channel来创建带缓冲区的channel</p>
<p>入参为缓冲区大小,当缓冲区塞满时进行阻塞</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">use std::thread;</span><br><span class="line">use std::time::Duration;</span><br><span class="line">use std::sync::mpsc;</span><br><span class="line"></span><br><span class="line">fn main(){ </span><br><span class="line"> thread_study();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fn thread_study () {</span><br><span class="line"> let (tx, rx) = mpsc::channel();</span><br><span class="line"> let tx1 = mpsc::Sender::clone(&tx);</span><br><span class="line"> </span><br><span class="line"> thread::spawn(move || {</span><br><span class="line"> let vals = vec![</span><br><span class="line"> String::from("hi"),</span><br><span class="line"> String::from("from"),</span><br><span class="line"> String::from("the"),</span><br><span class="line"> String::from("thread"),</span><br><span class="line"> ];</span><br><span class="line"> for val in vals {</span><br><span class="line"> tx.send(val).unwrap();</span><br><span class="line"> thread::sleep(Duration::from_millis(1));</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> thread::spawn(move || {</span><br><span class="line"> let vals = vec![</span><br><span class="line"> String::from("hi"),</span><br><span class="line"> String::from("from"),</span><br><span class="line"> String::from("the"),</span><br><span class="line"> String::from("thread"),</span><br><span class="line"> ];</span><br><span class="line"> for val in vals {</span><br><span class="line"> tx1.send(val).unwrap();</span><br><span class="line"> thread::sleep(Duration::from_millis(1));</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> for received in rx {</span><br><span class="line"> println!("Got :{}",received);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="4-总结"><a href="#4-总结" class="headerlink" title="4.总结"></a>4.总结</h4><h5 id="发送端的方法"><a href="#发送端的方法" class="headerlink" title="发送端的方法"></a>发送端的方法</h5><p>send:</p>
<p>参数:想要发送的数据</p>
<p>返回:Result<T,E>,如果有问题(例如接收端已经被丢弃),就返回一个错误</p>
<h5 id="接收端的方法"><a href="#接收端的方法" class="headerlink" title="接收端的方法"></a>接收端的方法</h5><p>recv方法:</p>
<p><strong>阻止当前线程执行</strong>,直到Channel中有值被送来</p>
<p>· 一旦有值收到,就返回Result<T,E></p>
<p>· 当发送端关闭,就会收到一个错误</p>
<p><img src="/../assets/img/24-4.jpg" alt="图24-4" title="24-4"></p>
<p>如图所示:虽然发送线程中设置了一秒钟的延时,但是接受线程中在第一次接受到内容并打印后,也在继续等待直到接收到下一个信息</p>
<p>try_recv方法:</p>
<p><strong>不会阻塞</strong></p>
<p>· 立即返回Result<T,E>:</p>
<p>· 有数据达到:返回Ok,里面包含着数据</p>
<p>· 否则,返回错误</p>
<p>· 通常会使用循环调用来检查try_recv的结果</p>
<p><img src="/../assets/img/24-5.jpg" alt="图24-5" title="24-5"></p>
<p>如图所示:使用loop循环来进行监听,而如果此处不进行循环,在执行一次 try_recv 操作后就会结束</p>
<h3 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h3><h4 id="1-单主线程多工作线程实现线程池处理文件"><a href="#1-单主线程多工作线程实现线程池处理文件" class="headerlink" title="1. 单主线程多工作线程实现线程池处理文件"></a>1. 单主线程多工作线程实现线程池处理文件</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">use std::thread;</span><br><span class="line"></span><br><span class="line">use crossbeam_channel::{unbounded, Receiver, Sender};</span><br><span class="line"></span><br><span class="line">fn main() {</span><br><span class="line"></span><br><span class="line"> let file_paths = vec![</span><br><span class="line"> "file1.txt", "file2.txt", "file3.txt", "file4.txt",</span><br><span class="line"> "file5.txt", "file6.txt", "file7.txt", "file8.txt",</span><br><span class="line"> "file9.txt", "file10.txt",</span><br><span class="line"> "file11.txt", "file12.txt", "file13.txt", "file14.txt",</span><br><span class="line"> "file15.txt", "file16.txt", "file17.txt", "file18.txt",</span><br><span class="line"> "file19.txt", "file20.txt",</span><br><span class="line"> ];</span><br><span class="line"> // 创建一个可以被多个消费者共享的通道</span><br><span class="line"> let (tx, rx) = unbounded();</span><br><span class="line"></span><br><span class="line"> // 创建一个向通道发送任务的生产者线程</span><br><span class="line"> let producer_handle = std::thread::spawn(move || {</span><br><span class="line"> for i in 0..10{</span><br><span class="line"> tx.send(file_paths[i]).unwrap();</span><br><span class="line"> // std::thread::sleep(std::time::Duration::from_millis(100));</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> // 创建多个消费者线程</span><br><span class="line"> let num_consumers = 4;</span><br><span class="line"></span><br><span class="line"> let consumer_handles = (0..num_consumers).map(|i| {</span><br><span class="line"> let rx = rx.clone(); // `crossbeam-channel` 的 Receiver 可以被克隆</span><br><span class="line"> thread::spawn(move || {</span><br><span class="line"> while let Ok(task) = rx.recv() {</span><br><span class="line"> println!("Consumer processed task: {}, {}", task, i);</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }).collect::<Vec<_>>();</span><br><span class="line"></span><br><span class="line"> // 等待生产者线程结束</span><br><span class="line"> producer_handle.join().unwrap();</span><br><span class="line"></span><br><span class="line"> // 等待所有消费者线程结束</span><br><span class="line"> for handle in consumer_handles {</span><br><span class="line"> handle.join().unwrap();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/11/03/rust24/" data-id="cm31qkqgp000058uy2rtsa7vx" data-title="rust 学习笔记二十四 Rust的并发" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-rust23" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/10/31/rust23/" class="article-date">
<time class="dt-published" datetime="2024-10-31T15:29:54.176Z" itemprop="datePublished">2024-10-31</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/10/31/rust23/">rust 学习笔记二十三 Rust测试</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<p>Rust支持以下测试:</p>
<h3 id="(一)Unit"><a href="#(一)Unit" class="headerlink" title="(一)Unit"></a>(一)Unit</h3><p>单元测试实例如下:</p>
<p><img src="/../assets/img/23-1.jpg" alt="图23-1" title="23-1"></p>
<p>直接点击每一个mod上方的运行符号即可执行这个测试mod并在终端查看运行结果,在mod中每一个测试函数上方,都用一个属性宏#[test]来表示这是一个单元测试,在函数中,除了用assert_eq!()来判断是否执行成功以外,也可以通过返回Result类型,如下所示:</p>
<p><img src="/../assets/img/23-2.jpg" alt="图23-2" title="23-2"></p>
<p>当我们在这个函数中手动抛出一个Err()时,运行结果显示测试结果失败</p>
<p>已知暂未开发完的函数,不希望发生panic的情况下,需要在函数中调用todo!宏,并在函数上方注明 #[should_panic],即可跳过报错,如果需要跳过整个单元测试,则需要在函数上方注明: #[ignore]</p>
<h3 id="(二)Integration"><a href="#(二)Integration" class="headerlink" title="(二)Integration"></a>(二)Integration</h3><p>在根目录下新建tests文件夹,创建集成测试文件,可以在这里调用src 中lib文件中的单元测试函数,然后在控制台中执行cargo test,会先挨个进行单元测试,再进行集成测试,如果只需要执行集成测试,则在cargo test后加上—test 集成文件名,因为cargo 会将tests目录下的每一个文件都识别为一个crate,也可以通过cargo test 函数名来执行争对lib文件中某一个函数的单元测试,如果不同的单元测试中有一些共同的操作,可以将这些操作封装成函数,在tests目录下再新建子目录,在其中新建mod.rs文件,将函数封装在此处,因为直接在tests中新建文件的话也会被识别为测试文件</p>
<h3 id="(三)Docs"><a href="#(三)Docs" class="headerlink" title="(三)Docs"></a>(三)Docs</h3><p>文档测试写法如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">///Add to number</span><br><span class="line">/// #Example</span><br><span class="line">/// ```rust</span><br><span class="line">/// assert_eq!(rust_lib::add(1,1),2)</span><br><span class="line">/// ```</span><br><span class="line">/// # Panic</span><br><span class="line">/// # Errors</span><br><span class="line">/// # Safety</span><br></pre></td></tr></table></figure>
<p>在终端通过<code>cargo test –doc</code>执行文档测试</p>
<h3 id="(四)Examples"><a href="#(四)Examples" class="headerlink" title="(四)Examples"></a>(四)Examples</h3><p>当大型项目开发完后,需要写一些demo示范如何使用:在根目录下创建example目录,可以创建一个文件,在其中调用写好的方法,也可以创建一些测试用例:<br>此时可以在控制台输入<code>cargo test – example crate名</code>执行其中的测试用例,也可以通过<code>cargo test – examples</code>执行examples目录下所有测试</p>
<h3 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h3><p>创建一个带各种测试的crate并通过ci yaml推送到crates.io并同步更新</p>
<p><a target="_blank" rel="noopener" href="https://github.com/liucanzhu/rust-test">https://github.com/liucanzhu/rust-test</a></p>
<p><a target="_blank" rel="noopener" href="https://crates.io/crates/lcz-rust-test">https://crates.io/crates/lcz-rust-test</a></p>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/10/31/rust23/" data-id="cm2yv4hkr00002suyakob2tf2" data-title="rust 学习笔记二十三 Rust测试" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-rust22" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/10/29/rust22/" class="article-date">
<time class="dt-published" datetime="2024-10-29T14:51:01.745Z" itemprop="datePublished">2024-10-29</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/10/29/rust22/">rust 学习笔记二十二 Rust中的迭代器&关联类型</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<h3 id="(一)什么是迭代器"><a href="#(一)什么是迭代器" class="headerlink" title="(一)什么是迭代器"></a>(一)什么是迭代器</h3><p>迭代器模式:对一系列项执行某些任务</p>
<p>迭代器负责遍历每个项,确定序列(遍历)何时完成</p>
<p>Rust的迭代器是懒惰的,除非调用<strong>消费迭代器</strong>的方法,否则迭代器本身没有有任何效果</p>
<h3 id="(二)迭代器的实现"><a href="#(二)迭代器的实现" class="headerlink" title="(二)迭代器的实现"></a>(二)迭代器的实现</h3><p>迭代器是一个能够逐一生成元素的对象。它提供了一个统一的接口,用于遍历容器中的元素,同时保证了类型安全和内存安全。在Rust中,迭代器是实现了Iterator trait的对象。该trait定义了一个<strong>next方法,用于返回下一个元素</strong>。所有迭代器都实现了Iterator Trait,Iterator Trait定义于标准库,定义大致如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">pub trait Iterator{</span><br><span class="line"> type Item;</span><br><span class="line"> fn next(&mut self)-> Option<Self::Item>;</span><br><span class="line"> //methods with default inplementations elided</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>type item和Self::item定义了与此该trait关联的类型。</p>
<p>实现Iterator trait需要你定义一个Item类型,它用于<strong>next方法的返回类型(迭代器的返回类型)</strong></p>
<h3 id="(三)关联类型"><a href="#(三)关联类型" class="headerlink" title="(三)关联类型"></a>(三)关联类型</h3><p>关联类型是Trait中的类型占位符,它可以用于Trait的方法签名中,可以定义出包含某些类型的Trait,而在实现前无需知道这些类型是什么</p>
<h4 id="关联类型与泛型的区别"><a href="#关联类型与泛型的区别" class="headerlink" title="关联类型与泛型的区别"></a>关联类型与泛型的区别</h4><p>范型</p>
<p>每次实现Trait时标注类型<br>可以为一个类型多次实现某个Trait(不同的泛型参数)</p>
<p>关联类型</p>
<p>无需标注类型<br>无法为单个类型多次实现某个Trait</p>
<h3 id="(四)简单迭代器"><a href="#(四)简单迭代器" class="headerlink" title="(四)简单迭代器"></a>(四)简单迭代器</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">let numbers=vec![1,2,3,4,5];</span><br><span class="line">let mut iter=numbers.iter();//创建迭代器</span><br><span class="line">while let Some(num)=iter.next(){//使用迭代器逐一获取元素</span><br><span class="line"> println!("{}",num);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">let v1 = vec![1,2,3];</span><br><span class="line">let v1_iter = v1.iter();</span><br><span class="line">for val in v1_iter{</span><br><span class="line"> println!("{:?}",val);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>解释:<br>numbers.iter()创建了一个迭代器,该迭代器按顺序返回 numbers向量中的每个元素。<br>iter.next()返回迭代器中的下一个元素。如果没有更更多元素,返回None</p>
<p>在上面的两种遍历方法中,如果是使用 while let Some(xxx)=iter.next()的语法遍历,要求iter必须声明为mut,而for…in iter的语法遍历,却不用将其声明为mut,可以理解为,在第一种遍历方式中,每一次next,都会消耗迭代器中的一个元素,会<strong>使迭代器发生变化</strong>,所以需要将其声明为mut,而是用for in时,是通过引用获取到其中每一个变量的所有权,而不改变迭代器本身</p>
<h3 id="(五)几个迭代的方法"><a href="#(五)几个迭代的方法" class="headerlink" title="(五)几个迭代的方法"></a>(五)几个迭代的方法</h3><p>iter方法:在不可变引用上创建迭代器</p>
<p>into_iter方法:创建的迭代器会获得所有权</p>
<p>iter_mut方法:迭代可变的引用</p>
<h3 id="(六)消耗迭代器的方法"><a href="#(六)消耗迭代器的方法" class="headerlink" title="(六)消耗迭代器的方法"></a>(六)消耗迭代器的方法</h3><p>在标准库中,Iterator trait有一些带默认实现的方法,其中有一些方法会调用next方法,这也是实现Iterator trait时必须实现next方法的原因之一</p>
<p><strong>调用next的方法叫做”消耗型适配器”,因为调用它们会把迭代器消耗尽</strong></p>
<p>例如:sum方法(就会耗尽迭代器)<br>。取得迭代器的所有权<br>。通过反复调用next,遍历所有元素<br>。每次迭代,把当前元素添加到一个总和里,迭代结束,返回总和</p>
<h3 id="(七)产生其他迭代器的方法"><a href="#(七)产生其他迭代器的方法" class="headerlink" title="(七)产生其他迭代器的方法"></a>(七)产生其他迭代器的方法</h3><p>定义在Iterator trait上的另外一些方法叫做”迭代器适配器”,把迭代器转换为不同种类的迭代器,可以通过链式调用使用多个迭代器适配器来执行复杂的操作,这种调用可读性较高。</p>
<p>比如:</p>
<h4 id="1-map方法"><a href="#1-map方法" class="headerlink" title="1.map方法"></a>1.map方法</h4><p>map方法允许我们对迭代器中的每个元素应用一个函数,并返回一个新的迭代器。</p>
<p>示例:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">fn main(){</span><br><span class="line"> let numbers=vec![1,2,3,4,5];</span><br><span class="line"> let squares:Vec<i32>=numbers.iter()</span><br><span class="line"> .map(|x|x*x)//每个元素平方</span><br><span class="line"> .collect();//收集结果</span><br><span class="line"> println!("{:?}",squares);//输出[1,4,9,16,25]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>解释:</p>
<p>map将一个闭包应用于每个元素,这里是计算平方。</p>
<p>collect消耗型适配器,将迭代器的结果收集到一个容器中,这里是Vec<i32>。</p>
<h4 id="2-filter-方法"><a href="#2-filter-方法" class="headerlink" title="2.filter 方法"></a>2.filter 方法</h4><p>filter方法允许我们根据条件筛选元素,并返回满足条件的元素的迭代器</p>
<p>示例:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">fn main(){</span><br><span class="line"> let numbers=vec![1,2,3,4,5];</span><br><span class="line"> let even_numbers:Vec<_>=numbers.iter()</span><br><span class="line"> .filter(|&x|x % 2==0)//筛选偶数</span><br><span class="line"> .collect();</span><br><span class="line"> println!("{:?}",even_numbers);//输出[2,4]</span><br><span class="line">} </span><br></pre></td></tr></table></figure>
<p>解释:</p>
<p>filter筛选出所有满足闭包条件(偶数)的元素。</p>
<h3 id="(八)自定义迭代器"><a href="#(八)自定义迭代器" class="headerlink" title="(八)自定义迭代器"></a>(八)自定义迭代器</h3><p>即实现 Iterator trait</p>
<p>可以通过实现Iterator trait来创建自己的迭代器。<strong>必须实现next方法</strong>,该方法定义了迭代器如何产生下一个元素。</p>
<p>示例:自定义计数迭代器</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">struct Counter {</span><br><span class="line"> count: u32,</span><br><span class="line">}</span><br><span class="line">impl Counter{</span><br><span class="line"> fn new() -> Counter {</span><br><span class="line"> Counter{count:0}</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">impl Iterator for Counter{</span><br><span class="line"> type Item=u32;</span><br><span class="line"> </span><br><span class="line"> fn next(&mut self) -> Option<Self::Item> {</span><br><span class="line"> self.count += 1;</span><br><span class="line"> if self.count <= 5 {</span><br><span class="line"> Some(self.count)</span><br><span class="line"> }else{</span><br><span class="line"> None</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">fn main() {</span><br><span class="line"> let mut counter = Counter::new();</span><br><span class="line"> while let Some(count)= counter.next(){</span><br><span class="line"> println!("{}",count)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>解释:</p>
<p>Counter结构体实现了Iterator trait,其中next方法每次返回下一个计数值,直到达到5为止.</p>
<h3 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h3><p>用自定义迭代器生成斐波那契数列</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">struct Fibonacci {</span><br><span class="line"> curr: u32,</span><br><span class="line"> next: u32,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">impl Fibonacci {</span><br><span class="line"> fn new() -> Self {</span><br><span class="line"> Fibonacci {</span><br><span class="line"> curr: 0,</span><br><span class="line"> next: 1,</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fn into_vec(&mut self, count: usize) -> Vec<u64> {</span><br><span class="line"> let mut vec:Vec<u64> = Vec::new();</span><br><span class="line"> for _ in 0..count {</span><br><span class="line"> vec.push(self.next().unwrap().into());</span><br><span class="line"> }</span><br><span class="line"> vec</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// Implement `Iterator` for `Fibonacci`.</span><br><span class="line">// The `Iterator` trait only requires a method to be defined for the `next` element.</span><br><span class="line">impl Iterator for Fibonacci {</span><br><span class="line"> // We can refer to this type using Self::Item</span><br><span class="line"> type Item = u32;</span><br><span class="line"> </span><br><span class="line"> /* Implement next method */</span><br><span class="line"> fn next(&mut self) -> Option<Self::Item>{</span><br><span class="line"> if self.curr > self.next {</span><br><span class="line"> self.next = self.next + self.curr;</span><br><span class="line"> Some(self.curr)</span><br><span class="line"> }else {</span><br><span class="line"> self.curr = self.next + self.curr;</span><br><span class="line"> Some(self.next)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">fn main() {</span><br><span class="line"> let fib = Fibonacci::new();</span><br><span class="line"> </span><br><span class="line"> for number in fib.take(10) {</span><br><span class="line"> println!("{}", number);</span><br><span class="line"> }</span><br><span class="line"> let mut fib = Fibonacci::new();</span><br><span class="line"> let vec = fib.into_vec(5);</span><br><span class="line"> println!("{:?}", vec);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/10/29/rust22/" data-id="cm2w1xdy3000028uy5t66aqa4" data-title="rust 学习笔记二十二 Rust中的迭代器&关联类型" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-rust21" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/10/27/rust21/" class="article-date">
<time class="dt-published" datetime="2024-10-27T10:46:54.547Z" itemprop="datePublished">2024-10-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/10/27/rust21/">rust 学习笔记二十一 Rust中的闭包</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<h3 id="(一)概念"><a href="#(一)概念" class="headerlink" title="(一)概念"></a>(一)概念</h3><p>闭包是一个可以捕获所在环境中的变量的匿名函数,在Rust中,闭包通过||符号定义,可以像普通函数一样调用,但与函数不同,闭包可以访问外部作用域的变量。</p>
<h3 id="(二)特点"><a href="#(二)特点" class="headerlink" title="(二)特点"></a>(二)特点</h3><ol>
<li><p>可以捕获周围作用域的变量。</p>
</li>
<li><p>支持作为参数传递给其他函数。</p>
</li>
<li><p>可以返回闭包作为函数的返回值。</p>
</li>
<li><p>类型推断:闭包通常通过类型推断来确定参数和返回值的类型。</p>
</li>
</ol>
<h3 id="三-定义"><a href="#三-定义" class="headerlink" title="(三)定义"></a>(三)定义</h3><h4 id="1-闭包的语法"><a href="#1-闭包的语法" class="headerlink" title="1.闭包的语法"></a>1.闭包的语法</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Let closure_name=|参数列表|->返回类型{ </span><br><span class="line"> 代码块</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-简单示例"><a href="#2-简单示例" class="headerlink" title="2.简单示例"></a>2.简单示例</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">let add_one = |x:i32|-> i32{x+1};</span><br><span class="line">println!("{}", add_one(5));//输出:6</span><br></pre></td></tr></table></figure>
<h4 id="3-省略类型的闭包"><a href="#3-省略类型的闭包" class="headerlink" title="3.省略类型的闭包"></a>3.省略类型的闭包</h4><p>Rust可以推断闭包的参数和返回值类型,因此在很多情况下可以省略类型声明。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">let add_one = |x| |x|+1;</span><br><span class="line">println!("{}",add_one(5));//输出:6</span><br></pre></td></tr></table></figure>
<h3 id="(四)使用"><a href="#(四)使用" class="headerlink" title="(四)使用"></a>(四)使用</h3><h4 id="1-作为函数参数"><a href="#1-作为函数参数" class="headerlink" title="1.作为函数参数"></a>1.作为函数参数</h4><p>闭包可以作为函数的参数传递,从而实现更灵活的代码结构。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">fn apply_to_3<F>(f: F) -> i32{</span><br><span class="line">where</span><br><span class="line">F:Fn(i32)->i32,</span><br><span class="line">{</span><br><span class="line">f(3)</span><br><span class="line">}</span><br><span class="line">let double= |x| x * 2;</span><br><span class="line">println!("{}",apply_to_3(double));//输出:6</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-捕获环境变量"><a href="#2-捕获环境变量" class="headerlink" title="2. 捕获环境变量"></a>2. 捕获环境变量</h4><p>闭包可以捕获并使用其定义所在环境中的变量</p>
<p><img src="/../assets/img/21-1.jpg" alt="图21-1" title="21-1"></p>
<p>从图中可以看出,当闭包尝试访问外界变量x时,代码正常执行,当函数尝试访问x时,报错<code>:can't capture dynamic environment in a fn item</code></p>
<h4 id="3-闭包的三种捕获方式"><a href="#3-闭包的三种捕获方式" class="headerlink" title="3. 闭包的三种捕获方式"></a>3. 闭包的三种捕获方式</h4><p>按值捕获:将环境变量的所有权移入闭包。</p>
<p>按引用捕获:通过引用捕获环境变量。</p>
<p>按可变引用捕获:通过可变引用捕获环境变量。</p>
<p>示例:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">let mut num = 5;</span><br><span class="line">// 按引用捕获</span><br><span class="line">let add_num = |x: i32|x + num;</span><br><span class="line">println!("{}",add_num(3));//输出:8</span><br><span class="line">//按可变引用捕获</span><br><span class="line">let mut change_num = |x: i32|num += x;</span><br><span class="line">change_num(5);</span><br><span class="line">println!{"{}",num};//输出:10</span><br></pre></td></tr></table></figure>
<h3 id="(四)闭包原理"><a href="#(四)闭包原理" class="headerlink" title="(四)闭包原理"></a>(四)闭包原理</h3><h4 id="1-自动实现的函数类型"><a href="#1-自动实现的函数类型" class="headerlink" title="1.自动实现的函数类型"></a>1.自动实现的函数类型</h4><p>Fn、FnMut和FnOnce是Rust提供的三种函数闭包类型,分别表示按引用捕获、按可变引用捕获和按值捕获。</p>
<h4 id="2-闭包的类型推断"><a href="#2-闭包的类型推断" class="headerlink" title="2.闭包的类型推断"></a>2.闭包的类型推断</h4><p>Rust能够根据闭包的使用上下文推断出闭包的具体类型。</p>
<p>Fn、FnMut和FnOnce是闭包在不同情况下自动实现的为trait</p>
<h4 id="3-生命周期与闭包"><a href="#3-生命周期与闭包" class="headerlink" title="3.生命周期与闭包"></a>3.生命周期与闭包</h4><p>闭包可以捕获引用,但需要保证引用的生命周期超过闭包的生命周期</p>
<p>示例:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">let s=String::from("hello");</span><br><span class="line">let closure=|| println!("{}",s);</span><br><span class="line">closure();//正常运行,因为s在closure之前有效</span><br></pre></td></tr></table></figure>
<p>创建闭包时,通过闭包对环境值的使用,Rust推断出具体使用哪个trait</p>
<p>·所有的闭包都实现了FnOnce</p>
<p>·没有移动捕获变量的实现了FnMut</p>
<p>·无需可变访问频获变量的闭包实现了Fn</p>
<h3 id="(五)move关键字"><a href="#(五)move关键字" class="headerlink" title="(五)move关键字"></a>(五)move关键字</h3><p>在参数列表前使用move关键字,可以强制闭包取得它所使用的环境值的所有权,当将闭包传递给新线程以移动数据使其归新线程所有时,此技术最为有用。</p>
<p>例子</p>
<p><img src="/../assets/img/21-2.jpg" alt="图21-2" title="21-2"></p>
<p>如图所示,闭包获取了外界值x和y的所有权,之后二者都不能在打印</p>
<h3 id="六-实践"><a href="#六-实践" class="headerlink" title="(六) 实践"></a>(六) 实践</h3><p>实现PageCache结构体:<br>该结构体应缓存根据用户ID和文章ID渲染的页面。<br>你需要为该结构体实现一个get_page方法,该方法接受用户ID和文章ID,并返回演染后的页面内容。<br>如果相同的用户ID和文章ID已经渲染过,则get_page应直接返回缓存的页面,而不是重新渲染。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">use std::collections::HashMap;</span><br><span class="line"></span><br><span class="line">// 定义 PageCache 结构体</span><br><span class="line">struct PageCache<F></span><br><span class="line">where</span><br><span class="line"> F: Fn(&str, &u32) -> String,</span><br><span class="line">{</span><br><span class="line"> render_page: F,</span><br><span class="line"> cache: HashMap<(String, u32), String>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">impl<F> PageCache<F></span><br><span class="line">where</span><br><span class="line"> F: Fn(&str, &u32) -> String,</span><br><span class="line">{</span><br><span class="line"> // 实现构造器方法</span><br><span class="line"> fn new(render_page: F) -> Self {</span><br><span class="line"> PageCache {</span><br><span class="line"> render_page,</span><br><span class="line"> cache: HashMap::new(),</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 实现 get_page 方法</span><br><span class="line"> fn get_page(&mut self, user_id: &str, article_id: u32) -> String {</span><br><span class="line"> let key = (user_id.to_string(), article_id);</span><br><span class="line"> if !self.cache.contains_key(&key) {</span><br><span class="line"> let rendered_page = (self.render_page)(user_id, &article_id);</span><br><span class="line"> self.cache.insert(key.clone(), rendered_page);</span><br><span class="line"> }</span><br><span class="line"> self.cache.get(&key).unwrap().to_string()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fn main() {</span><br><span class="line"> let mut page_cache = PageCache::new(|user_id, article_id| {</span><br><span class="line"> println!("Rendering page for user {} and article {}", user_id, article_id);</span><br><span class="line"> format!("Rendered HTML content for user {} and article {}", user_id, article_id)</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> // 第一次调用,会执行页面渲染</span><br><span class="line"> println!("{}", page_cache.get_page("user1", 42)); // 输出 "Rendering page for user user1 and article 42" 和 "Rendered HTML content for user user1 and article 42"</span><br><span class="line"> // 第二次调用,直接返回缓存结果</span><br><span class="line"> println!("{}", page_cache.get_page("user1", 42)); // 仅输出 "Rendered HTML content for user user1 and article 42",不再渲染</span><br><span class="line"> // 不同用户查看同一文章,会重新渲染</span><br><span class="line"> println!("{}", page_cache.get_page("user2", 42)); // 输出 "Rendering page for user user2 and article 42" 和 "Rendered HTML content for user user2 and article 42"</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/10/27/rust21/" data-id="cm2rh3xgq0000y4uygydsgx14" data-title="rust 学习笔记二十一 Rust中的闭包" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-rust20" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/10/26/rust20/" class="article-date">
<time class="dt-published" datetime="2024-10-25T16:02:23.857Z" itemprop="datePublished">2024-10-26</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/10/26/rust20/">rust 学习笔记二十 Rust中的宏</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<p>Rust中的宏主要分为两种:</p>
<h3 id="(一)声明式宏"><a href="#(一)声明式宏" class="headerlink" title="(一)声明式宏"></a>(一)声明式宏</h3><p>声明宏(Declarative Macros),也称为”macro_rules!”,是最常见的宏类型。它们允许你通过模式匹配来生成代码。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">fn main(){</span><br><span class="line"> say_hello!();</span><br><span class="line"> say_hello! {};</span><br><span class="line"> let var_name = say_hello![];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">//定义一个简单的宏来打印消息</span><br><span class="line"></span><br><span class="line">#[macro_export]</span><br><span class="line">macro_rules! say_hello {</span><br><span class="line"> () => {</span><br><span class="line"> println!("Hello, world!");</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>和函数相比,声明宏在调用时候需要加!,且可以传入变参,并且从上面的代码可以发现,调用时可以用[], {}, ()三种方式</p>
<p>Rust中常见的声明式宏主要有:println format等</p>
<h3 id="(二)过程宏"><a href="#(二)过程宏" class="headerlink" title="(二)过程宏"></a>(二)过程宏</h3><p>过程宏(Procedural Macros)允许你使用函数生成代码。它们分为三种类型:</p>
<h4 id="1-派生宏"><a href="#1-派生宏" class="headerlink" title="1.派生宏"></a>1.派生宏</h4><p>常用的derive 主要用在struct和enum中</p>
<p>创建:</p>
<p>创建一个新项目,在toml中声明:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[lib]</span><br><span class="line">proc-macro = true</span><br></pre></td></tr></table></figure>
<p>表示这是一个扩展宏, 然后再依赖中添加 quote 和 syn 用于将我们的代码与AST树之间相互转换</p>
<p>举例:</p>
<p>创建:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">use proc_macro:: TokenStream;</span><br><span class="line">use quote::quote;</span><br><span class="line">#[proc_macro_derive(HelloMacro)]</span><br><span class="line">pub fn hello_macro_derive(input: TokenStream) -> TokenStream {</span><br><span class="line"> let ast=syn::parse(input).unwrap();</span><br><span class="line"> let name = &ast.ident;</span><br><span class="line"> let gen = quote!{</span><br><span class="line"> impl HelloMacro for #name {</span><br><span class="line"> fn hello_macro(){</span><br><span class="line"> println!("Hello, Macro! My name is {}!", stringify!(#name));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> gen.into()</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>调用:导入之后通过derive衍生</p>
<h4 id="2-属性宏-Attribute-like-macro"><a href="#2-属性宏-Attribute-like-macro" class="headerlink" title="2.属性宏(Attribute-like macro)"></a>2.属性宏(Attribute-like macro)</h4><p>比如:在测试前面声明的#[test],指定一个条件编译的配置选项:<code>#![cfg(target_arch)]</code>, 用于抑制编译器对未使用代码的警告:#![allow(dead_code)]<br>声明:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">#[route(GET, "/")]</span><br><span class="line">fn index()</span><br><span class="line">#[proc_macro_attribute]</span><br><span class="line">pub fn route(attr:TokenStream,item:TokenStream) -> TokenStream {</span><br></pre></td></tr></table></figure>
<h4 id="3-函数宏-Function-like-macro"><a href="#3-函数宏-Function-like-macro" class="headerlink" title="3.函数宏(Function-like macro)"></a>3.函数宏(Function-like macro)</h4><p>比如 dbg!: assert! 和 assert_eq!等</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#[proc_macro]</span><br><span class="line">pub fn sql(input: TokenStream) ->TokenStream{</span><br></pre></td></tr></table></figure>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>创建声明宏:使用 macro_rules!定义宏,通过模式匹配生成代码。<br>创建过程宏:使用过程宏函数<code>(#[proc_macro])</code>、派生宏<code>(#[proc_macro_derive])</code>和属性宏<code>(#[proc_macro_attribute])</code>生成代码。</p>
<p>宏的应用场景<br>1.减少重复代码:通过宏生成重复的代码。<br>2.编译期计算:在<strong>编译期</strong>进行计算并生成代码,提高运行时性能。<br>3.DSL(领域特定语言):使用宏定义领域特定语言,提高代码的表达力和可读性。</p>
<h3 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h3><p>使用通过<code>macro_rules!</code>实现对应的macro</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">//定义宏</span><br><span class="line">macro_rules! repeat {</span><br><span class="line"> ($string:expr, $n:expr) => {{</span><br><span class="line"> let mut result = String::new();</span><br><span class="line"> for _ in 0..$n {</span><br><span class="line"> result.push_str($string);</span><br><span class="line"> }</span><br><span class="line"> result</span><br><span class="line"> }};</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">macro_rules! sum {</span><br><span class="line"> ($($val:expr),+) => {{</span><br><span class="line"> let mut total = 0;</span><br><span class="line"> $(</span><br><span class="line"> total += $val;</span><br><span class="line"> )+</span><br><span class="line"> total</span><br><span class="line"> }};</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">macro_rules! max_value {</span><br><span class="line"></span><br><span class="line"> ($x:expr) => { $x };</span><br><span class="line"></span><br><span class="line"> ($x:expr, $($rest:expr),+) => {</span><br><span class="line"> if $x > max_value!($($rest),+) {</span><br><span class="line"> $x</span><br><span class="line"> } else {</span><br><span class="line"> max_value!($($rest),+)</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fn main() {</span><br><span class="line"> // 使用宏</span><br><span class="line"> assert_eq!(repeat!("x", 3), "xxx");</span><br><span class="line"> assert_eq!(sum!(1, 2, 3, 4, 5), 15);</span><br><span class="line"> assert_eq!(max_value!(1, 8, 9), 9);</span><br><span class="line"> </span><br><span class="line"> println!("All tests passed!");</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/10/26/rust20/" data-id="cm2oxbt5z00003guyeo4qappl" data-title="rust 学习笔记二十 Rust中的宏" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<article id="post-rust19" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<div class="article-meta">
<a href="/2024/10/22/rust19/" class="article-date">
<time class="dt-published" datetime="2024-10-22T15:33:34.843Z" itemprop="datePublished">2024-10-22</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="p-name article-title" href="/2024/10/22/rust19/">rust 学习笔记十九 Rust中的trait</a>
</h1>
</header>
<div class="e-content article-entry" itemprop="articleBody">
<h3 id="(一)概念"><a href="#(一)概念" class="headerlink" title="(一)概念"></a>(一)概念</h3><p>trait定义了某个特定类型拥有可能与其他类型共享的功能, 类似于其他语言中的常被称为接口(interfaces)的功能,!虽然有一些不同。</p>
<h3 id="(二)实现"><a href="#(二)实现" class="headerlink" title="(二)实现"></a>(二)实现</h3><p>使用trait关键字来声明一个特征, Summary是特征名, 在大括号中定义了该特征的所有方法, 只定义特征方法的签名,而不进行实现,此时方法签名结尾是;而不是一个{}。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">pub trait Summary{</span><br><span class="line"> fn summarize(&self) ->String;</span><br><span class="line">}</span><br><span class="line">为类型实现特征</span><br><span class="line">pub trait Summary{</span><br><span class="line">fn summarize(&self) ->String;</span><br><span class="line">}</span><br><span class="line">pub struct Post{</span><br><span class="line"> pub title:String,//标题</span><br><span class="line"> pub author:String,//作者</span><br><span class="line"> pub content:String,//内容</span><br><span class="line">}</span><br><span class="line">impl Summary for Post{</span><br><span class="line"> fn summarize(&self) -> String{</span><br><span class="line"> format!("文章{},作者是{}",self.title,self.author)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">pub struct Weibo{</span><br><span class="line"> pub username:String,</span><br><span class="line"> pub content: String</span><br><span class="line">}</span><br><span class="line">impl Summary for Weibo {</span><br><span class="line"> fn summarize(&self) ->String{</span><br><span class="line"> format!("{}发表了微博{}",self.username,self.content)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="(三)特征定义与实现的位置-孤儿规则"><a href="#(三)特征定义与实现的位置-孤儿规则" class="headerlink" title="(三)特征定义与实现的位置(孤儿规则)"></a>(三)特征定义与实现的位置(孤儿规则)</h3><p>如果你想要为结构体 A 实现特征 T,那么A或者 T 至少有一个是在当前作用域中定义的!(即不要为标准库中的类型实现标准库中的特征)</p>
<p>Trait中也可以直接对函数进行实现,这种情况下在为结构体实现trait时候就可以跳过已实现的方法,也可以对其进行重载</p>
<h3 id="(四)带泛型的trait"><a href="#(四)带泛型的trait" class="headerlink" title="(四)带泛型的trait"></a>(四)带泛型的trait</h3><p>在具体方法调用的时候,必须加以类型标注以明确使用的:是哪一个具体的实现</p>
<p>可以对同一个目标类型,多次impl此trait,每次提供不同的泛型参数</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">trait Converter<T> {</span><br><span class="line">fn convert(&self) -> T;</span><br><span class="line">}</span><br><span class="line">struct MyInt(i32);</span><br><span class="line">impl Converter<String>for MyInt {</span><br><span class="line"> fn convert(&self) ->String {</span><br><span class="line"> self.0.to_string()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">impl Converter<f32> for MyInt {</span><br><span class="line"> fn convert(&self) -> f32 {</span><br><span class="line"> self.0 as f32</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">fn main(){</span><br><span class="line"> let my_int = MyInt(42);</span><br><span class="line"> let output:String=my_int.convert();</span><br><span class="line"> println!("outputis:{}",output);</span><br><span class="line"> let output:f32 = my_int.convert();</span><br><span class="line"> println!("output is:{}", output);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在这种情况下,由于编译器不知道你需要的是针对哪种类型的同名方法convert,所以需要在变量output处显式的声明一下类型</p>
<h3 id="(五)关联类型"><a href="#(五)关联类型" class="headerlink" title="(五)关联类型"></a>(五)关联类型</h3><p>关联类型是trait定义中的类型占位符。定义的时候,并不定义它的具体的类型是什么。在impl这个trait的时候,才为这个关联类型赋予确定的类型。也就是说,在实现的时候,才知道它的具体类型是什么。<br>·关联类型方式<strong>只允许对目标类型实现一次</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">trait Converter{</span><br><span class="line"> type Output;</span><br><span class="line"> fn convert(&self) ->Self::Output;</span><br><span class="line">}</span><br><span class="line">impl Converter for MyInt{</span><br><span class="line"> type Output=String;</span><br><span class="line"> fn convert(&self) ->Self::Output {</span><br><span class="line"> self.0.to_string()</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="(六)默认泛型类型参数"><a href="#(六)默认泛型类型参数" class="headerlink" title="(六)默认泛型类型参数"></a>(六)默认泛型类型参数</h3><p>实现Add trait(也是一种关联类型的trait)时不指定Rhs的具体类型,Rhs的类型将是默认的Self类型,也就是在其上实现Add的类型。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">trait Add<Rhs=Self> {</span><br><span class="line"> type Output;</span><br><span class="line"> fn add(self,rhs:Rhs)->Self::Output;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>所以如果需要实现两种不同类型相加,需要在实现trait时声明另一种加数的类型:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">struct Millimeters(u32);</span><br><span class="line"></span><br><span class="line">struct Meters(u32);</span><br><span class="line"></span><br><span class="line">impl Add<Meters> for Millimeters {</span><br><span class="line"> type Output = Millimeters;</span><br><span class="line"></span><br><span class="line"> fn add(self, rhs: Meters) -> Millimeters {</span><br><span class="line"> Millimeters(self.0 + rhs.0 * 1000)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="(七)使用特征作为函数参数"><a href="#(七)使用特征作为函数参数" class="headerlink" title="(七)使用特征作为函数参数"></a>(七)使用特征作为函数参数</h3><h4 id="1-impl-Trait语法"><a href="#1-impl-Trait语法" class="headerlink" title="1.impl Trait语法"></a>1.impl Trait语法</h4><p>你可以使用任何实现了Summary特征的类型作为该函数的参数,除了单个约束条件,我们还可以通过+语法指定多个约束条件</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pub fn notify(item: &impl Summary) {</span><br><span class="line"> println!("Brenking news!{}", item.summarize());</span><br><span class="line">}</span><br><span class="line">pub fn notify(item: &(impl Summary + Display)) {}</span><br></pre></td></tr></table></figure>
<h4 id="2-Trait-Bound语法"><a href="#2-Trait-Bound语法" class="headerlink" title="2.Trait Bound语法"></a>2.Trait Bound语法</h4><p>impl Trait 适用于短小的例子,它trait bound语法糖, 更长的trait bound则适用于更复杂的场景,除了单个约束条件,我们还可以通过+语法指定多个约束条件</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pub fn notify<T:Summary>(ikem:&T){</span><br><span class="line">println!("Breaking news!{}", item.summarize());</span><br><span class="line">}</span><br><span class="line">pub fn notify<T:Summary + Display>(item:&T){}</span><br></pre></td></tr></table></figure>
<p>也可以改为如下写法:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">fn notify<T, U>(item1: &T, item2: &U) </span><br><span class="line">where T:Summary + Display,</span><br><span class="line"> U:Summary + Display + Debug,</span><br><span class="line">{</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>将泛型的类型声明写在后面增强代码易读性</p>
<h3 id="(八)实现方法"><a href="#(八)实现方法" class="headerlink" title="(八)实现方法"></a>(八)实现方法</h3><p>可以有条件地只为那些实现了特定trait的类型实现方法,只有那些为T类型实现了Partialord trait(来允许比较)和Display trait(来启用打印)的Pair<T>才会实现cmp_display 方法</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">use std::fmt::Display;</span><br><span class="line">struct Pair<T>{</span><br><span class="line"> x: T,</span><br><span class="line"> y: T</span><br><span class="line">}</span><br><span class="line">impl<T> Pair<T>{</span><br><span class="line"> fn new(x:T,y:T)->Self{</span><br><span class="line"> Self{x,y}</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">impl<T:Display + Partialord> Pair<T>{</span><br><span class="line"> fn cmp_display(&self){</span><br><span class="line"> if self.x>=self.y{</span><br><span class="line"> println!("The largest member is x={}",self.x);</span><br><span class="line"> }else{</span><br><span class="line"> println!("The largest member is y={}",self.y);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="(九)impl-Trait-语法"><a href="#(九)impl-Trait-语法" class="headerlink" title="(九)impl Trait 语法"></a>(九)impl Trait 语法</h3><p>在返回值中使用impl Trait语法,来返回实现了某个trait的类型</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">fn returns_summarizable() -> impl Summary {</span><br><span class="line"> Tweet{</span><br><span class="line"> username:String::from("horse_ebooks"),</span><br><span class="line"> content:String::from(</span><br><span class="line"> "of course, as you probably already know, people",</span><br><span class="line"> ),</span><br><span class="line"> reply: false,</span><br><span class="line"> retweet: false,</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>但如果根据不同情况返回值是实现了该特征的不同结构,直接在返回类型写impl trait会报错,需要使用impl<code><dyn trait></code>的格式来运行时判断返回类型</p>
<h3 id="trait-实践"><a href="#trait-实践" class="headerlink" title="trait 实践"></a>trait 实践</h3><p>应用了<strong>关联类型</strong>和<strong>动态分发</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line">use std::fmt::Display;</span><br><span class="line"></span><br><span class="line">trait Item<T = String>{</span><br><span class="line"> type Output:Display;</span><br><span class="line"> fn summarize(&self) -> Self::Output;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">struct Apple{</span><br><span class="line"> name: String,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">impl Item for Apple{</span><br><span class="line"> type Output = String;</span><br><span class="line"> fn summarize(&self) -> String{</span><br><span class="line"> self.name.to_string()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">struct Weibo{</span><br><span class="line"> author:String,</span><br><span class="line"> content:String,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">impl Item for Weibo{</span><br><span class="line"> type Output = String;</span><br><span class="line"> fn summarize(&self) -> String{</span><br><span class="line"> format!("@{}:{}",self.author,self.content)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">pub struct Container{</span><br><span class="line"> items:Vec<Box<dyn Item<Output = String>>> ,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">impl Container{</span><br><span class="line"> pub fn iterator(&self){</span><br><span class="line"> for item in self.items.iter(){</span><br><span class="line"> println!("{}",item.summarize());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">fn main(){</span><br><span class="line"> let apple=Apple {</span><br><span class="line"> name:"Apple".to_string(),</span><br><span class="line"> };</span><br><span class="line"> let w=Weibo{</span><br><span class="line"> author:"weibo".to_string(),</span><br><span class="line"> content:"hello".to_string(),</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> let container = Container {</span><br><span class="line"> items:vec![Box::new(apple), Box::new(w)],</span><br><span class="line"> };</span><br><span class="line"> container.iterator();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="https://liucanzhu.github.io/2024/10/22/rust19/" data-id="cm2klvmdr0000l0uy2n369t0y" data-title="rust 学习笔记十九 Rust中的trait" class="article-share-link"><span class="fa fa-share">Share</span></a>
</footer>
</div>
</article>
<nav id="page-nav">
<span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="page-number" href="/page/3/">3</a><a class="extend next" rel="next" href="/page/2/">Next »</a>
</nav>
</section>
<aside id="sidebar">
<div class="widget-wrap">
<h3 class="widget-title">Archives</h3>
<div class="widget">
<ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2024/11/">November 2024</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2024/10/">October 2024</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2024/09/">September 2024</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Recent Posts</h3>
<div class="widget">
<ul>
<li>
<a href="/2024/11/21/solana3/">solana 学习笔记三 Solana基础,账户与简单交互</a>
</li>
<li>
<a href="/2024/11/17/solana2/">solana 学习笔记二 Solana开发入门</a>
</li>
<li>
<a href="/2024/11/13/solana1/">solana 学习笔记一 Solana基础与环境搭建</a>
</li>
<li>
<a href="/2024/11/09/rust25/">rust 学习笔记二十五 Rust中的共享内存</a>
</li>
<li>
<a href="/2024/11/03/rust24/">rust 学习笔记二十四 Rust的并发</a>
</li>
</ul>
</div>
</div>
</aside>
</div>
<footer id="footer">
<div class="outer">
<div id="footer-info" class="inner">
© 2024 Liu Canzhu<br>