-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
663 lines (320 loc) · 453 KB
/
search.xml
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Rust学习笔记:函数作为参数</title>
<link href="/posts/i5v2u2.html"/>
<url>/posts/i5v2u2.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><p>在之前<code>闭包</code>的文章中,我们可以知道,在Rust中函数也可以像在Python、Nodejs一样作为参数传入到另一个函数中,具体使用时主要有两种方法来实现</p><h2 id="闭包作为参数"><a href="#闭包作为参数" class="headerlink" title="闭包作为参数"></a>闭包作为参数</h2><p>在之前的文章中讲到闭包对捕获的变量所有权有三种特性:<code>Fn</code>、<code>FnMut</code>和<code>FnOnce</code>,其分别表示了闭包在捕获环境时的不同方式。 </p><h3 id="Fn闭包作为参数"><a href="#Fn闭包作为参数" class="headerlink" title="Fn闭包作为参数"></a>Fn闭包作为参数</h3><p><code>Fn</code>:闭包通过不可变借用捕获环境中的变量,可以在多次调用中复用,不会改变捕获的变量。</p><figure class="highlight rust"><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"><span class="comment">// 定义一个函数,接受一个实现了 Fn 特性的闭包作为参数</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">apply_fn</span><F>(f: F, value: <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span></span><br><span class="line"><span class="keyword">where</span></span><br><span class="line"> F: <span class="title function_ invoke__">Fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>,</span><br><span class="line">{</span><br><span class="line"> <span class="title function_ invoke__">f</span>(value)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 使用一个简单的闭包,返回输入的两倍</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result1</span> = <span class="title function_ invoke__">apply_fn</span>(|x| x * <span class="number">2</span>, <span class="number">5</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result1: {}"</span>, result1); <span class="comment">// 输出 Result1: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用闭包捕获环境,但不改变环境变量的值</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">offset</span> = <span class="number">3</span>;</span><br><span class="line"> <span class="comment">// 使用一个闭包,返回输入加上捕获的环境变量</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result2</span> = <span class="title function_ invoke__">apply_fn</span>(|x| x + offset, <span class="number">5</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result2: {}"</span>, result2); <span class="comment">// 输出 Result2: 8</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"factor: {}"</span>, factor); <span class="comment">// factor的值没有改变</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="FnMut闭包作为参数"><a href="#FnMut闭包作为参数" class="headerlink" title="FnMut闭包作为参数"></a>FnMut闭包作为参数</h3><p><code>FnMut</code>:闭包通过可变借用捕获环境中的变量,可以修改捕获的变量。</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">apply_fn_mut</span><F>(<span class="keyword">mut</span> f: F, value: <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span></span><br><span class="line"><span class="keyword">where</span></span><br><span class="line"> F: <span class="title function_ invoke__">FnMut</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>,</span><br><span class="line">{</span><br><span class="line"> <span class="title function_ invoke__">f</span>(value)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 使用 FnMut 闭包</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">factor</span> = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result1</span> = <span class="title function_ invoke__">apply_fn_mut</span>(</span><br><span class="line"> |x| {</span><br><span class="line"> factor *= <span class="number">2</span>;</span><br><span class="line"> x * factor</span><br><span class="line"> },</span><br><span class="line"> <span class="number">5</span>,</span><br><span class="line"> );</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result1 (FnMut): {}"</span>, result1); <span class="comment">// 输出 Result1 (FnMut): 20</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"factor: {}"</span>, factor); <span class="comment">// 输出:factor: 4</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行上面的代码可以发现,变量<code>factor</code>的值发生了改变</p><h3 id="FnOnce闭包作为参数"><a href="#FnOnce闭包作为参数" class="headerlink" title="FnOnce闭包作为参数"></a>FnOnce闭包作为参数</h3><p><code>FnOnce</code>:闭包通过值捕获环境中的变量,消耗捕获的变量,这种闭包只能调用一次。</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">apply_fn_once</span><F>(f: F, value: <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span></span><br><span class="line"><span class="keyword">where</span></span><br><span class="line"> F: <span class="title function_ invoke__">FnOnce</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>,</span><br><span class="line">{</span><br><span class="line"> <span class="title function_ invoke__">f</span>(value)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 变量factor是一个String类型,是一个非Copy类型</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">factor</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"2"</span>);</span><br><span class="line"> <span class="comment">// 使用move关键字强制按值捕获</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result2</span> = <span class="title function_ invoke__">apply_fn_once</span>(<span class="keyword">move</span> |x| x * factor.parse::<<span class="type">i32</span>>().<span class="title function_ invoke__">unwrap</span>(), <span class="number">5</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result2 (FnOnce): {}"</span>, result2);</span><br><span class="line"> <span class="comment">// 下面这行会编译错误,因为factor的所有权已经被移动</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, factor);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="函数指针作为参数"><a href="#函数指针作为参数" class="headerlink" title="函数指针作为参数"></a>函数指针作为参数</h2><p>通过以上的例子可以看出,使用闭包在非常方便的将函数作为参数传入到其他函数中(闭包也是一种函数),但使用闭包不容易编写逻辑性较为复杂的代码,否则可能会造成代码难以阅读,这时我们可以使用函数指针</p><h3 id="函数指针作为参数-1"><a href="#函数指针作为参数-1" class="headerlink" title="函数指针作为参数"></a>函数指针作为参数</h3><p>下面是一个最简单的函数指针作为参数的例子</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// apply函数接受一个函数指针f和一个整数x作为参数</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">apply</span>(f: <span class="title function_ invoke__">fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>, x: <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span> {</span><br><span class="line"> <span class="title function_ invoke__">f</span>(x)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">double</span>(x: <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span> {</span><br><span class="line"> x * <span class="number">2</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result</span> = <span class="title function_ invoke__">apply</span>(double, <span class="number">5</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result: {}"</span>, result); <span class="comment">// 输出: Result: 10</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="多个函数指针参数"><a href="#多个函数指针参数" class="headerlink" title="多个函数指针参数"></a>多个函数指针参数</h3><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">process</span>(input: <span class="type">i32</span>, f1: <span class="title function_ invoke__">fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>, f2: <span class="title function_ invoke__">fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span> {</span><br><span class="line"> <span class="title function_ invoke__">f2</span>(<span class="title function_ invoke__">f1</span>(input))</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">double</span>(x: <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span> { x * <span class="number">2</span> }</span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">square</span>(x: <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span> { x * x }</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result</span> = <span class="title function_ invoke__">process</span>(<span class="number">3</span>, double, square);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result: {}"</span>, result); <span class="comment">// 输出: Result: 36</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="函数指针作为返回值"><a href="#函数指针作为返回值" class="headerlink" title="函数指针作为返回值"></a>函数指针作为返回值</h3><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">get_operation</span>(op: &<span class="type">str</span>) <span class="punctuation">-></span> <span class="title function_ invoke__">fn</span>(<span class="type">i32</span>, <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span> {</span><br><span class="line"> <span class="keyword">match</span> op {</span><br><span class="line"> <span class="string">"add"</span> => |x, y| x + y,</span><br><span class="line"> <span class="string">"multiply"</span> => |x, y| x * y,</span><br><span class="line"> _ => |x, _| x,</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">add_op</span> = <span class="title function_ invoke__">get_operation</span>(<span class="string">"add"</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result: {}"</span>, <span class="title function_ invoke__">add_op</span>(<span class="number">5</span>, <span class="number">3</span>)); <span class="comment">// 输出: Result: 8</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="特征对象"><a href="#特征对象" class="headerlink" title="特征对象"></a>特征对象</h3><p>结合之前<code>Box<T></code>智能指针,可以更灵活的使用函数指针,比如下面的例子</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">functions</span>: <span class="type">Vec</span><<span class="type">Box</span><<span class="keyword">dyn</span> <span class="title function_ invoke__">Fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>>> = <span class="built_in">vec!</span>[</span><br><span class="line"> <span class="comment">// vector中的数据类型必须一致且大小需要在编译中确定</span></span><br><span class="line"> <span class="comment">// 这里使用指针类型保证编译时类型和大小一致</span></span><br><span class="line"> <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(|x| x + <span class="number">1</span>),</span><br><span class="line"> <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(|x| x * <span class="number">2</span>),</span><br><span class="line"> <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(|x| x * x),</span><br><span class="line"> ];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> <span class="variable">f</span> <span class="keyword">in</span> functions.<span class="title function_ invoke__">iter</span>() {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result: {}"</span>, <span class="title function_ invoke__">f</span>(<span class="number">5</span>));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>以上只是一个非常基础的举例,事实上还有很多巧妙的用法,这里就不赘述了</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>使用函数作为参数可以使程序更灵活、强大,可以提高代码的复用性和灵活性和实现回调机制等等优点,但在使用中也需要结合特定的场景来选择。比如需要考虑环境变量时,可能使用闭包更好用,但需要实现较为复杂的函数功能时,可能函数指针更加的方便,具体需要根据我们在实际编程中的使用场景来确定</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:错误处理</title>
<link href="/posts/a7bp87o2.html"/>
<url>/posts/a7bp87o2.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h2><p>Rust中的错误处理不同于Python或者Java中常见的<code>try···except···</code>模式,Rust将错误分为两大类:<code>可恢复错误(recoverable)</code>与<code>不可恢复错误(unrecoverable)</code>,对于可恢复错误,在编写代码时可以使用Result枚举或者Option枚举来打包,可以更好地实现后续的错误处理逻辑</p><h2 id="Result枚举"><a href="#Result枚举" class="headerlink" title="Result枚举"></a>Result枚举</h2><p>在Rust中,Result枚举是处理可能产生错误操作的非常好用的工具,其在标准库中的定义可以看做</p><figure class="highlight rust"><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"><span class="keyword">enum</span> <span class="title class_">Result</span><T, E> {</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(T), <span class="comment">// Ok(T):表示操作成功,并且包含一个类型为T的值</span></span><br><span class="line"> <span class="title function_ invoke__">Err</span>(E), <span class="comment">// Err(E):表示操作失败,并且包含一个类型为E的错误值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在我们编写代码的时候,一般就可以这样来使用</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">file_to_read</span>() <span class="punctuation">-></span> <span class="type">Result</span><<span class="type">String</span>, io::Error> { <span class="comment">// 注意这里返回值的类型是Result枚举类型</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">f</span> = File::<span class="title function_ invoke__">open</span>(<span class="string">"hello.txt"</span>)?; <span class="comment">// 这里使用了'?'进行错误传递</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">s</span> = <span class="type">String</span>::<span class="title function_ invoke__">new</span>();</span><br><span class="line"> f.<span class="title function_ invoke__">read_to_string</span>(&<span class="keyword">mut</span> s)?;</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(s)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果结合之前文章里的<code>Trait对象</code>的话可以实现更加通用的效果</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">file_to_read</span>() <span class="punctuation">-></span> <span class="type">Result</span><<span class="type">String</span>, <span class="type">Box</span><<span class="keyword">dyn</span> std::error::Error>> { </span><br><span class="line"> <span class="comment">// 这里返回值使用Trait对象让函数返回实现'Error'Trait的类型</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">f</span> = File::<span class="title function_ invoke__">open</span>(<span class="string">"hello.txt"</span>)?; </span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">s</span> = <span class="type">String</span>::<span class="title function_ invoke__">new</span>();</span><br><span class="line"> f.<span class="title function_ invoke__">read_to_string</span>(&<span class="keyword">mut</span> s)?;</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(s)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="is-ok-、is-err-方法"><a href="#is-ok-、is-err-方法" class="headerlink" title="is_ok()、is_err()方法"></a>is_ok()、is_err()方法</h3><p><code>is_ok()</code>和<code>is_err()</code>方法主要是用来判断Result的类型值是否有效或存在错误,用法也很简单:<br>(1)<code>is_ok()</code>是在Result是Ok时返回true,Err则返回false<br>(2)<code>is_err()</code>是在Result是Ok时返回false,Ok则返回true</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">res</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">5</span>);</span><br><span class="line"> <span class="keyword">if</span> res.<span class="title function_ invoke__">is_ok</span>() {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Result is Ok"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="ok-、err-方法"><a href="#ok-、err-方法" class="headerlink" title="ok()、err()方法"></a>ok()、err()方法</h3><p><code>ok()</code>和<code>err()</code>方法主要是用来将Result枚举转换为Option枚举,它们的用法如下:<br>(1)<code>ok()</code>将<code>Result<T, E></code>转换为<code>Option<T></code>,如果是Err则返回None<br>(2)<code>err()</code>将<code>Result<T, E></code>转换为<code>Option<E></code>,如果是Ok则返回None</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">res</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">5</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">opt</span> = res.<span class="title function_ invoke__">ok</span>(); <span class="comment">// opt is Some(5)</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">res</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"hello"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">opt</span> = res.<span class="title function_ invoke__">err</span>(); <span class="comment">// opt is Some("hello")</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id=""><a href="#" class="headerlink" title=""></a></h3><h2 id="Option枚举"><a href="#Option枚举" class="headerlink" title="Option枚举"></a>Option枚举</h2><p>和Result枚举类似,Option枚举也是一个相当重要的工具,但它主要是为了解决<code>NULL</code>指针这种容易引起错误的操作,Option枚举在标准库的定义如下</p><figure class="highlight rust"><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"><span class="keyword">enum</span> <span class="title class_">Option</span><T> {</span><br><span class="line"> <span class="literal">None</span>,</span><br><span class="line"> <span class="title function_ invoke__">Some</span>(T),</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当使用Option枚举作为返回值时,函数调用者就必须处理返回值为<code>None</code>这种情况,从而实现让程序更稳定的目的</p><h3 id="is-some-、is-none-方法"><a href="#is-some-、is-none-方法" class="headerlink" title="is_some()、is_none()方法"></a>is_some()、is_none()方法</h3><p>和Result枚举类似,Option枚举的<code>is_some()</code>和<code>is_none()</code>也是用来判断值是否有效或为<code>None</code>,即:<br>(1)<code>is_some()</code>是在Option为Some时返回true,否则返回false<br>(2)<code>is_none()</code>是在Option为Some时返回false,否则返回true</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">opt</span> = <span class="title function_ invoke__">Some</span>(<span class="number">5</span>);</span><br><span class="line"> <span class="keyword">if</span> opt.<span class="title function_ invoke__">is_some</span>() {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Option has a value"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="直接处理"><a href="#直接处理" class="headerlink" title="直接处理"></a>直接处理</h2><p>除了以上介绍方法外,这两个枚举还有一些通用的方法</p><h3 id="unwrap-方法"><a href="#unwrap-方法" class="headerlink" title="unwrap()方法"></a>unwrap()方法</h3><p><code>unwrap()</code>方法在Rust中主要用来从Result或Option类型中提取值<br>(1)如果调用unwrap()时类型是<code>Ok或Some</code>,程序会直接进行提取<br>(2)如果调用unwrap()时类型是<code>Err或None</code>,程序则会panic并终止运行</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ok_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = ok_value.<span class="title function_ invoke__">unwrap</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">err_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"An error occurred"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = err_value.<span class="title function_ invoke__">unwrap</span>(); <span class="comment">// 这里会 panic</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">some_value</span> = <span class="title function_ invoke__">Some</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = some_value.<span class="title function_ invoke__">unwrap</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">none_value</span>: <span class="type">Option</span><<span class="type">i32</span>> = <span class="literal">None</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = none_value.<span class="title function_ invoke__">unwrap</span>(); <span class="comment">// 这里会 panic</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="unwrap-or-方法"><a href="#unwrap-or-方法" class="headerlink" title="unwrap_or()方法"></a>unwrap_or()方法</h3><p><code>unwrap_or()</code>方法在Rust中用于处理Option和Result类型,提供一个<code>默认值</code>以防返回值为None或Err</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ok_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = ok_value.<span class="title function_ invoke__">unwrap_or</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">err_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"An error occurred"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = err_value.<span class="title function_ invoke__">unwrap_or</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">some_value</span> = <span class="title function_ invoke__">Some</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = some_value.<span class="title function_ invoke__">unwrap_or</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">none_value</span>: <span class="type">Option</span><<span class="type">i32</span>> = <span class="literal">None</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = none_value.<span class="title function_ invoke__">unwrap_or</span>(<span class="number">0</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="unwrap-or-else-方法"><a href="#unwrap-or-else-方法" class="headerlink" title="unwrap_or_else()方法"></a>unwrap_or_else()方法</h3><p><code>unwrap_or_else()</code>方法类似于unwrap_or(),但其接受一个闭包作为参数,当需要默认值时才会调用该闭包,对于计算开销较大的默认值、需要执行一些逻辑才能获得默认值或者需要对err进行处理时的情况非常有用,比如</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ok_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = ok_value.<span class="title function_ invoke__">unwrap_or_else</span>(|err| {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Encountered an error: {}"</span>, err);</span><br><span class="line"> <span class="number">0</span></span><br><span class="line"> });</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">err_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"An error occurred"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = err_value.<span class="title function_ invoke__">unwrap_or_else</span>(|err| {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Encountered an error: {}"</span>, err);</span><br><span class="line"> <span class="number">0</span></span><br><span class="line"> });</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">some_value</span> = <span class="title function_ invoke__">Some</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = some_value.<span class="title function_ invoke__">unwrap_or_else</span>(|| <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">none_value</span>: <span class="type">Option</span><<span class="type">i32</span>> = <span class="literal">None</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = none_value.<span class="title function_ invoke__">unwrap_or_else</span>(|| <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>而且,因为<code>unwrap_or_else()</code>是接受闭包作为参数的,这样就可以很方便的捕获环境值,具体怎么使用闭包可以参考之前的文章</p><h3 id="unwrap-or-default-方法"><a href="#unwrap-or-default-方法" class="headerlink" title="unwrap_or_default()方法"></a>unwrap_or_default()方法</h3><p><code>unwrap_or_default()</code>方法在Rust中主要用来处理Option和Result类型的值为None或Err时,返回类型的默认值,默认值则由<code>Default</code>trait提供</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">some_value</span> = <span class="title function_ invoke__">Some</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = some_value.<span class="title function_ invoke__">unwrap_or_default</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">none_value</span>: <span class="type">Option</span><<span class="type">i32</span>> = <span class="literal">None</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = none_value.<span class="title function_ invoke__">unwrap_or_default</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ok_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = ok_value.<span class="title function_ invoke__">unwrap_or_default</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">err_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"An error occurred"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = err_value.<span class="title function_ invoke__">unwrap_or_default</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 0</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们也可以为自定义的类型实现<code>Default</code>trait,使其能调用<code>unwrap_or_default()</code>方法,比如下面这样</p><figure class="highlight rust"><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"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyStruct</span> {</span><br><span class="line"> value: <span class="type">i32</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Default</span> <span class="keyword">for</span> <span class="title class_">MyStruct</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">default</span>() <span class="punctuation">-></span> <span class="keyword">Self</span> {</span><br><span class="line"> MyStruct { value: <span class="number">42</span> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">some_value</span>: <span class="type">Option</span><MyStruct> = <span class="title function_ invoke__">Some</span>(MyStruct { value: <span class="number">10</span> });</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = some_value.<span class="title function_ invoke__">unwrap_or_default</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {:?}"</span>, value); <span class="comment">// 输出: The value is: MyStruct { value: 10 }</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">none_value</span>: <span class="type">Option</span><MyStruct> = <span class="literal">None</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = none_value.<span class="title function_ invoke__">unwrap_or_default</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {:?}"</span>, value); <span class="comment">// 输出: The value is: MyStruct { value: 42 }</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="expect-方法"><a href="#expect-方法" class="headerlink" title="expect()方法"></a>expect()方法</h3><p><code>expect()</code>方法和unwrap()方法类似,都是用于从Result或Option类型中提取值,但相比于unwrap(),expect()允许你提供更具描述性的错误信息,以便更容易调试和定位问题</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ok_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = ok_value.<span class="title function_ invoke__">expect</span>(<span class="string">"Expected Ok, but got Err"</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">err_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"An error occurred"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = err_value.<span class="title function_ invoke__">expect</span>(<span class="string">"Expected Ok, but got Err"</span>); <span class="comment">// 这里会 panic,并显示 "Expected Ok, but got Err: An error occurred"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">some_value</span> = <span class="title function_ invoke__">Some</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = some_value.<span class="title function_ invoke__">expect</span>(<span class="string">"Expected a value, but got None"</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The value is: {}"</span>, value); <span class="comment">// 输出: The value is: 10</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">none_value</span>: <span class="type">Option</span><<span class="type">i32</span>> = <span class="literal">None</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">value</span> = none_value.<span class="title function_ invoke__">expect</span>(<span class="string">"Expected a value, but got None"</span>); <span class="comment">// 这里会 panic,并显示 "Expected a value, but got None"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="map-方法"><a href="#map-方法" class="headerlink" title="map()方法"></a>map()方法</h3><p><code>map()</code>方法用于对Option或Result类型中的值应用一个函数,并返回一个新的Option或Result,通常用于在不改变原始类型的情况下处理值</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">some_value</span> = <span class="title function_ invoke__">Some</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = some_value.<span class="title function_ invoke__">map</span>(|x| x * <span class="number">2</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: Some(20)</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">none_value</span>: <span class="type">Option</span><<span class="type">i32</span>> = <span class="literal">None</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = none_value.<span class="title function_ invoke__">map</span>(|x| x * <span class="number">2</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: None</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ok_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = ok_value.<span class="title function_ invoke__">map</span>(|x| x * <span class="number">2</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: Ok(20)</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">err_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"An error occurred"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = err_value.<span class="title function_ invoke__">map</span>(|x| x * <span class="number">2</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: Err("An error occurred")</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>对于Result类型,还提供了<code>map_err()</code>方法以适用发生错误的场景</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ok_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = ok_value.<span class="title function_ invoke__">map_err</span>(|e| <span class="built_in">format!</span>(<span class="string">"Error: {}"</span>, e));</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: Ok(10)</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">err_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"An error occurred"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = err_value.<span class="title function_ invoke__">map_err</span>(|e| <span class="built_in">format!</span>(<span class="string">"Error: {}"</span>, e));</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: Err("Error: An error occurred")</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="and-then-方法"><a href="#and-then-方法" class="headerlink" title="and_then()方法"></a>and_then()方法</h3><p><code>and_then()</code>方法在Rust中用于在Option或Result类型中链式处理值。其作用与map()方法类似,但不同之处在于and_then()期望闭包返回一个新的Option或Result,从而使得链式处理更灵活</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">some_value</span> = <span class="title function_ invoke__">Some</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = some_value.<span class="title function_ invoke__">and_then</span>(|x| <span class="title function_ invoke__">Some</span>(x * <span class="number">2</span>));</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: Some(20)</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">none_value</span>: <span class="type">Option</span><<span class="type">i32</span>> = <span class="literal">None</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = none_value.<span class="title function_ invoke__">and_then</span>(|x| <span class="title function_ invoke__">Some</span>(x * <span class="number">2</span>));</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: None</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ok_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Ok</span>(<span class="number">10</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = ok_value.<span class="title function_ invoke__">and_then</span>(|x| <span class="title function_ invoke__">Ok</span>(x * <span class="number">2</span>));</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: Ok(20)</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">err_value</span>: <span class="type">Result</span><<span class="type">i32</span>, &<span class="type">str</span>> = <span class="title function_ invoke__">Err</span>(<span class="string">"An error occurred"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_value</span> = err_value.<span class="title function_ invoke__">and_then</span>(|x| <span class="title function_ invoke__">Ok</span>(x * <span class="number">2</span>));</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The new value is: {:?}"</span>, new_value); <span class="comment">// 输出: The new value is: Err("An error occurred")</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><div class="note info modern"><p>其实在生产环境中,应该避免直接使用unwrap()或expect(),因为其会在发生错误时导致程序崩溃,除非错误是可预见的或期望发生的,否则应该更多使用类似unwrap_or()、unwrap_or_else()这类方法</p></div><h2 id="模式匹配"><a href="#模式匹配" class="headerlink" title="模式匹配"></a>模式匹配</h2><p>如上所述,我们在生产环境中应当避免直接使用一些会导致程序奔溃的方法,而当同时需要针对错误的类型实现更加复杂的处理逻辑时,上面的直接处理方法可能就存在一定局限,这时候我们也可以使用<code>模式匹配</code>来细分各种错误的类型进行分别处理,比如下面这样</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> std::fs::File;</span><br><span class="line"><span class="keyword">use</span> std::io::{<span class="keyword">self</span>, Read};</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">read_file_content</span>(file_path: &<span class="type">str</span>) <span class="punctuation">-></span> <span class="type">Result</span><<span class="type">String</span>, io::Error> {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">file</span> = File::<span class="title function_ invoke__">open</span>(file_path)?;</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">content</span> = <span class="type">String</span>::<span class="title function_ invoke__">new</span>();</span><br><span class="line"> file.<span class="title function_ invoke__">read_to_string</span>(&<span class="keyword">mut</span> content)?;</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(content)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 使用模式匹配对不同情况进行处理</span></span><br><span class="line"> <span class="keyword">match</span> <span class="title function_ invoke__">read_file_content</span>(<span class="string">"example.txt"</span>) {</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(content) => <span class="built_in">println!</span>(<span class="string">"File content: {}"</span>, content),</span><br><span class="line"> <span class="title function_ invoke__">Err</span>(e) => <span class="built_in">println!</span>(<span class="string">"Failed to read file: {}"</span>, e),</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="错误传递"><a href="#错误传递" class="headerlink" title="错误传递"></a>错误传递</h2><p>在本文最开始的示例代码中,我们使用到了<code>?</code>操作符进行函数间错误的传递,在Rust中,<code>?</code>操作符主要用于简化错误处理和传递,即当一个函数返回Result或Option类型时,可以使用<code>?</code>操作符来自动处理错误或无值的情况,并将它们传递给函数调用者来进行后续处理</p><h3 id="‘-’操作符的工作原理"><a href="#‘-’操作符的工作原理" class="headerlink" title="‘?’操作符的工作原理"></a>‘?’操作符的工作原理</h3><p>(1)对于Result类型:如果表达式返回Ok,则<code>?</code>操作符将其解包并返回其中的值;如果返回Err,则<code>?</code>操作符会将Err提前返回,结束当前函数的执行<br>(2)对于Option类型:如果表达式返回Some,则<code>?</code>操作符将其解包并返回其中的值;如果返回None,则<code>?</code>操作符会将None提前返回,结束当前函数的执行</p><figure class="highlight rust"><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"><span class="keyword">use</span> std::fs::File;</span><br><span class="line"><span class="keyword">use</span> std::io::{<span class="keyword">self</span>, Read};</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">read_file_content</span>(file_path: &<span class="type">str</span>) <span class="punctuation">-></span> <span class="type">Result</span><<span class="type">String</span>, io::Error> {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">file</span> = File::<span class="title function_ invoke__">open</span>(file_path)?; <span class="comment">// 使用'?'进行错误处理和传递</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">content</span> = <span class="type">String</span>::<span class="title function_ invoke__">new</span>();</span><br><span class="line"> file.<span class="title function_ invoke__">read_to_string</span>(&<span class="keyword">mut</span> content)?;</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(content)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() <span class="punctuation">-></span> <span class="type">Result</span><(), io::Error> {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">content</span> = <span class="title function_ invoke__">read_file_content</span>(<span class="string">"example.txt"</span>)?;</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"File content: {}"</span>, content);</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在以上示例中可以看出,<code>?</code>操作符大大简化了错误处理</p><h3 id="‘-’操作符的注意事项"><a href="#‘-’操作符的注意事项" class="headerlink" title="‘?’操作符的注意事项"></a>‘?’操作符的注意事项</h3><h4 id="返回类型必须是Result或Option"><a href="#返回类型必须是Result或Option" class="headerlink" title="返回类型必须是Result或Option"></a>返回类型必须是Result或Option</h4><p>使用<code>?</code>操作符的函数必须返回Result或Option类型,如果尝试在返回类型不是Result或Option的函数中使用,编译器就会报错,比如下面的例子</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">invalid_use_of_question_mark</span>(file_path: &<span class="type">str</span>) <span class="punctuation">-></span> <span class="type">String</span> {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">file</span> = std::fs::File::<span class="title function_ invoke__">open</span>(file_path)?; <span class="comment">// 此处会发生编译错误,因为函数返回类型为String</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">content</span> = <span class="type">String</span>::<span class="title function_ invoke__">new</span>();</span><br><span class="line"> file.<span class="title function_ invoke__">read_to_string</span>(&<span class="keyword">mut</span> content)?;</span><br><span class="line"> content</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="错误类型的转换"><a href="#错误类型的转换" class="headerlink" title="错误类型的转换"></a>错误类型的转换</h4><p>使用<code>?</code>操作符时,<mark class="hl-label red">函数的错误类型必须与调用链中的错误类型一致</mark> </p><figure class="highlight rust"><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"><span class="keyword">use</span> std::io;</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">parse_number</span>(s: &<span class="type">str</span>) <span class="punctuation">-></span> <span class="type">Result</span><<span class="type">i32</span>, std::num::ParseIntError> {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">num</span>: <span class="type">i32</span> = s.<span class="title function_ invoke__">trim</span>().<span class="title function_ invoke__">parse</span>()?;</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(num)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() <span class="punctuation">-></span> <span class="type">Result</span><(), io::Error> {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s</span> = <span class="string">"195"</span>;</span><br><span class="line"> <span class="comment">// 下面的代码会发生错误,因为parse_number函数传递的是std::num::ParseIntError错误</span></span><br><span class="line"> <span class="comment">// 而main函数可能返回的是io::Error错误</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">number</span> = <span class="title function_ invoke__">parse_number</span>(&s)?;</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Parsed number: {}"</span>, number);</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果不一致,但是又确有这样的需求时,就需要通过<code>From</code>trait或显式的错误转换来解决</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> std::fs::File;</span><br><span class="line"><span class="keyword">use</span> std::io::{<span class="keyword">self</span>, Read};</span><br><span class="line"></span><br><span class="line"><span class="meta">#[allow(dead_code)]</span></span><br><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">MyError</span> {</span><br><span class="line"> <span class="title function_ invoke__">Io</span>(io::Error),</span><br><span class="line"> <span class="title function_ invoke__">Parse</span>(std::num::ParseIntError),</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">From</span><io::Error> <span class="keyword">for</span> <span class="title class_">MyError</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">from</span>(err: io::Error) <span class="punctuation">-></span> MyError {</span><br><span class="line"> MyError::<span class="title function_ invoke__">Io</span>(err)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">From</span><std::num::ParseIntError> <span class="keyword">for</span> <span class="title class_">MyError</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">from</span>(err: std::num::ParseIntError) <span class="punctuation">-></span> MyError {</span><br><span class="line"> MyError::<span class="title function_ invoke__">Parse</span>(err)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">read_file_content</span>(file_path: &<span class="type">str</span>) <span class="punctuation">-></span> <span class="type">Result</span><<span class="type">String</span>, MyError> {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">file</span> = File::<span class="title function_ invoke__">open</span>(file_path)?;</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">content</span> = <span class="type">String</span>::<span class="title function_ invoke__">new</span>();</span><br><span class="line"> file.<span class="title function_ invoke__">read_to_string</span>(&<span class="keyword">mut</span> content)?;</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(content)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">parse_number</span>(s: &<span class="type">str</span>) <span class="punctuation">-></span> <span class="type">Result</span><<span class="type">i32</span>, MyError> {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">num</span>: <span class="type">i32</span> = s.<span class="title function_ invoke__">trim</span>().<span class="title function_ invoke__">parse</span>()?;</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(num)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() <span class="punctuation">-></span> <span class="type">Result</span><(), MyError> {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">content</span> = <span class="title function_ invoke__">read_file_content</span>(<span class="string">"example.txt"</span>)?;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">number</span> = <span class="title function_ invoke__">parse_number</span>(&content)?;</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Parsed number: {}"</span>, number);</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="不要滥用’-’"><a href="#不要滥用’-’" class="headerlink" title="不要滥用’?’"></a>不要滥用’?’</h4><p>虽然<code>?</code>操作符非常方便,但也不应滥用,在某些情况下,明确处理错误可能更合适</p><h4 id="确保错误传递的合理性"><a href="#确保错误传递的合理性" class="headerlink" title="确保错误传递的合理性"></a>确保错误传递的合理性</h4><p>当使用<code>?</code>操作符传递错误时,应确保错误处理的设计合理,某些错误可能需要立即处理,而不是传递</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>使用Podman构建Rust项目</title>
<link href="/posts/l4j4c8hb.html"/>
<url>/posts/l4j4c8hb.html</url>
<content type="html"><![CDATA[<h2 id="编写Rust程序"><a href="#编写Rust程序" class="headerlink" title="编写Rust程序"></a>编写Rust程序</h2><figure class="highlight bash"><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"><span class="comment"># 创建Cargo项目</span></span><br><span class="line">cargo new hello</span><br><span class="line"><span class="comment"># 编辑main.rs文件</span></span><br><span class="line">vi /hello/src/main.rs</span><br></pre></td></tr></table></figure><h2 id="拉取所需Rust版本的镜像"><a href="#拉取所需Rust版本的镜像" class="headerlink" title="拉取所需Rust版本的镜像"></a>拉取所需Rust版本的镜像</h2><p>DockerHub项目地址:<a href="https://hub.docker.com/_/rust/tags">https://hub.docker.com/_/rust/tags</a></p><figure class="highlight bash"><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"><span class="comment"># 拉取所需版本的Rust镜像</span></span><br><span class="line">podman pull rust:1.74.1</span><br></pre></td></tr></table></figure><h2 id="使用容器构建Rust程序"><a href="#使用容器构建Rust程序" class="headerlink" title="使用容器构建Rust程序"></a>使用容器构建Rust程序</h2><figure class="highlight bash"><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"><span class="comment"># 进入Rust项目目录</span></span><br><span class="line"><span class="built_in">cd</span> hello/</span><br><span class="line"><span class="comment"># 进入Podman容器</span></span><br><span class="line">podman run -it --privileged=<span class="literal">true</span> --<span class="built_in">rm</span> -v .:/app rust:1.74.1 /bin/bash</span><br><span class="line"><span class="comment"># 构建项目</span></span><br><span class="line"><span class="built_in">cd</span> app/</span><br><span class="line">cargo build</span><br><span class="line"><span class="comment"># 退出容器</span></span><br><span class="line"><span class="built_in">exit</span></span><br><span class="line"><span class="comment"># 在宿主机运行二进制程序</span></span><br><span class="line">./target/debug/hello <span class="comment"># 此处仅为示例,具体文件名应根据项目进行填写</span></span><br></pre></td></tr></table></figure><div class="note warning modern"><p>在使用容器构建Rust项目时应注意容器中的libc和其他运行库的版本和宿主机环境是否兼容,否则编译出的二进制文件可能无法正常运行</p></div>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 环境配置 </tag>
<tag> 备忘 </tag>
<tag> Rust </tag>
<tag> Podman </tag>
</tags>
</entry>
<entry>
<title>使用Rust为Python编写模块</title>
<link href="/posts/zn4ra72x.html"/>
<url>/posts/zn4ra72x.html</url>
<content type="html"><![CDATA[<div class="note icon-padding simple"><i class="note-icon fa-brands fa-python"></i><p>我在之前的博客中分享了<code>使用Python调用C语言动态链接库</code>的相关内容,在开发中我们也可以很方便地使用maturin配合<a href="https://github.com/PyO3/pyo3">pyo3</a>为Python开发可调用的Rust模块来加速Python程序,这篇博客就来分享一下入门的用法</p></div><h2 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h2><h3 id="maturin安装"><a href="#maturin安装" class="headerlink" title="maturin安装"></a>maturin安装</h3><p>关于Rust环境的配置这里就不多介绍了,具体可以参考之前的博客,这里介绍一下maturin的安装,具体命令如下</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install maturin</span><br></pre></td></tr></table></figure><h3 id="环境初始化"><a href="#环境初始化" class="headerlink" title="环境初始化"></a>环境初始化</h3><p>maturin安装完成后,可以使用以下的命令进行Rust环境的初始化</p><figure class="highlight bash"><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"><span class="comment"># 初始化编译环境命令</span></span><br><span class="line">maturin new <span class="built_in">sum</span></span><br></pre></td></tr></table></figure><p>运行完成后可以看见当前目录下新增了<code>sum</code>目录,这就是我们的项目目录</p><h2 id="实例:编写Rust下的累加函数"><a href="#实例:编写Rust下的累加函数" class="headerlink" title="实例:编写Rust下的累加函数"></a>实例:编写Rust下的累加函数</h2><h3 id="编辑lib-rs文件"><a href="#编辑lib-rs文件" class="headerlink" title="编辑lib.rs文件"></a>编辑lib.rs文件</h3><figure class="highlight bash"><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"><span class="built_in">cd</span> <span class="built_in">sum</span></span><br><span class="line">vi src/lib.rs</span><br></pre></td></tr></table></figure><h3 id="Rust代码"><a href="#Rust代码" class="headerlink" title="Rust代码"></a>Rust代码</h3><figure class="highlight rust"><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"><span class="keyword">use</span> pyo3::prelude::*;</span><br><span class="line"></span><br><span class="line"><span class="meta">#[pyfunction]</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">add_up_u128</span>(x: <span class="type">u128</span>) <span class="punctuation">-></span> PyResult<<span class="type">u128</span>> {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">sum</span> = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">i</span> = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> i <= x {</span><br><span class="line"> sum += i;</span><br><span class="line"> i += <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(sum)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#[pymodule]</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">sum</span>(_py: Python, m: &PyModule) <span class="punctuation">-></span> PyResult<()> {</span><br><span class="line"> m.<span class="title function_ invoke__">add_function</span>(wrap_pyfunction!(add_up_u128, m)?)?;</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(())</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="编译、安装模块"><a href="#编译、安装模块" class="headerlink" title="编译、安装模块"></a>编译、安装模块</h3><p>使用maturin时有两种方式安装Python模块</p><h4 id="直接编译、安装"><a href="#直接编译、安装" class="headerlink" title="直接编译、安装"></a>直接编译、安装</h4><p>模块代码编写完成之后,直接在项目目录下运行</p><figure class="highlight bash"><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"><span class="comment"># 编译项目</span></span><br><span class="line">maturin build --release</span><br><span class="line"><span class="comment"># 安装模块(注意:此处需根据具体whl文件名称确定安装文件)</span></span><br><span class="line">pip install target/wheels/sum-0.1.0-cp39-cp39-manylinux_2_34_x86_64.whl</span><br></pre></td></tr></table></figure><p>注意:修改Rust模块代码导致需要重新安装模块时,可能需要添加<code>--force-reinstall</code>参数</p><h4 id="使用Python虚拟环境安装"><a href="#使用Python虚拟环境安装" class="headerlink" title="使用Python虚拟环境安装"></a>使用Python虚拟环境安装</h4><figure class="highlight bash"><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"><span class="comment"># 创建Python虚拟环境</span></span><br><span class="line">python -m venv virtualenv</span><br><span class="line"><span class="comment"># 运行虚拟环境</span></span><br><span class="line"><span class="built_in">source</span> virtualenv/bin/activate</span><br><span class="line"><span class="comment"># 安装模块(项目目录运行)</span></span><br><span class="line">maturin develop --release</span><br></pre></td></tr></table></figure><p>注意:使用这种方法安装模块,之后想调用该模块时都需要先运行已安装模块的虚拟环境</p><h3 id="Python代码"><a href="#Python代码" class="headerlink" title="Python代码"></a>Python代码</h3><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> <span class="built_in">sum</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">num_add_up</span>(<span class="params">x: <span class="built_in">int</span></span>) -> <span class="literal">None</span>:</span><br><span class="line"> start = time.time()</span><br><span class="line"> result: <span class="built_in">int</span> = <span class="built_in">sum</span>.add_up_u128(x)</span><br><span class="line"> <span class="built_in">print</span>(result)</span><br><span class="line"> <span class="built_in">print</span>(time.time()-start)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> num_add_up(<span class="number">100000000</span>)</span><br><span class="line"></span><br><span class="line">输出:</span><br><span class="line"><span class="number">5000000050000000</span></span><br><span class="line"><span class="number">0.08351874351501465</span></span><br></pre></td></tr></table></figure><h2 id="数据类型映射关系"><a href="#数据类型映射关系" class="headerlink" title="数据类型映射关系"></a>数据类型映射关系</h2><p>和ctypes类似,使用Rust模块时也需要进行数据类型的映射,以下是一些Rust和Python的基础数据类型对应关系</p><table><thead><tr><th align="center">Rust 类型</th><th align="center">Python 类型</th></tr></thead><tbody><tr><td align="center">i32</td><td align="center">int</td></tr><tr><td align="center">i64</td><td align="center">int</td></tr><tr><td align="center">i128</td><td align="center">int</td></tr><tr><td align="center">f32</td><td align="center">float</td></tr><tr><td align="center">f64</td><td align="center">float</td></tr><tr><td align="center">bool</td><td align="center">bool</td></tr><tr><td align="center">string</td><td align="center">str</td></tr><tr><td align="center">Vec<T></td><td align="center">list</td></tr><tr><td align="center">[T; N]</td><td align="center">list</td></tr><tr><td align="center">(T1, T2, …)</td><td align="center">tuple</td></tr><tr><td align="center">HashMap<K, V></td><td align="center">dict</td></tr></tbody></table><p>除以上数据类型外,还有其他如<code>struct类型</code>、<code>Result类型</code>、<code>Option类型</code>等,这些就不一一列出了</p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>其实从上面的运行结果可以看出,使用Rust编写的模块在速度上较C语言稍慢,但Rust在内存安全和数据类型支持上比C语言更好,比如读者可以将累加的数据设置为<code>10000000000</code>再运行一下查看结果。同时,也可以看出Rust模块在编写和调用时也更加简单</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Python </tag>
<tag> Rust </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:迭代器</title>
<link href="/posts/xpyovm89.html"/>
<url>/posts/xpyovm89.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>和Python类似,Rust中的迭代器也是惰性的,即在调用消费迭代器的方法之前,迭代器本身没有任何效果,迭代器不会一次性计算和生成所有元素,而是在需要时逐个生成元素</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">v</span> = <span class="built_in">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">v_iter</span> = v.<span class="title function_ invoke__">iter</span>();</span><br><span class="line"> <span class="keyword">for</span> <span class="variable">ele</span> <span class="keyword">in</span> v_iter { <span class="comment">// 注意:这里的变量"ele"的类型是&i32</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, ele);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Iterator-trait"><a href="#Iterator-trait" class="headerlink" title="Iterator trait"></a>Iterator trait</h2><p>在Rust中,所有迭代器都需要实现<code>Iterator trait</code>,且因为<code>Iterator trait</code>中所包含的一些默认方法会调用到<code>next()</code>方法,故在Rust中要求实现这个trait时必须实现<code>next()</code>方法,<code>Iterator trait</code>的定义大致如下所示</p><figure class="highlight rust"><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"><span class="keyword">pub</span> <span class="keyword">trait</span> <span class="title class_">Iterator</span> {</span><br><span class="line"> <span class="comment">// 关联类型</span></span><br><span class="line"> <span class="keyword">type</span> <span class="title class_">Item</span>;</span><br><span class="line"> <span class="comment">// Iterator trait仅要求实现next()方法</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">next</span>(&<span class="keyword">mut</span> <span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">Option</span><<span class="keyword">Self</span>::item>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>next()</code>方法的主要作用是<mark class="hl-label green">当每次调用迭代器的next()方法时,将迭代器中的一项包装在Some()里并返回,当迭代结束时返回None</mark> </p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">v</span> = <span class="built_in">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">v_iter</span> = v.<span class="title function_ invoke__">iter</span>(); <span class="comment">// 注意: v_iter是可变的</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">_a</span> = v_iter.<span class="title function_ invoke__">next</span>(); <span class="comment">// 变量"_a"的类型为Option<&i32></span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, v_iter.<span class="title function_ invoke__">next</span>());</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, v_iter.<span class="title function_ invoke__">next</span>());</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, v_iter.<span class="title function_ invoke__">next</span>()); <span class="comment">// 最后打印的结果是None</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>需要注意的是,变量”v_iter”是mutable的,因为在用户在调用迭代器的next()方法时相当于改变迭代器中的记录位置序列的状态,可以理解成每一次调用next()方法时都消耗了迭代器中一个元素</p><h2 id="迭代器中元素的所有权"><a href="#迭代器中元素的所有权" class="headerlink" title="迭代器中元素的所有权"></a>迭代器中元素的所有权</h2><h3 id="iter-方法"><a href="#iter-方法" class="headerlink" title="iter()方法"></a>iter()方法</h3><p>在使用iter()方法时会返回一个<code>不可变引用</code>的迭代器,只允许对每个元素进行只读的操作,但不允许修改原始数据,通常可以用在可迭代的不可变引用类型,例如<code>&[T]</code>这样的类型</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">numbers</span> = <span class="built_in">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 变量number的类型是&i32,是迭代器内元素的不可变引用</span></span><br><span class="line"> <span class="keyword">for</span> <span class="variable">number</span> <span class="keyword">in</span> numbers.<span class="title function_ invoke__">iter</span>() {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Read Only: {}"</span>, number);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, numbers);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注意:<mark class="hl-label blue">通过iter方法获得的是迭代器中元素的不可变引用而不是迭代器的不可变引用</mark> </p><h3 id="into-iter-方法"><a href="#into-iter-方法" class="headerlink" title="into_iter()方法"></a>into_iter()方法</h3><p>在使用into_iter()方法时返回一个<code>拥有所有权</code>的迭代器,允许对每个元素进行拥有所有权的操作,即可以进行移动语义的操作,迭代后原始数据无法再使用,通常用于可迭代的拥有所有权的类型,例如<code>Vec<T></code></p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">numbers</span> = <span class="built_in">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 变量number的类型是i32,其拥有迭代器内元素的所有权</span></span><br><span class="line"> <span class="keyword">for</span> <span class="variable">number</span> <span class="keyword">in</span> numbers.<span class="title function_ invoke__">into_iter</span>() {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Owned: {}"</span>, number);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 此处会出现错误,因为numbers的所有权已被转移</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, numbers);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>和iter()一样,<mark class="hl-label blue">into_iter()方法获得的也是迭代器内部元素的所有权而不是迭代器的所有权</mark> </p><h3 id="iter-mut-方法"><a href="#iter-mut-方法" class="headerlink" title="iter_mut()方法"></a>iter_mut()方法</h3><p>在使用iter_mut()方法时返回一个<code>可变引用</code>的迭代器,允许对每个元素进行读写操作,可以修改原始数据,适用于可迭代的可变引用类型,例如<code>&mut [T]</code></p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">numbers</span> = <span class="built_in">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>]; <span class="comment">// 变量numbers是可变的Vector</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 变量number的类型是&mut i32,是迭代器内元素的可变引用</span></span><br><span class="line"> <span class="keyword">for</span> <span class="variable">number</span> <span class="keyword">in</span> numbers.<span class="title function_ invoke__">iter_mut</span>() {</span><br><span class="line"> *number = *number + <span class="number">1</span>; <span class="comment">// 修改原始数据</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, numbers);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="产生迭代器方法-迭代器适配器"><a href="#产生迭代器方法-迭代器适配器" class="headerlink" title="产生迭代器方法(迭代器适配器)"></a>产生迭代器方法(迭代器适配器)</h2><h3 id="enumerate-方法"><a href="#enumerate-方法" class="headerlink" title="enumerate()方法"></a>enumerate()方法</h3><p><code>enumerate()</code>方法用于同时迭代集合中的元素和它们的索引,其返回一个产生元素和索引元组的迭代器</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">fruits</span> = <span class="built_in">vec!</span>[<span class="string">"apple"</span>, <span class="string">"banana"</span>, <span class="string">"cherry"</span>];</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">for</span> (index, fruit) <span class="keyword">in</span> fruits.<span class="title function_ invoke__">iter</span>().<span class="title function_ invoke__">enumerate</span>() {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Index: {}, Fruit: {}"</span>, index, fruit);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="map-方法"><a href="#map-方法" class="headerlink" title="map()方法"></a>map()方法</h3><p><code>map()</code>方法用于将一个迭代器中的每个元素映射为另一个元素,然后返回一个产生映射结果的新迭代器,可以对集合中的每个元素应用某个函数,并在新迭代器中获取映射后的值</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">numbers</span> = <span class="built_in">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用map()将每个数字平方</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">squared_numbers</span>: <span class="type">Vec</span><<span class="type">i32</span>> = numbers.<span class="title function_ invoke__">iter</span>().<span class="title function_ invoke__">map</span>(|&x| x * x).<span class="title function_ invoke__">collect</span>();</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Original Numbers: {:?}"</span>, numbers);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Squared Numbers: {:?}"</span>, squared_numbers);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="zip-方法"><a href="#zip-方法" class="headerlink" title="zip()方法"></a>zip()方法</h3><p><code>zip()</code>方法用于将两个迭代器逐对地组合,然后返回一个新的、产生元组的迭代器,其中第一个元素来自第一个迭代器,第二个元素来自第二个迭代器。可以理解成zip()将两个迭代器压缩在了一起,<mark class="hl-label orange">最后形成的元组的数量取决于两个迭代器中数量少的那个</mark> </p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">names</span> = <span class="built_in">vec!</span>[<span class="string">"Alice"</span>, <span class="string">"Bob"</span>, <span class="string">"Charlie"</span>, <span class="string">"June"</span>]; <span class="comment">// 两个Vector内元素数量不一致</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">ages</span> = <span class="built_in">vec!</span>[<span class="number">25</span>, <span class="number">30</span>, <span class="number">22</span>];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用zip()将姓名和年龄逐对组合</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">combined</span>: <span class="type">Vec</span><(&&<span class="type">str</span>, &<span class="type">i32</span>)> = names.<span class="title function_ invoke__">iter</span>().<span class="title function_ invoke__">zip</span>(ages.<span class="title function_ invoke__">iter</span>()).<span class="title function_ invoke__">collect</span>();</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">for</span> (name, age) <span class="keyword">in</span> combined {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Name: {}, Age: {}"</span>, name, age);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="fliter-方法"><a href="#fliter-方法" class="headerlink" title="fliter()方法"></a>fliter()方法</h3><p><code>filter()</code>方法用于根据特定条件过滤出符合条件的元素生成一个新的迭代器</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">numbers</span> = <span class="built_in">vec!</span>[<span class="number">3</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">6</span>, <span class="number">4</span>, <span class="number">9</span>, <span class="number">2</span>, <span class="number">7</span>, <span class="number">5</span>];</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">limit</span> = <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用filter()方法筛选出大于等于6的元素</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">filtered_numbers</span>: <span class="type">Vec</span><<span class="type">i32</span>> = numbers.<span class="title function_ invoke__">iter</span>().<span class="title function_ invoke__">cloned</span>().<span class="title function_ invoke__">filter</span>(|&x| x >= limit).<span class="title function_ invoke__">collect</span>(); <span class="comment">// 这里使用闭包捕获了环境</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Original Numbers: {:?}"</span>, numbers);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Filtered Numbers: {:?}"</span>, filtered_numbers);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>除以上方法外,Rust中还有<code>flat_map()、cycle()、take()、skip()、chain()、rev()</code>等迭代器适配器,这里就不一一举例了</p><h2 id="消耗迭代器方法-消耗型适配器"><a href="#消耗迭代器方法-消耗型适配器" class="headerlink" title="消耗迭代器方法(消耗型适配器)"></a>消耗迭代器方法(消耗型适配器)</h2><h3 id="collect-方法"><a href="#collect-方法" class="headerlink" title="collect()方法"></a>collect()方法</h3><p><code>collect()</code>方法用于从一个迭代器中收集元素并创建一个集合,如数组、向量、哈希映射等,比如下面这个创建HashMap的例子</p><figure class="highlight rust"><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"><span class="keyword">use</span> std::collections::HashMap;</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">person_data</span> = <span class="built_in">vec!</span>[(<span class="string">"Alice"</span>, <span class="number">25</span>), (<span class="string">"Bob"</span>, <span class="number">30</span>), (<span class="string">"Charlie"</span>, <span class="number">22</span>)];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用collect()方法将键值对收集到一个新的HashMap中</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">person_map</span>: HashMap<_, _> = person_data.<span class="title function_ invoke__">into_iter</span>().<span class="title function_ invoke__">collect</span>();</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, person_map);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>也可以配合使用zip()方法一起使用</p><figure class="highlight rust"><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"><span class="keyword">use</span> std::collections::HashMap;</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">keys</span> = <span class="built_in">vec!</span>[<span class="string">"Alice"</span>, <span class="string">"Bob"</span>, <span class="string">"Charlie"</span>];</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">values</span> = <span class="built_in">vec!</span>[<span class="number">25</span>, <span class="number">30</span>, <span class="number">22</span>, <span class="number">27</span>]; <span class="comment">// 此处两个Vector内元素数量不一致</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">map</span>: HashMap<_, _> = keys.<span class="title function_ invoke__">iter</span>().<span class="title function_ invoke__">zip</span>(values.<span class="title function_ invoke__">iter</span>()).<span class="title function_ invoke__">collect</span>();</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, map);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><div class="note warning modern"><p>一般情况下,在使用collect()方法时需要明确指定要收集的元素类型以及要返回的集合类型,如果不明确指定类型,编译器可能无法推断出正确的类型,导致编译错误</p></div><h3 id="sum-方法"><a href="#sum-方法" class="headerlink" title="sum()方法"></a>sum()方法</h3><p><code>sum()</code>方法会取得迭代器的所有权,其主要的原理是反复调用迭代器的next()方法,将当前元素累加到总和,最后耗尽迭代器,并返回总和</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">numbers</span> = <span class="built_in">vec!</span>[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">total</span>: <span class="type">i32</span> = numbers.<span class="title function_ invoke__">iter</span>().<span class="title function_ invoke__">sum</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"total: {}"</span>, total);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>和迭代器适配器一样,Rust中还有<code>min()、max()、nth()、last()、product()、fold()</code>等消耗型适配器,这里也不展开了</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:Trait对象</title>
<link href="/posts/5cm2dmd2.html"/>
<url>/posts/5cm2dmd2.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="静态分发-static-dispatch"><a href="#静态分发-static-dispatch" class="headerlink" title="静态分发(static dispatch)"></a>静态分发(static dispatch)</h2><p>Rust中的静态分发(static dispatch)主要靠<code>泛型</code>来完成,对于不同的泛型类型参数,编译器会执行<code>单态化</code>处理,即<mark class="hl-label green">在编译阶段就确定好应该调用的函数</mark> ,为每一个被泛型类型参数代替的具体类型生成不同版本的函数和方法,这部分可以查看泛型部分进行了解</p><h3 id="单态化-monomorphization"><a href="#单态化-monomorphization" class="headerlink" title="单态化(monomorphization)"></a>单态化(monomorphization)</h3><p>单态化(monomorphization):即Rust编译器将一个泛型类型转化为多个非泛型类型的过程,编译器会在编译期间对每个实例进行单独优化,优化的结果是<mark class="hl-label orange">静态分发</mark> 有着<mark class="hl-label green">(1)性能更好、(2)代码体积更小、(3)能保证类型安全等优点</mark> 但也存在<mark class="hl-label blue">(1)代码灵活度不高、(2)编译时间更长、(3)输出二进制文件更大等不足</mark> </p><h2 id="动态分发-dynamic-dispatch"><a href="#动态分发-dynamic-dispatch" class="headerlink" title="动态分发(dynamic dispatch)"></a>动态分发(dynamic dispatch)</h2><p>Rust中的动态分发(dynamic dispatch)可以使用户在不知道具体的类型的情况下,调用泛型类型上的Trait方法。动态分发通过<code>Trait对象(Trait Object)</code>完成,具体的实现机制是:<mark class="hl-label pink">Trait对象将实现了某Trait的类型的指针包装在一个特殊的结构体中,而该结构体又包含一个指向实现该Trait的类型的指针和一个指向Trait的虚函数表(vtable)的指针</mark> ,结合下面的示意图可以更好地理解这段话<img src="https://i.vgy.me/B6xCTW.gif" alt="Trait对象示意图"></p><h3 id="虚函数表-vtable"><a href="#虚函数表-vtable" class="headerlink" title="虚函数表(vtable)"></a>虚函数表(vtable)</h3><p>虚函数表(vtable)是一个包含Trait方法的函数指针的数组,它允许在运行时动态调用Trait方法,当一个类型实现了一个Trait时,编译器会为该Trait生成一个虚函数表,并将该类型的实现添加到虚函数表中,<mark class="hl-label purple">即当一个对象被转换为Trait对象时,其会被分配一个指向该Trait的虚函数表的指针</mark> </p><h2 id="Trait对象"><a href="#Trait对象" class="headerlink" title="Trait对象"></a>Trait对象</h2><p>要创建一个Trait对象,需要使用<code>Box<dyn trait></code>或<code>&dyn Trait</code>关键字将实现了某Trait的类型的指针包装在一个<code>Box<dyn trait></code>或<code>&dyn trait</code>类型的变量中。这个变量可以被传递给函数或存储在变量中,并在运行时动态分配,比如下面的例子</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> width: <span class="type">f64</span>,</span><br><span class="line"> height: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * (<span class="keyword">self</span>.width + <span class="keyword">self</span>.height)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="keyword">self</span>.width * <span class="keyword">self</span>.height</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Circle</span> {</span><br><span class="line"> radius: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Circle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = <span class="title function_ invoke__">return_trait_object</span>(<span class="literal">false</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, rec.<span class="title function_ invoke__">area</span>());</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 以下的函数会返回一个Trait对象,这时用户不需关心返回的具体类型</span></span><br><span class="line"><span class="comment">// 只需要设置返回类型所需要满足的Trait</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">return_trait_object</span>(flag: <span class="type">bool</span>) <span class="punctuation">-></span> <span class="type">Box</span><<span class="keyword">dyn</span> Shape> {</span><br><span class="line"> <span class="keyword">if</span> flag {</span><br><span class="line"> <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(Rectangle {</span><br><span class="line"> width: <span class="number">11.0</span>,</span><br><span class="line"> height: <span class="number">9.0</span>,</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(Circle { radius: <span class="number">7.0</span> })</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 可以把上面的例子和Trait部分最后的代码示例做一下对比,看一下有什么区别</span></span><br></pre></td></tr></table></figure><p>但需要注意的是,动态分发相比于静态分发产生了更多的指针跳转操作,因此使用Trait对象时会在运行时产生一定额外的开销。实际情况下,用户应根据具体需求选择使用哪种分发模式</p><h3 id="对象安全"><a href="#对象安全" class="headerlink" title="对象安全"></a>对象安全</h3><p>在使用Trait对象时,应注意<mark class="hl-label orange">在Rust中,只能将对象安全(Object-Safe)的Trait转化为Trait对象</mark> ,而对象安全的Trait应满足<mark class="hl-label green">(1)方法返回的类型不是Self、(2)方法中不包含任何泛型类型参数</mark> ,比如标准库中的<code>Clone trait</code>就是一个非对象安全的trait</p><figure class="highlight rust"><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"><span class="comment">// Clone trait返回类型是Self,所以它是一个非对象安全的trait</span></span><br><span class="line"><span class="keyword">pub</span> <span class="keyword">trait</span> <span class="title class_">Clone</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">clone</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="keyword">Self</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:Box<T>智能指针</title>
<link href="/posts/pm7yb0b2.html"/>
<url>/posts/pm7yb0b2.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><h3 id="Box智能指针的组成"><a href="#Box智能指针的组成" class="headerlink" title="Box<T>智能指针的组成"></a><code>Box<T></code>智能指针的组成</h3><p>在Rust的智能指针中,<code>Box<T></code>是最简单的智能指针,<mark class="hl-label orange">其主要由两部分组成:(1)储存在堆内存(heap)上的数据、(2)储存在栈内存(stack)上的、指向堆内存数据的指针</mark> ,比如下面的例子中,我们使用<code>Box::new()</code>在堆内存上请求了一块区域来存储数字”5”,然后返回了一个指向该区域的<code>指针</code>并赋值给变量”a”</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">a</span> = <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(<span class="number">5</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* Box<T>示意图</span></span><br><span class="line"><span class="comment">------- ------ </span></span><br><span class="line"><span class="comment">|stack| |heap|</span></span><br><span class="line"><span class="comment">------- ------</span></span><br><span class="line"><span class="comment">| ptr ------------>|data|</span></span><br><span class="line"><span class="comment">------- ------</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><h3 id="Box指针的使用场景"><a href="#Box指针的使用场景" class="headerlink" title="Box<T>指针的使用场景"></a><code>Box<T></code>指针的使用场景</h3><ol><li>在编译时,某类型的大小无法确定。但使用该类型时,上下文却需要知道其确切大小</li><li>当存在大量数据想要移交所有权,但需确保操作时数据不会被复制</li><li>当使用某个值时,只关心其是否满足特定的Trait约束,而不关心其具体的类型<br>总体而言,<code>Box<T></code>的作用主要有:(1)动态分配内存、(2)所有权转移、(3)避免内存泄漏、(4)处理递归类型、(5)实现Trait对象(见Trait对象部分)</li></ol><h2 id="使用Box指针创建递归类型"><a href="#使用Box指针创建递归类型" class="headerlink" title="使用Box<T>指针创建递归类型"></a>使用<code>Box<T></code>指针创建递归类型</h2><p>我们首先必须了解这样一个规定:<mark class="hl-label orange">Rust在编译时,必须知道一个类型所占用的空间大小</mark> ,而递归类型(如链表)因为递归层数的不确定,其大小在编译时也是无法确定的,比如下面的代码就无法通过编译,因为<code>List</code>所占用的空间大小在编译时没法确定</p><figure class="highlight rust"><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"><span class="keyword">use</span> crate::List::{Cons, Nil};</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">List</span> {</span><br><span class="line"> <span class="title function_ invoke__">Cons</span>(<span class="type">i32</span>, List),</span><br><span class="line"> Nil,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">list</span> = <span class="title function_ invoke__">Cons</span>(<span class="number">1</span>, <span class="title function_ invoke__">Cons</span>(<span class="number">2</span>, <span class="title function_ invoke__">Cons</span>(<span class="number">3</span>, Nil)));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是,我们知道在Rust中指针变量的大小是可以确定的,在这里就可以使用<code>Box<T></code>来定义递归类型</p><figure class="highlight rust"><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"><span class="keyword">use</span> crate::List::{Cons, Nil};</span><br><span class="line"></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">List</span> {</span><br><span class="line"> <span class="title function_ invoke__">Cons</span>(<span class="type">i32</span>, <span class="type">Box</span><List>),</span><br><span class="line"> Nil,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">list</span> = <span class="title function_ invoke__">Cons</span>(<span class="number">1</span>, <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(<span class="title function_ invoke__">Cons</span>(<span class="number">2</span>, <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(<span class="title function_ invoke__">Cons</span>(<span class="number">3</span>, <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(Nil))))));</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">current</span> = &list;</span><br><span class="line"> <span class="comment">// 遍历list</span></span><br><span class="line"> <span class="keyword">loop</span> {</span><br><span class="line"> <span class="keyword">match</span> current {</span><br><span class="line"> <span class="title function_ invoke__">Cons</span>(value, next) => {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, value);</span><br><span class="line"> current = next;</span><br><span class="line"> }</span><br><span class="line"> Nil => <span class="keyword">break</span>,</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="两个基本trait"><a href="#两个基本trait" class="headerlink" title="两个基本trait"></a>两个基本trait</h2><h3 id="Deref-trait"><a href="#Deref-trait" class="headerlink" title="Deref trait"></a>Deref trait</h3><ul><li>Deref trait的作用:Deref trait可以让用户自定义<code>解引用运算符*</code>的行为,即通过实现Deref trait,用户可以像使用常规引用一样使用智能指针</li></ul><h4 id="Box的解引用"><a href="#Box的解引用" class="headerlink" title="Box<T>的解引用"></a><code>Box<T></code>的解引用</h4><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 常规引用的解引用</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">x</span> = &<span class="number">5</span>;</span><br><span class="line"> <span class="built_in">assert_eq!</span>(<span class="number">5</span>, *x);</span><br><span class="line"> <span class="comment">// Box<T>的解引用</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">y</span> = <span class="type">Box</span>::<span class="title function_ invoke__">new</span>(<span class="number">3</span>);</span><br><span class="line"> <span class="built_in">assert_eq!</span>(<span class="number">3</span>, *y);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="自定义Box指针"><a href="#自定义Box指针" class="headerlink" title="自定义Box<T>指针"></a>自定义<code>Box<T></code>指针</h4><p>在Rust中,<code>Box<T></code>被定义为拥有一个元素的元组结构体(tuple struct)</p><figure class="highlight rust"><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"><span class="keyword">use</span> std::ops::Deref;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyBox</span><T>(T);</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span><T> MyBox<T> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">new</span>(x: T) <span class="punctuation">-></span> MyBox<T> {</span><br><span class="line"> <span class="title function_ invoke__">MyBox</span>(x)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 为MyBox实现Deref trait让我们可以使用解引用运算符</span></span><br><span class="line"><span class="keyword">impl</span><T> Deref <span class="keyword">for</span> <span class="title class_">MyBox</span><T> {</span><br><span class="line"> <span class="keyword">type</span> <span class="title class_">Target</span> = T;</span><br><span class="line"> <span class="comment">// 实现Deref trait中的deref方法</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">deref</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> &<span class="keyword">Self</span>::Target {</span><br><span class="line"> &<span class="keyword">self</span>.<span class="number">0</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">x</span> = MyBox::<span class="title function_ invoke__">new</span>(<span class="number">5</span>);</span><br><span class="line"> <span class="built_in">assert_eq!</span>(<span class="number">5</span>, *x); <span class="comment">// 这里"*x"会被隐式的展开为"*(x.deref())"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="隐式解引用转化-Deref-Coercion"><a href="#隐式解引用转化-Deref-Coercion" class="headerlink" title="隐式解引用转化(Deref Coercion)"></a>隐式解引用转化(Deref Coercion)</h4><p>隐式解引用转化(Deref Coercion)是<mark class="hl-label orange">为函数和方法提供的一种便捷特性</mark> ,具体来说就是:如果一个类型T实现了Deref trait,那么在<mark class="hl-label green">编译时,编译器就会将T的引用隐式地转换为经过Deref操作后生成的引用</mark> </p><figure class="highlight rust"><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"><span class="keyword">use</span> std::ops::Deref;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyBox</span><T>(T);</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span><T> MyBox<T> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">new</span>(x: T) <span class="punctuation">-></span> MyBox<T> {</span><br><span class="line"> <span class="title function_ invoke__">MyBox</span>(x)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span><T> Deref <span class="keyword">for</span> <span class="title class_">MyBox</span><T> {</span><br><span class="line"> <span class="keyword">type</span> <span class="title class_">Target</span> = T;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">deref</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> &<span class="keyword">Self</span>::Target {</span><br><span class="line"> &<span class="keyword">self</span>.<span class="number">0</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">x</span> = MyBox::<span class="title function_ invoke__">new</span>(<span class="string">"hello, world!"</span>.<span class="title function_ invoke__">to_string</span>());</span><br><span class="line"> <span class="comment">// 注意:向hello()传参时会发生隐式解引用转化</span></span><br><span class="line"> <span class="title function_ invoke__">hello</span>(&x);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">hello</span>(s: &<span class="type">str</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在上面的代码中,变量<code>x</code>经过了两次Deref转换,即:<code>&Mybox<String> -> &String -> &str</code>(注:在标准库中String实现了Deref trait,所以它会隐式地转换为&str)</p><h3 id="Drop-trait"><a href="#Drop-trait" class="headerlink" title="Drop trait"></a>Drop trait</h3><ul><li>Drop trait的作用:实现Drop trait,可以让用户<code>自定义当值将要离开作用域时发生的动作</code>,比如文件或其他资源的释放等,它有点像Python中的<code>__del__</code>方法</li></ul><h4 id="drop-方法的触发"><a href="#drop-方法的触发" class="headerlink" title="drop()方法的触发"></a>drop()方法的触发</h4><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">CustomSmartPointer</span> {</span><br><span class="line"> data: <span class="type">String</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 为CustomSmartPointer实现Drop trait</span></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Drop</span> <span class="keyword">for</span> <span class="title class_">CustomSmartPointer</span> {</span><br><span class="line"> <span class="comment">// 实现drop()方法</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">drop</span>(&<span class="keyword">mut</span> <span class="keyword">self</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Dropping CustomSmartPointer with data `{}`!"</span>, <span class="keyword">self</span>.data);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">c</span> = CustomSmartPointer {</span><br><span class="line"> data: <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"my stuff"</span>),</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">d</span> = CustomSmartPointer {</span><br><span class="line"> data: <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"other stuff"</span>),</span><br><span class="line"> };</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"CustomSmartPointers created."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">OutPut:</span></span><br><span class="line"><span class="comment">CustomSmartPointers created. </span></span><br><span class="line"><span class="comment">Dropping CustomSmartPointer with data `other stuff`! </span></span><br><span class="line"><span class="comment">Dropping CustomSmartPointer with data `my stuff`! </span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>通过输出信息可以知道:程序运行完成后,内存中变量释放的顺序是从下往上的</p><h4 id="手动释放内存"><a href="#手动释放内存" class="headerlink" title="手动释放内存"></a>手动释放内存</h4><p>在Rust中,我们并不能直接调用某类型的drop()方法,但一般也不需要手动调用drop()方法,因为<code>Drop trait的功能是自动运行变量被释放后的一系列行为</code>,但有时我们又确实存在手动释放内存的需求,这时我们可以使用标准库提供的<mark class="hl-label green">std::mem::drop</mark> 函数</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> std::mem::drop;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">CustomSmartPointer</span> {</span><br><span class="line"> data: <span class="type">String</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Drop</span> <span class="keyword">for</span> <span class="title class_">CustomSmartPointer</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">drop</span>(&<span class="keyword">mut</span> <span class="keyword">self</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Dropping CustomSmartPointer with data `{}`!"</span>, <span class="keyword">self</span>.data);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">c</span> = CustomSmartPointer {</span><br><span class="line"> data: <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"my stuff"</span>),</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 手动释放变量</span></span><br><span class="line"> <span class="title function_ invoke__">drop</span>(c);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">d</span> = CustomSmartPointer {</span><br><span class="line"> data: <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"other stuff"</span>),</span><br><span class="line"> };</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"CustomSmartPointers created."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">OutPut:</span></span><br><span class="line"><span class="comment">Dropping CustomSmartPointer with data `my stuff`!</span></span><br><span class="line"><span class="comment">CustomSmartPointers created.</span></span><br><span class="line"><span class="comment">Dropping CustomSmartPointer with data `other stuff`!</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>可以发现打印的信息顺序发生了变化,这是因为我们先显式地释放了变量<code>c</code>,<code>c</code>在释放时触发了c.drop()方法,所以最后的结果表现不一样</p><h2 id="其他智能指针"><a href="#其他智能指针" class="headerlink" title="其他智能指针"></a>其他智能指针</h2><p>除<code>Box<T></code>外,Rust中还存在其他的智能指针,如<code>Rc<T></code>、<code>Arc<T></code>、<code>Mutex<T></code>等等,这些就等用到的时候再写吧…</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:闭包</title>
<link href="/posts/7h4old6e.html"/>
<url>/posts/7h4old6e.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="创建闭包"><a href="#创建闭包" class="headerlink" title="创建闭包"></a>创建闭包</h2><p>闭包的创建和函数的创建基本一致,但不用使用<code>fn</code>来定函数。比如下面的main函数中,我们定义了一个闭包</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 标准语法</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">add</span> = |x: <span class="type">f64</span>, y: <span class="type">f64</span>| <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{} + {} = {}"</span>, x, y, x + y);</span><br><span class="line"> x + y</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">_result</span> = <span class="title function_ invoke__">add</span>(<span class="number">1.0</span>, <span class="number">2.0</span>); <span class="comment">// 调用闭包</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>一般情况下,我们其实可以采用简化语法来创建闭包</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 简化语法</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">add</span> = |x, y| {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{} + {} = {}"</span>, x, y, x + y);</span><br><span class="line"> x + y</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">_result</span> = <span class="title function_ invoke__">add</span>(<span class="number">1.0</span>, <span class="number">2.0</span>); <span class="comment">// 调用闭包</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="闭包中的变量类型"><a href="#闭包中的变量类型" class="headerlink" title="闭包中的变量类型"></a>闭包中的变量类型</h2><p>在创建和使用闭包时,编译器通常可以直接推断出使用的变量类型而不用自己定义。但需注意的是,变量类型确定后就不能再使用其他类型的变量了</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">c</span> = |x| x;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s</span> = <span class="title function_ invoke__">c</span>(<span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"hello"</span>));</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">n</span> = <span class="title function_ invoke__">c</span>(<span class="number">5</span>); <span class="comment">// 注意:该行会发生panic,因为闭包c的参数类型已经确定为"String"类型</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="捕获环境"><a href="#捕获环境" class="headerlink" title="捕获环境"></a>捕获环境</h2><p>闭包可捕获其所在定义域的环境值,这让闭包的使用比普通函数灵活得多</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">var</span> = <span class="number">3</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">var_add</span> = |x| {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{} + {} = {}"</span>, x, var, x + var)</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">var_add</span>(<span class="number">3</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>类似的,如果使用函数的话就不能满足这样的要求了</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">var</span> = <span class="number">3</span>;</span><br><span class="line"> <span class="comment">// 以下的写法会引发panic</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">var_add</span>(x: <span class="type">i32</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{} + {} = {}"</span>, x, var, x + var)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">var_add</span>(<span class="number">3</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="闭包所捕获环境值的所有权"><a href="#闭包所捕获环境值的所有权" class="headerlink" title="闭包所捕获环境值的所有权"></a>闭包所捕获环境值的所有权</h2><p>闭包捕获的环境值的所有权和普通函数的所有权保持一致,一共有三种方式</p><h3 id="取得变量所有权:FnOnce"><a href="#取得变量所有权:FnOnce" class="headerlink" title="取得变量所有权:FnOnce"></a>取得变量所有权:<code>FnOnce</code></h3><p>满足<code>FnOnce</code> trait的闭包捕获的环境值只能被闭包调用一次,后续无法再次调用</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">name</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"rust"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">c</span> = <span class="keyword">move</span> |greeting: <span class="type">String</span>| (greeting, name); <span class="comment">// 使用move关键字将变量name的所有权传入闭包中</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">result</span> = <span class="title function_ invoke__">c</span>(<span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>());</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"result: {:?}"</span>, result);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, name); <span class="comment">// 因为变量name的所有权已经转移了,所以这行会发生错误</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="获得变量的可变借用:FnMut"><a href="#获得变量的可变借用:FnMut" class="headerlink" title="获得变量的可变借用:FnMut"></a>获得变量的可变借用:<code>FnMut</code></h3><p>满足<code>FnMut</code> trait的闭包可以获得环境值的可变借用</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">name</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"hello"</span>); </span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">c_mut</span> = || { </span><br><span class="line"> name.<span class="title function_ invoke__">push_str</span>(<span class="string">" rust"</span>); </span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"c: {}"</span>, name); </span><br><span class="line"> }; </span><br><span class="line"> <span class="title function_ invoke__">c_mut</span>();</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, name); <span class="comment">// 变量name的所有权没有改变,但其中的值已经被修改了</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="获得变量的不可变借用:Fn"><a href="#获得变量的不可变借用:Fn" class="headerlink" title="获得变量的不可变借用:Fn"></a>获得变量的不可变借用:<code>Fn</code></h3><p>满足<code>Fn</code> trait的闭包不会消耗所捕获环境值的所有权</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">y</span> = <span class="type">String</span>::<span class="title function_ invoke__">from</span>(<span class="string">"hello"</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">closure</span> = |x| {</span><br><span class="line"> x == y</span><br><span class="line"> };</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, <span class="title function_ invoke__">closure</span>(<span class="string">"hello"</span>));</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, y);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在创建闭包时,Rust可以通过闭包对环境值的使用来推断出闭包具体使用哪个trait,具体而言,即(1)<code>所有的闭包都实现了FnOnce trait</code>、(2)<code>没有移动捕获变量的实现了FnMut trait</code>、(3)<code>无需可变访问捕获变量的闭包实现了Fn trait</code></p><h2 id="闭包作为参数"><a href="#闭包作为参数" class="headerlink" title="闭包作为参数"></a>闭包作为参数</h2><p>Rust支持函数式编程,所以闭包也可以作为参数、返回值和结构体成员变量。但需注意值的类型是实现<code>Fn</code>、<code>FnOnce</code>或者<code>FnMut</code> trait的类型</p><h3 id="作为传入参数"><a href="#作为传入参数" class="headerlink" title="作为传入参数"></a>作为传入参数</h3><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">closure</span> = |x: <span class="type">i32</span>| {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"print in closure: {}"</span>, x);</span><br><span class="line"> x</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">call_closure</span>(closure)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">call_closure</span>(closure: <span class="keyword">impl</span> <span class="title class_">Fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>) {</span><br><span class="line"> <span class="title function_ invoke__">closure</span>(<span class="number">10</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="作为返回值"><a href="#作为返回值" class="headerlink" title="作为返回值"></a>作为返回值</h3><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">a</span> = <span class="title function_ invoke__">call_closure</span>();</span><br><span class="line"> <span class="title function_ invoke__">a</span>(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">call_closure</span>() <span class="punctuation">-></span> <span class="keyword">impl</span> <span class="title class_">Fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span> {</span><br><span class="line"> |x: <span class="type">i32</span>| {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"print in closure: {}"</span>, x);</span><br><span class="line"> x</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><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Cacher</span><T> </span><br><span class="line"> <span class="keyword">where</span> T: <span class="title function_ invoke__">Fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>,</span><br><span class="line">{</span><br><span class="line"> calc: T,</span><br><span class="line"> value: <span class="type">Option</span><<span class="type">i32</span>>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span><T> Cacher<T> </span><br><span class="line"> <span class="keyword">where</span> T: <span class="title function_ invoke__">Fn</span>(<span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span>,</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">new</span>(clac: T) <span class="punctuation">-></span> <span class="keyword">Self</span> {</span><br><span class="line"> Cacher { </span><br><span class="line"> calc: clac, </span><br><span class="line"> value: <span class="literal">None</span>,</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">value</span>(&<span class="keyword">mut</span> <span class="keyword">self</span>, arg: <span class="type">i32</span>) <span class="punctuation">-></span> <span class="type">i32</span> {</span><br><span class="line"> <span class="keyword">match</span> <span class="keyword">self</span>.value {</span><br><span class="line"> <span class="title function_ invoke__">Some</span>(v) => v,</span><br><span class="line"> <span class="literal">None</span> => {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">v</span> = (<span class="keyword">self</span>.calc)(arg); <span class="comment">// 调用传入闭包</span></span><br><span class="line"> <span class="keyword">self</span>.value = <span class="title function_ invoke__">Some</span>(v);</span><br><span class="line"> v</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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">cacher</span> = Cacher::<span class="title function_ invoke__">new</span>(|x| x); <span class="comment">// 将闭包传递进结构体中,作为成员变量</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"caching: {:?}"</span>, cacher.<span class="title function_ invoke__">value</span>(<span class="number">2</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:泛型</title>
<link href="/posts/zr161ci0.html"/>
<url>/posts/zr161ci0.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><p>泛型的作用主要体现在提高代码的复用能力。在官方文档中,泛型被定义为<code>具体类型或其他属性的抽象代替</code>,在实际使用时可以将泛型理解为一种<code>模板</code>或<code>占位符</code>,编译器会在<mark class="hl-label green">编译时</mark> 执行<code>单态化(monomorphization)</code>操作,即<mark class="hl-label red">将泛型类型替换为具体的类型</mark> </p><h2 id="在结构体中使用泛型"><a href="#在结构体中使用泛型" class="headerlink" title="在结构体中使用泛型"></a>在结构体中使用泛型</h2><p>在结构体中使用泛型一般存在两种情况,其一为定义结构体时,其二为在方法定义时</p><h3 id="在定义中使用泛型"><a href="#在定义中使用泛型" class="headerlink" title="在定义中使用泛型"></a>在定义中使用泛型</h3><figure class="highlight rust"><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"><span class="comment">// 注意:这里的泛型类型要求x和y的类型必须一致</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Point</span><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"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Point { x: <span class="number">12.5</span>, y: <span class="number">9.2</span> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">squ</span> = Point { x: <span class="number">9</span>, y: <span class="number">9</span> };</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"x: {}, y: {}"</span>, rec.x, rec.y);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"x: {}, y: {}"</span>, squ.x, squ.y);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="在方法中使用泛型"><a href="#在方法中使用泛型" class="headerlink" title="在方法中使用泛型"></a>在方法中使用泛型</h3><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注意:这里的泛型类型要求x和y的类型可以不一致,也可以一致</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Point</span><T, U> {</span><br><span class="line"> x: T,</span><br><span class="line"> y: U,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 此处意思为针对泛型类型T和U实现的方法而不是针对某特定类型实现方法,即所有类型都可以调用该方法</span></span><br><span class="line"><span class="keyword">impl</span><T, U> Point<T, U> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">mixup</span><V, W>(<span class="keyword">self</span>, other: Point<V, W>) <span class="punctuation">-></span> Point<T, W> {</span><br><span class="line"> Point {</span><br><span class="line"> x: <span class="keyword">self</span>.x,</span><br><span class="line"> y: other.y,</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 class="comment">// 此处意思为仅针对数据类型为i32时实现方法,当结构体中字段为非i32类型时无法调用该方法</span></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Point</span><<span class="type">i32</span>, <span class="type">i32</span>> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">mix_i32</span>(<span class="keyword">self</span>, other: Point<<span class="type">i32</span>, <span class="type">i32</span>>) <span class="punctuation">-></span> Point<<span class="type">i32</span>, <span class="type">i32</span>> {</span><br><span class="line"> Point {</span><br><span class="line"> x: <span class="keyword">self</span>.x,</span><br><span class="line"> y: other.y,</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 class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">p1</span> = Point { x: <span class="number">5</span>, y: <span class="number">10.4</span> };</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">p2</span> = Point { x: <span class="string">"Hello"</span>, y: <span class="string">'c'</span>};</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">p3</span> = p1.<span class="title function_ invoke__">mixup</span>(p2);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"p3.x = {}, p3.y = {}"</span>, p3.x, p3.y);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>同时,由上面的例子可注意到在结构体中的泛型类型参数<code>可以</code>和方法的泛型类型参数不同</p><h2 id="在枚举中使用泛型"><a href="#在枚举中使用泛型" class="headerlink" title="在枚举中使用泛型"></a>在枚举中使用泛型</h2><p>在枚举中使用泛型和结构体类似,一般用在使枚举的变体持有泛型数据类型。比如Option枚举和Result枚举<br> <figure class="highlight rust"><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"><span class="comment">// Option枚举</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">Option</span><T> {</span><br><span class="line"> <span class="title function_ invoke__">Some</span>(T),</span><br><span class="line"> <span class="literal">None</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Result枚举</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">Result</span><T, E> {</span><br><span class="line"> <span class="title function_ invoke__">Ok</span>(T),</span><br><span class="line"> <span class="title function_ invoke__">Err</span>(E),</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="在函数中使用泛型"><a href="#在函数中使用泛型" class="headerlink" title="在函数中使用泛型"></a>在函数中使用泛型</h2><p>以下是一个最简单的在函数中使用泛型的例子,但在实际项目中,我们通常需要<mark class="hl-label red">对泛型进行限制以保证函数得到的参数能满足后续相关的功能要求</mark> ,这时需要使用Trait对泛型参数进行约束</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">a</span> = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">b</span> = <span class="number">2.0</span>;</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, <span class="title function_ invoke__">back</span>(a));</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, <span class="title function_ invoke__">back</span>(b));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">back</span><T>(item: T) <span class="punctuation">-></span> T {</span><br><span class="line"> item</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="泛型的Trait约束"><a href="#泛型的Trait约束" class="headerlink" title="泛型的Trait约束"></a>泛型的Trait约束</h2><p>在Trait部分我们说,可以通过Trait对传入或返回参数的类型进行约束。在<mark class="hl-label blue">使用泛型类型参数时可以配合Trait使用,以实现对传入参数的类型进行约束</mark> </p><h3 id="简单情况"><a href="#简单情况" class="headerlink" title="简单情况"></a>简单情况</h3><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> std::fmt::<span class="built_in">Debug</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> width: <span class="type">f64</span>,</span><br><span class="line"> height: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * (<span class="keyword">self</span>.width + <span class="keyword">self</span>.height)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="keyword">self</span>.width * <span class="keyword">self</span>.height</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle {</span><br><span class="line"> width: <span class="number">12.0</span>,</span><br><span class="line"> height: <span class="number">20.0</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">trait_param</span>(&rec);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类型T必须实现Shape trait和Debug trait</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">trait_param</span><T: Shape + <span class="built_in">Debug</span>>(item: &T) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, item);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, item.<span class="title function_ invoke__">area</span>());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="使用where关键字"><a href="#使用where关键字" class="headerlink" title="使用where关键字"></a>使用<code>where</code>关键字</h3><p>在上面的例子中,我们实现了对函数传入参数进行约束。但在实际项目中,泛型参数或Trait约束较多会造成代码不易阅读,我们可以使用<code>where</code>关键字来使代码更加直观。上面例子其实可以看做使用<code>where</code>关键字的语法糖</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> std::fmt::<span class="built_in">Debug</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> width: <span class="type">f64</span>,</span><br><span class="line"> height: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * (<span class="keyword">self</span>.width + <span class="keyword">self</span>.height)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="keyword">self</span>.width * <span class="keyword">self</span>.height</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Circle</span> {</span><br><span class="line"> radius: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Circle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle {</span><br><span class="line"> width: <span class="number">12.0</span>,</span><br><span class="line"> height: <span class="number">20.0</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">cir</span> = Circle { radius: <span class="number">11.0</span> };</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">trait_param</span>(&rec, &cir);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">trait_param</span><T, U>(rec: &T, cir: &U)</span><br><span class="line"><span class="comment">// 使用where对多个泛型参数设置多个Trait约束</span></span><br><span class="line"><span class="keyword">where</span></span><br><span class="line"> <span class="comment">// 类型T必须实现Shape trait</span></span><br><span class="line"> T: Shape,</span><br><span class="line"> <span class="comment">// 类型U必须实现Shape trait和Debug trait</span></span><br><span class="line"> U: Shape + <span class="built_in">Debug</span>,</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, rec.<span class="title function_ invoke__">area</span>());</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, cir);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:Trait</title>
<link href="/posts/akex76ga.html"/>
<url>/posts/akex76ga.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="Trait的作用"><a href="#Trait的作用" class="headerlink" title="Trait的作用"></a>Trait的作用</h2><p>在Rust中Trait(特质)是一种任何类型都可以选择支持或不支持的一种定义行为的机制,Trait可以被认为是某类型能够做什么的一种能力,其类似于其他语言中的接口,但存在一定区别。Trait的作用主要包含(1)定义共享行为、(2)实现多态、(3)扩展类型的功能、(4)提高代码可读性和可维护性、(5)约束泛型类型(见泛型部分)</p><h2 id="Trait的定义与实现"><a href="#Trait的定义与实现" class="headerlink" title="Trait的定义与实现"></a>Trait的定义与实现</h2><h3 id="Trait的定义"><a href="#Trait的定义" class="headerlink" title="Trait的定义"></a>Trait的定义</h3><p>使用<code>trait</code>关键字定义一个Trait。Trait的定义是将方法的签名进行封装,以定义实现某种目的所必须的一组行为</p><figure class="highlight rust"><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"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="comment">// 定义计算面积的方法签名</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="comment">// 定义计算周长的方法签名</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在Trait的定义中只有方法签名而没有具体的实现,<mark class="hl-label orange">其中封装的方法的具体实现由实现该Trait的类型提供</mark> </p><h3 id="Trait的实现"><a href="#Trait的实现" class="headerlink" title="Trait的实现"></a>Trait的实现</h3><p>Trait中定义方法的实现和结构体或枚举方法的实现很相似,都是使用<code>impl</code>关键字。具体写法为<code>impl ... for ...</code></p><figure class="highlight rust"><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"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> width: <span class="type">f64</span>,</span><br><span class="line"> height: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 为Rectangle提供Shape trait实现</span></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="keyword">self</span>.width * <span class="keyword">self</span>.height</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * (<span class="keyword">self</span>.width + <span class="keyword">self</span>.height)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Circle</span> {</span><br><span class="line"> radius: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 为Circle提供Shape trait实现</span></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Circle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle {</span><br><span class="line"> width: <span class="number">12.0</span>,</span><br><span class="line"> height: <span class="number">20.0</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 调用已经实现Trait中的方法也和调用普通方法一致</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"area: {}, perimeter: {}"</span>, rec.<span class="title function_ invoke__">area</span>(), rec.<span class="title function_ invoke__">perimeter</span>());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在上面的例子中,“圆”和“矩形”是不同的类型,但我们可以在函数中使用同样的函数签名来调用它们绑定的方法</p><h2 id="Trait的实现约束"><a href="#Trait的实现约束" class="headerlink" title="Trait的实现约束"></a>Trait的实现约束</h2><p>在类型上实现Trait存在两个限制条件</p><ol><li><mark class="hl-label red">这个类型`或`这个Trait(至少有一个)是在本地crate定义的</mark> ,比如我们可以为Rectangle类型实现Debug Trait,也可以为Vector类型实现Shape Trait</li><li>为确保两个不同库的代码不互相影响,在Rust中<mark class="hl-label red">无法为外部类型实现外部Trait</mark> ,比如我们无法为标准库中的String类型实现Display Trait</li></ol><h2 id="Trait默认实现与重载"><a href="#Trait默认实现与重载" class="headerlink" title="Trait默认实现与重载"></a>Trait默认实现与重载</h2><h3 id="Trait的默认实现"><a href="#Trait的默认实现" class="headerlink" title="Trait的默认实现"></a>Trait的默认实现</h3><p>在前面,我们说Trait所封装的方法的实现由实现这个Trait的类型提供,但在定义Trait时其实也可以提供默认实现</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="comment">// 提供默认实现</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"default method, return perimeter"</span>);</span><br><span class="line"> <span class="comment">// 在默认实现中可以调用trait中定义的方法,且不用管其他方法是否提供了默认实现</span></span><br><span class="line"> <span class="keyword">self</span>.<span class="title function_ invoke__">perimeter</span>()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> width: <span class="type">f64</span>,</span><br><span class="line"> height: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="comment">// 当一个类型想调用默认实现时,不需要为该类型实现已经默认实现的方法</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * (<span class="keyword">self</span>.width + <span class="keyword">self</span>.height)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle {</span><br><span class="line"> width: <span class="number">12.0</span>,</span><br><span class="line"> height: <span class="number">20.0</span>,</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 这里的代码调用了Shaper trait中的默认实现,最后打印的是周长</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"rec area: {}"</span>, rec.<span class="title function_ invoke__">area</span>());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面的例子中,Trait在定义时就提供了默认实现。但需要注意的是,<mark class="hl-label red">默认实现无法获得类型中的字段(field)</mark> ,因为Trait定义的是具体类型的共享行为,它无法知道用户定义的类型会提供什么数据,比如我们在矩形中我们提供了长和宽,而圆只提供了半径</p><h3 id="Trait的重载"><a href="#Trait的重载" class="headerlink" title="Trait的重载"></a>Trait的重载</h3><p>虽然Trait可以提供默认实现,但我们也可以针对特定类型进行Trait的重载,重载不会影响其他类型调用默认实现</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"default method, return perimeter"</span>);</span><br><span class="line"> <span class="keyword">self</span>.<span class="title function_ invoke__">perimeter</span>()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Circle</span> {</span><br><span class="line"> radius: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Circle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 重载默认实现</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">cir</span> = Circle {</span><br><span class="line"> radius: <span class="number">5.0</span>,</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 因为Circle重载了area()方法,所以打印的是正确的面积值</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"cir area: {}"</span>, cir.<span class="title function_ invoke__">area</span>());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Trait作为传入类型和返回类型"><a href="#Trait作为传入类型和返回类型" class="headerlink" title="Trait作为传入类型和返回类型"></a>Trait作为传入类型和返回类型</h2><h3 id="作为传入类型"><a href="#作为传入类型" class="headerlink" title="作为传入类型"></a>作为传入类型</h3><p>在Rust中,我们可以将Trait作为函数传入的参数类型,就可以实现将多种具体的类型传入同一函数</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> width: <span class="type">f64</span>,</span><br><span class="line"> height: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * (<span class="keyword">self</span>.width + <span class="keyword">self</span>.height)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="keyword">self</span>.width * <span class="keyword">self</span>.height</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle {</span><br><span class="line"> width: <span class="number">12.0</span>,</span><br><span class="line"> height: <span class="number">20.0</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="title function_ invoke__">trait_param</span>(rec);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个函数接收一个实现Shape trait的类型</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">trait_param</span>(item: <span class="keyword">impl</span> <span class="title class_">Shape</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, item.<span class="title function_ invoke__">area</span>());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在上面的例子中,<code>trait_param()</code>这个函数接收一个实现了Shape trait的类型,在调用时可以将任何实现Shape trait的类型传入。当我们想约束传入的类型需要实现多个Trait时,Trait之间可以使用<code>+</code>号连接</p><h3 id="作为返回类型"><a href="#作为返回类型" class="headerlink" title="作为返回类型"></a>作为返回类型</h3><p>和传入参数时类似,Trait也可以作为返回类型</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">use</span> std::fmt::<span class="built_in">Debug</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> width: <span class="type">f64</span>,</span><br><span class="line"> height: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * (<span class="keyword">self</span>.width + <span class="keyword">self</span>.height)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="keyword">self</span>.width * <span class="keyword">self</span>.height</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = <span class="title function_ invoke__">return_trait</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, rec);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个函数会返回一个实现了Shape trait和Debug trait的类型</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">return_trait</span>() <span class="punctuation">-></span> <span class="keyword">impl</span> <span class="title class_">Shape</span> + <span class="built_in">Debug</span> {</span><br><span class="line"> Rectangle {</span><br><span class="line"> width: <span class="number">11.0</span>,</span><br><span class="line"> height: <span class="number">9.0</span>,</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面的例子中,<code>return_trait()</code>函数会返回一个实现Shape trait和Debug trait的类型。但是需要注意的是,<mark class="hl-label orange">当我们使用特定trait作为返回类型时,用户必须保证这个函数返回的具体类型是确定的</mark> ,否则就会像下面例子中的代码一样出现错误</p><figure class="highlight rust"><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"><span class="keyword">use</span> std::fmt::<span class="built_in">Debug</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">trait</span> <span class="title class_">Shape</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> width: <span class="type">f64</span>,</span><br><span class="line"> height: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * (<span class="keyword">self</span>.width + <span class="keyword">self</span>.height)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="keyword">self</span>.width * <span class="keyword">self</span>.height</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Circle</span> {</span><br><span class="line"> radius: <span class="type">f64</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Shape</span> <span class="keyword">for</span> <span class="title class_">Circle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">perimeter</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> <span class="number">2.0</span> * std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">f64</span> {</span><br><span class="line"> std::<span class="type">f64</span>::consts::PI * <span class="keyword">self</span>.radius * <span class="keyword">self</span>.radius</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = <span class="title function_ invoke__">return_trait</span>(<span class="literal">false</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, rec);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 以下的函数会出现一个错误,因为函数返回的具体类型的可能性不唯一</span></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">return_trait</span>(flag: <span class="type">bool</span>) <span class="punctuation">-></span> <span class="keyword">impl</span> <span class="title class_">Shape</span> + <span class="built_in">Debug</span> {</span><br><span class="line"> <span class="keyword">if</span> flag {</span><br><span class="line"> Rectangle {</span><br><span class="line"> width: <span class="number">11.0</span>,</span><br><span class="line"> height: <span class="number">9.0</span>,</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> Circle { radius: <span class="number">7.0</span> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>以上的例子是无法通过编译的,但如果我们确实需要实现与例子中<code>return_trait()</code>函数类似的功能也是有办法的,具体见Trait对象部分</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:枚举</title>
<link href="/posts/0lozupq7.html"/>
<url>/posts/0lozupq7.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="枚举的基本操作"><a href="#枚举的基本操作" class="headerlink" title="枚举的基本操作"></a>枚举的基本操作</h2><p>在Rust中,枚举类型是一种通过<code>enum</code>关键字定义的特殊的类型,允许用户定义一个取值范围有限的变量</p><h3 id="定义枚举"><a href="#定义枚举" class="headerlink" title="定义枚举"></a>定义枚举</h3><p>一个枚举可以包含一系列命名的值,这些值就被称为枚举成员(enumeration variant)</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义枚举</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">Species</span> {</span><br><span class="line"> Human,</span><br><span class="line"> Animal,</span><br><span class="line"> Plant,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 创建枚举变量</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">john</span> = Species::Human;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tom</span> = Species::Animal;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>枚举类型作为自定义数据类型,和其他的数据类型一样,枚举变量也可以通过函数进行传递或作为结构体字段类型</p><figure class="highlight rust"><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"><span class="keyword">enum</span> <span class="title class_">Species</span> {</span><br><span class="line"> Human,</span><br><span class="line"> Animal,</span><br><span class="line"> Plant,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 作为结构体字段类型</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Earth</span> {</span><br><span class="line"> leader: Species,</span><br><span class="line"> member: <span class="type">String</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">john</span> = Species::Human;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tom</span> = Species::Animal;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在函数间传递</span></span><br><span class="line"> <span class="title function_ invoke__">sleep</span>(john);</span><br><span class="line"> <span class="title function_ invoke__">sleep</span>(tom);</span><br><span class="line"> <span class="title function_ invoke__">sleep</span>(Species::Animal);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">sleep</span>(species: Species) {}</span><br></pre></td></tr></table></figure><p>枚举成员可以是一个简单的标识符,也可以包含数据或者匿名结构体</p><figure class="highlight rust"><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"><span class="keyword">enum</span> <span class="title class_">Species</span> {</span><br><span class="line"> <span class="title function_ invoke__">Human</span>(<span class="type">String</span>),</span><br><span class="line"> <span class="title function_ invoke__">Animal</span>(<span class="type">String</span>),</span><br><span class="line"> <span class="title function_ invoke__">Plant</span>(<span class="type">String</span>),</span><br><span class="line"> Alien { name: <span class="type">String</span>, attitude: <span class="type">String</span> },</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">john</span> = Species::<span class="title function_ invoke__">Human</span>(<span class="string">"Male"</span>.<span class="title function_ invoke__">to_string</span>());</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tom</span> = Species::<span class="title function_ invoke__">Animal</span>(<span class="string">"Cat"</span>.<span class="title function_ invoke__">to_string</span>());</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tiga</span> = Species::Alien {</span><br><span class="line"> name: <span class="string">"Ultraman"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> attitude: <span class="string">"Friendly"</span>.<span class="title function_ invoke__">to_string</span>(),</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>和结构体类似,枚举也可以使用<code>impl</code>定义其关联的方法</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">Species</span> {</span><br><span class="line"> <span class="title function_ invoke__">Human</span>(<span class="type">String</span>),</span><br><span class="line"> <span class="title function_ invoke__">Animal</span>(<span class="type">String</span>),</span><br><span class="line"> <span class="title function_ invoke__">Plant</span>(<span class="type">String</span>),</span><br><span class="line"> Alien { name: <span class="type">String</span>, attitude: <span class="type">String</span> },</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Species</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">war</span>(&<span class="keyword">self</span>) {</span><br><span class="line"> <span class="comment">// 使用模式匹配解构枚举变量</span></span><br><span class="line"> <span class="keyword">match</span> <span class="keyword">self</span> {</span><br><span class="line"> Species::Alien { name, attitude } <span class="keyword">if</span> attitude == <span class="string">"Hostile"</span> => {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{} is a threat, Start War!"</span>, name);</span><br><span class="line"> }</span><br><span class="line"> _ => {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"Friendly!"</span>);</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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tiga</span> = Species::Alien {</span><br><span class="line"> name: <span class="string">"Ultraman"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> attitude: <span class="string">"Friendly"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">thanos</span> = Species::Alien {</span><br><span class="line"> name: <span class="string">"Eternals"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> attitude: <span class="string">"Hostile"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> };</span><br><span class="line"> tiga.<span class="title function_ invoke__">war</span>();</span><br><span class="line"> thanos.<span class="title function_ invoke__">war</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="枚举的解构"><a href="#枚举的解构" class="headerlink" title="枚举的解构"></a>枚举的解构</h2><p>当我们想使用枚举中的值或针对特定枚举执行相应的操作时,就需要通过模式匹配(pattern matching)来解构枚举。具体来说,可以使用<code>match</code>或<code>if let</code>表达式,将枚举变量与每个可能的值进行比较,并执行相应的操作</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">Species</span> {</span><br><span class="line"> Quit,</span><br><span class="line"> <span class="title function_ invoke__">Human</span>(<span class="type">String</span>),</span><br><span class="line"> <span class="title function_ invoke__">Animal</span>(<span class="type">String</span>),</span><br><span class="line"> <span class="title function_ invoke__">Plant</span>(<span class="type">String</span>),</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">flag</span> = Species::Quit;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">person</span> = Species::<span class="title function_ invoke__">Human</span>(<span class="string">"john"</span>.<span class="title function_ invoke__">to_string</span>());</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">cat</span> = Species::<span class="title function_ invoke__">Animal</span>(<span class="string">"tom"</span>.<span class="title function_ invoke__">to_string</span>());</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">chaos</span> = Species::<span class="title function_ invoke__">Human</span>(<span class="string">"jeff"</span>.<span class="title function_ invoke__">to_string</span>());</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用match匹配所有条件</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">person_name</span> = <span class="keyword">match</span> person {</span><br><span class="line"> Species::<span class="title function_ invoke__">Human</span>(name) => name,</span><br><span class="line"> Species::<span class="title function_ invoke__">Plant</span>(name) => name,</span><br><span class="line"> Species::<span class="title function_ invoke__">Animal</span>(name) => name,</span><br><span class="line"> Species::Quit => <span class="string">"No Person"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用if let匹配单个条件 </span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">chaos_name</span> = <span class="keyword">if</span> <span class="keyword">let</span> <span class="variable">Species</span>::<span class="title function_ invoke__">Plant</span>(name) = chaos {</span><br><span class="line"> name</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> <span class="keyword">let</span> <span class="variable">Species</span>::<span class="title function_ invoke__">Animal</span>(name) = chaos {</span><br><span class="line"> name</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="string">"chaos Unknown!"</span>.<span class="title function_ invoke__">to_string</span>()</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用"_"通配符匹配其余所有条件</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">animal_name</span> = <span class="keyword">match</span> cat {</span><br><span class="line"> Species::<span class="title function_ invoke__">Animal</span>(name) => <span class="title function_ invoke__">Some</span>(name),</span><br><span class="line"> _ => <span class="literal">None</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">let</span> <span class="variable">Species</span>::Quit = flag {}</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, person_name);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, animal_name);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, chaos_name);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>由上面例子可以看出来,使用<code>match</code>对枚举进行解构时需列举所有的可能情况,但我们也可以使用<code>_</code>通配符来简化匹配;<code>if let</code>则只适用于匹配单个模式的情况,但可配合使用<code>else</code>和<code>else if let</code>来匹配多个模式</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:结构体</title>
<link href="/posts/56u6za0a.html"/>
<url>/posts/56u6za0a.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="结构体基本操作"><a href="#结构体基本操作" class="headerlink" title="结构体基本操作"></a>结构体基本操作</h2><h3 id="定义、实例化"><a href="#定义、实例化" class="headerlink" title="定义、实例化"></a>定义、实例化</h3><p>在实例化结构体时,字段内的赋值顺序可以和结构体定义不同,但是字段数量必须和定义一致</p><figure class="highlight rust"><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"><span class="comment">// 定义结构体</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Person</span> {</span><br><span class="line"> name: <span class="type">String</span>,</span><br><span class="line"> age: <span class="type">u8</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 实例化</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tom</span> = Person {</span><br><span class="line"> name: <span class="string">"tom"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> age: <span class="number">10</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 实例化</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">jerry</span> = Person {</span><br><span class="line"> age: <span class="number">8</span>,</span><br><span class="line"> name: <span class="string">"jerry"</span>.<span class="title function_ invoke__">to_string</span>(),</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>需要注意的是,当我们将结构体声明为可变对象时,它下属的所有字段就都是可变的</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Person</span> {</span><br><span class="line"> name: <span class="type">String</span>,</span><br><span class="line"> age: <span class="type">u8</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 注意:这个结构体是可变的</span></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">tom</span> = Person {</span><br><span class="line"> name: <span class="string">"tom"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> age: <span class="number">10</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 访问结构体中的字段</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"name: {}, age: {}"</span>, tom.name, tom.age);</span><br><span class="line"> <span class="comment">// 修改结构体中的字段</span></span><br><span class="line"> tom.name = <span class="string">"jerry"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"name: {}, age: {}"</span>, tom.name, tom.age);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="基于已有实例创建新实例"><a href="#基于已有实例创建新实例" class="headerlink" title="基于已有实例创建新实例"></a>基于已有实例创建新实例</h3><figure class="highlight rust"><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"><span class="keyword">struct</span> <span class="title class_">Person</span> {</span><br><span class="line"> name: <span class="type">String</span>,</span><br><span class="line"> age: <span class="type">u8</span>,</span><br><span class="line"> species: <span class="type">String</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tom</span> = Person {</span><br><span class="line"> name: <span class="string">"tom"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> age: <span class="number">10</span>,</span><br><span class="line"> species: <span class="string">"cat"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 基于已有实例创建新实例</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">jerry</span> = Person {</span><br><span class="line"> name: <span class="string">"jerry"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> species: <span class="string">"mouse"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> ..tom</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="元组结构体-Tuple-Struct"><a href="#元组结构体-Tuple-Struct" class="headerlink" title="元组结构体(Tuple Struct)"></a>元组结构体(Tuple Struct)</h3><p>在某些情况下,我们只想给元组整体取名来实现其相对于别的元组的不同。比如下面的例子中的black和origin,虽然它们内部的字段是一样的,但是他们是不同类型的结构体,这样就可以针对其类型定义它们不同的行为</p><figure class="highlight rust"><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"><span class="comment">// 注意:定义元组结构体需要以分号结尾</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Color</span>(<span class="type">i32</span>, <span class="type">i32</span>, <span class="type">i32</span>);</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Point</span>(<span class="type">i32</span>, <span class="type">i32</span>, <span class="type">i32</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">black</span> = <span class="title function_ invoke__">Color</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">origin</span> = <span class="title function_ invoke__">Point</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="空结构体-Unit-Like-Struct"><a href="#空结构体-Unit-Like-Struct" class="headerlink" title="空结构体(Unit-Like Struct)"></a>空结构体(Unit-Like Struct)</h3><p>当我们想在某个类型上只实现行为而不想在其中储存数据的时候,就可以使用空结构体</p><figure class="highlight rust"><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"><span class="keyword">struct</span> <span class="title class_">Empty</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">a</span> = Empty;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="结构体数据的所有权"><a href="#结构体数据的所有权" class="headerlink" title="结构体数据的所有权"></a>结构体数据的所有权</h2><h3 id="一般类型数据"><a href="#一般类型数据" class="headerlink" title="一般类型数据"></a>一般类型数据</h3><p>如果结构中的数据储存在堆内存上的数据或者基础类型数据的话,那么这个结构体实例就<code>拥有</code>其所有的数据。只要实例有效,其内部的数据就有效;反过来说,当其内部数据的所有权发生转移,实例也就失效</p><figure class="highlight rust"><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"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Team</span> {</span><br><span class="line"> leader: <span class="type">String</span>,</span><br><span class="line"> member: <span class="type">String</span>,</span><br><span class="line"> episode: <span class="type">u32</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">msm</span> = Team {</span><br><span class="line"> leader: <span class="string">"tom"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> member: <span class="string">"jerry"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> episode: <span class="number">201</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 注意:这里传递的是&String</span></span><br><span class="line"> <span class="title function_ invoke__">hello</span>(&msm.leader);</span><br><span class="line"> <span class="comment">// 如果传递的不是引用,下面这句代码会发生错误,实例因其内部数据的所有权转移而失效</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, msm);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">hello</span>(name: &<span class="type">String</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"hello, {}"</span>, &name);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="引用类型数据"><a href="#引用类型数据" class="headerlink" title="引用类型数据"></a>引用类型数据</h3><p>在结构体中也可以储存引用,但是需要使用到<code>生命周期标注</code>来保证在实例有效的范围内,其内部引用也是有效的</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Team</span><<span class="symbol">'a</span>> {</span><br><span class="line"> leader: &<span class="symbol">'a</span> <span class="type">str</span>,</span><br><span class="line"> member: &<span class="symbol">'a</span> <span class="type">str</span>,</span><br><span class="line"> episode: <span class="type">u32</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">msm</span> = Team {</span><br><span class="line"> leader: <span class="string">"tom"</span>,</span><br><span class="line"> member: <span class="string">"jerry"</span>,</span><br><span class="line"> episode: <span class="number">201</span>,</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>当我们想要获得解构体内部值的时候,除了直接使用<code>.</code>标记法访问结构体数据外,还可以通过<code>模式匹配</code>来解构结构体实现直接获取内部值,但需要注意匹配造成的所有权转移</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> length: <span class="type">String</span>,</span><br><span class="line"> width: <span class="type">String</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle {</span><br><span class="line"> length: <span class="number">50</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> width: <span class="number">30</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 这里的等号就相当于模式匹配</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">Rectangle</span> { length, width } = rec;</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"length: {}, width: {}"</span>, length, width);</span><br><span class="line"> <span class="comment">// 注意:这句代码会发生错误,因为所有权被转移</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, rec);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="结构体方法"><a href="#结构体方法" class="headerlink" title="结构体方法"></a>结构体方法</h2><p>Rust中方法的作用和面向对象语言(如Python)中的方法有些类似但又有些区别,都是将行为与类型相关联,都可以访问关联类型的所有数据和方法,从而使代码更具可读性和可维护性。这里虽然标题是结构体方法,但其他的一些数据类型,比如枚举(enum)等也可以采用类似语法来实现与其关联的方法</p><h3 id="定义方法"><a href="#定义方法" class="headerlink" title="定义方法"></a>定义方法</h3><div class="note orange icon-padding simple"><i class="note-icon fa-solid fa-code"></i><p>定义方法的语法要求</p><ol><li>使用<code>impl</code>关键词进行定义</li><li>方法的定义在struct(或enum、trait对象)的上下文进行 </li><li>方法的第一个参数为self,表示被方法调用的实例</li><li>一个类型可拥有多个<code>impl</code>块或多个方法</li></ol></div><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Person</span> {</span><br><span class="line"> name: <span class="type">String</span>,</span><br><span class="line"> age: <span class="type">u8</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="comment">// 注意:这里是借用了实例</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">introduce</span>(&<span class="keyword">self</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"My name is {}, {} years old!"</span>, <span class="keyword">self</span>.name, <span class="keyword">self</span>.age);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 定义第二个方法</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">hello</span>(&<span class="keyword">self</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"hello {}"</span>, <span class="keyword">self</span>.name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 再次使用impl</span></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">sleep</span>(&<span class="keyword">self</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{} is sleeping"</span>, <span class="keyword">self</span>.name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tom</span> = Person {</span><br><span class="line"> name: <span class="string">"tom"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> age: <span class="number">10</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 调用方法</span></span><br><span class="line"> tom.<span class="title function_ invoke__">introduce</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="结构体方法中实例的所有权"><a href="#结构体方法中实例的所有权" class="headerlink" title="结构体方法中实例的所有权"></a>结构体方法中实例的所有权</h3><p>在上面的例子中,我们其实是使用借用的模式获得实例中的数据,但是也可以获得实例所有权或者得到实例的可变引用,这取决于用户想要实现的行为</p><figure class="highlight rust"><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"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Person</span> {</span><br><span class="line"> name: <span class="type">String</span>,</span><br><span class="line"> age: <span class="type">u8</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="comment">// 注意:这里获得了实例的所有权</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">introduce</span>(<span class="keyword">self</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"My name is {}, {} years old!"</span>, <span class="keyword">self</span>.name, <span class="keyword">self</span>.age);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">tom</span> = Person {</span><br><span class="line"> name: <span class="string">"tom"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> age: <span class="number">10</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> tom.<span class="title function_ invoke__">introduce</span>();</span><br><span class="line"> <span class="comment">// 下面这句代码会出现错误,因为实例的所有权已经离开这个作用域</span></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, tom);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight rust"><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"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Person</span> {</span><br><span class="line"> name: <span class="type">String</span>,</span><br><span class="line"> age: <span class="type">u8</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="comment">// 注意:这里是获得了可变引用</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">friend</span>(&<span class="keyword">mut</span> <span class="keyword">self</span>) {</span><br><span class="line"> <span class="keyword">self</span>.name.<span class="title function_ invoke__">push_str</span>(<span class="string">" and jerry"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">tom</span> = Person {</span><br><span class="line"> name: <span class="string">"tom"</span>.<span class="title function_ invoke__">to_string</span>(),</span><br><span class="line"> age: <span class="number">10</span>,</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, tom);</span><br><span class="line"> tom.<span class="title function_ invoke__">friend</span>();</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, tom);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="关联函数"><a href="#关联函数" class="headerlink" title="关联函数"></a>关联函数</h3><p>如果学过Python知道,在类中有叫静态方法的特殊类型函数,其不以<code>self</code>作为函数的第一个参数。在Rust中也有类似的函数,即关联函数,关联函数通常被用于构造器,比如下面的例子</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> length: <span class="type">usize</span>,</span><br><span class="line"> width: <span class="type">usize</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="comment">// new函数即为关联函数,它的作用是返回一个Rectangle实例</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">new</span>(length: <span class="type">usize</span>, width: <span class="type">usize</span>) <span class="punctuation">-></span> Rectangle {</span><br><span class="line"> <span class="comment">// 这里使用了简化写法</span></span><br><span class="line"> Rectangle { length, width, }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">usize</span> {</span><br><span class="line"> <span class="keyword">self</span>.length * <span class="keyword">self</span>.width</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="comment">// 调用关联函数时使用"::"</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle::<span class="title function_ invoke__">new</span>(<span class="number">50</span>, <span class="number">30</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, rec.<span class="title function_ invoke__">area</span>());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在结构体方法中,<code>self</code>是一个指向当前对象的指针,类似于Python定义类时的<code>self</code>参数,表示当前对象。<code>Self</code>是一个特殊的类型,表示当前类型本身,比如上面的例子可以这样改写</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> length: <span class="type">usize</span>,</span><br><span class="line"> width: <span class="type">usize</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="comment">// 使用Self作为返回类型</span></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">new</span>(length: <span class="type">usize</span>, width: <span class="type">usize</span>) <span class="punctuation">-></span> <span class="keyword">Self</span> {</span><br><span class="line"> <span class="keyword">Self</span> { length, width, }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle::<span class="title function_ invoke__">new</span>(<span class="number">50</span>, <span class="number">30</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, rec);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="关联函数和方法的内部调用"><a href="#关联函数和方法的内部调用" class="headerlink" title="关联函数和方法的内部调用"></a>关联函数和方法的内部调用</h3><p>在impl块中方法调用其他方法和关联函数的模式是不一样的</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#[derive(Debug)]</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> length: <span class="type">usize</span>,</span><br><span class="line"> width: <span class="type">usize</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">new</span>(length: <span class="type">usize</span>, width: <span class="type">usize</span>) <span class="punctuation">-></span> <span class="keyword">Self</span> {</span><br><span class="line"> <span class="keyword">Self</span> { length, width, }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">usize</span> {</span><br><span class="line"> <span class="keyword">self</span>.length * <span class="keyword">self</span>.width</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area_compare</span>(&<span class="keyword">self</span>, length: <span class="type">usize</span>, width: <span class="type">usize</span>) <span class="punctuation">-></span> <span class="type">bool</span> {</span><br><span class="line"> <span class="comment">// 调用关联函数</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec_new</span> = <span class="keyword">Self</span>::<span class="title function_ invoke__">new</span>(length, width);</span><br><span class="line"> <span class="comment">// 调用方法</span></span><br><span class="line"> <span class="keyword">self</span>.<span class="title function_ invoke__">area</span>() > rec_new.<span class="title function_ invoke__">area</span>()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle::<span class="title function_ invoke__">new</span>(<span class="number">50</span>, <span class="number">30</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{:?}"</span>, rec);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">e</span> = rec.<span class="title function_ invoke__">area_compare</span>(<span class="number">40</span>, <span class="number">35</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, e);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果关联函数想要调用结构体方法,则需要传入结构体实例</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> length: <span class="type">usize</span>,</span><br><span class="line"> width: <span class="type">usize</span>,</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">impl</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">new</span>(length: <span class="type">usize</span>, width: <span class="type">usize</span>) <span class="punctuation">-></span> <span class="keyword">Self</span> {</span><br><span class="line"> <span class="keyword">Self</span> { length, width, }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">area</span>(&<span class="keyword">self</span>) <span class="punctuation">-></span> <span class="type">usize</span> {</span><br><span class="line"> <span class="keyword">self</span>.length * <span class="keyword">self</span>.width</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">fn</span> <span class="title function_">double_area</span>(rec: &Rectangle) <span class="punctuation">-></span> <span class="type">usize</span> {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">new_rec</span> = <span class="keyword">Self</span>::<span class="title function_ invoke__">new</span>(rec.length * <span class="number">2</span>, rec.width * <span class="number">2</span>);</span><br><span class="line"> new_rec.<span class="title function_ invoke__">area</span>()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">rec</span> = Rectangle::<span class="title function_ invoke__">new</span>(<span class="number">50</span>, <span class="number">30</span>);</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">double_area</span> = Rectangle::<span class="title function_ invoke__">double_area</span>(&rec);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"The double area of the rectangle is {} square pixels."</span>, double_area);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>Rust学习笔记:引用与借用</title>
<link href="/posts/u74pp23h.html"/>
<url>/posts/u74pp23h.html</url>
<content type="html"><![CDATA[<div class="note orange icon-padding flat"><i class="note-icon fa-brands fa-rust"></i><p>以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正</p></div><h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>在Rust的基于所有权的内存管理模式中,对于引用类型变量,其所有权会在传递过程中发生变化。比如下面的代码中,当我们将String类型的变量传入函数后,s的所有权发生变更,我们想在main函数中继续使用s就不行了</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="comment">// 函数get_ownership()获得了变量s的所有权</span></span><br><span class="line"> <span class="title function_ invoke__">get_ownership</span>(s);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">get_ownership</span>(s: <span class="type">String</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line"> <span class="comment">// get_ownership()函数执行完,s被释放</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是在实际开发过程中我们常常需要继续在main函数中使用这些变量,这时主要三种方法可以满足类似的需求</p><h3 id="深拷贝"><a href="#深拷贝" class="headerlink" title="深拷贝"></a>深拷贝</h3><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="comment">// 将原变量深拷贝后传递给调用函数</span></span><br><span class="line"> <span class="title function_ invoke__">get_ownership</span>(s.<span class="title function_ invoke__">clone</span>());</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">get_ownership</span>(sc: <span class="type">String</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, sc);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过clone(),确实可以解决后续在main函数中使用变量的问题,但在实际情况中,我们如果要对原来的变量进行处理而不是单纯的传递,采用clone()就不是很友好了</p><h3 id="归还所有权"><a href="#归还所有权" class="headerlink" title="归还所有权"></a>归还所有权</h3><p>归还所有权就是当调用函数结束的时候,将变量的所有权返回至原函数,见下面的例子</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s</span> = <span class="title function_ invoke__">give_back</span>(s);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">give_back</span>(s: <span class="type">String</span>) <span class="punctuation">-></span> <span class="type">String</span> {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line"> s <span class="comment">// 通过返回值,将s的所有权返回原函数</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>归还所有权很好理解,但这样写出的代码比较冗余,且在返回值的处理上会比较麻烦</p><h3 id="传递引用"><a href="#传递引用" class="headerlink" title="传递引用"></a>传递引用</h3><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="comment">// 通过传递引用,将s的所有权保留在main函数中</span></span><br><span class="line"> <span class="title function_ invoke__">give_reference</span>(&s);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">give_reference</span>(s: &<span class="type">String</span>) {</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过传递引用不会改变原变量的所有权,这样就可以避免后续处理所有权的麻烦</p><h2 id="引用-Reference"><a href="#引用-Reference" class="headerlink" title="引用(Reference)"></a>引用(Reference)</h2><p>我们先分析下以下例子中的变量s和&s在内存中的关系。通过示意图可以看出:在引用变量s时程序创建了一个指针&s,它指向了变量s中指向堆内存数据的指针,当我们将&s传递到外部函数时,s1获得&s的所有权,在s1(&s)被释放后,不会导致变量s也被释放</p><figure class="highlight rust"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">length</span> = <span class="title function_ invoke__">calc_len</span>(&s);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">calc_len</span>(s1: &<span class="type">String</span>) <span class="punctuation">-></span> <span class="type">usize</span> {</span><br><span class="line"> s1.<span class="title function_ invoke__">len</span>()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引用在内存上的相互关系</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> &s s </span></span><br><span class="line"><span class="comment"> +----+-----+ +--------+-----+ +-------+-----+</span></span><br><span class="line"><span class="comment"> |name|value| | name |value| | index |value|</span></span><br><span class="line"><span class="comment"> +----+-----+ +--------+-----+ +-------+-----+</span></span><br><span class="line"><span class="comment"> | ptr|------------->| ptr |------------->| 0 | h |</span></span><br><span class="line"><span class="comment"> +----+-----+ +--------+-----+ +-------+-----+</span></span><br><span class="line"><span class="comment"> | len | 5 | | 1 | e |</span></span><br><span class="line"><span class="comment"> +--------+-----+ +-------+-----+</span></span><br><span class="line"><span class="comment"> |capacity| 5 | | 2 | l |</span></span><br><span class="line"><span class="comment"> +--------+-----+ +-------+-----+</span></span><br><span class="line"><span class="comment"> | 3 | l |</span></span><br><span class="line"><span class="comment"> +-------+-----+</span></span><br><span class="line"><span class="comment"> | 4 | o |</span></span><br><span class="line"><span class="comment"> +-------+-----+</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><p>Rust中的<code>&</code>符号就表示引用,它允许<mark class="hl-label red">调用值而不取得其所有权</mark> </p><h2 id="借用-Borrow"><a href="#借用-Borrow" class="headerlink" title="借用(Borrow)"></a>借用(Borrow)</h2><p>在Rust中将创建一个引用的行为称为”借用”,即将引用作为函数参数的行为就是”借用”。如同现实生活中,我们向别人借物品,使用完毕后必须还回去一样,因为我们没有该物品的所有权</p><h2 id="可变引用"><a href="#可变引用" class="headerlink" title="可变引用"></a>可变引用</h2><p>采用类似<code>&String</code>写法时,默认情况下我们是没法改变引用值的,但在很多场景中又确有修改引用值的需求。在Rust中提供了可变引用实现此类功能,具体见下面的例子</p><figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="title function_ invoke__">add_str</span>(&<span class="keyword">mut</span> s);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">fn</span> <span class="title function_">add_str</span>(s: &<span class="keyword">mut</span> <span class="type">String</span>) {</span><br><span class="line"> <span class="comment">// 通过可变引用修改s中的数据</span></span><br><span class="line"> s.<span class="title function_ invoke__">push_str</span>(<span class="string">", world!"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="可变引用的两个限制"><a href="#可变引用的两个限制" class="headerlink" title="可变引用的两个限制"></a>可变引用的两个限制</h3><p>Rust虽然提供了可变引用的方法,但同时也做了一些限制以保证内存安全</p><h4 id="限制一"><a href="#限制一" class="headerlink" title="限制一"></a>限制一</h4><mark class="hl-label red">在特定作用域内,对某一块数据只能有一个可变引用,以避免产生数据竞争</mark> 。比如下面的代码在编译时就会报错<figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s1</span> = &<span class="keyword">mut</span> s;</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s2</span> = &<span class="keyword">mut</span> s;</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"s1: {} s2: {}"</span>, s1, s2);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><div class="note red icon-padding simple"><i class="note-icon fa-solid fa-book-skull"></i><p>可能发生数据竞争的三种行为:</p><ol><li>多个指针同时访问同一个数据</li><li>至少有一个指针用于写入数据</li><li>没有使用任何机制同步对数据的访问行为</li></ol></div><mark class="hl-label green">对于以上的限制,我们其实可以通过采用划分作用域的方法达到非同时创建多个可变引用的目的</mark> ,比如以下的例子<figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s1</span> = &<span class="keyword">mut</span> s;</span><br><span class="line"> s1.<span class="title function_ invoke__">push_str</span>(<span class="string">",world"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s2</span> = &<span class="keyword">mut</span> s;</span><br><span class="line"> s2.<span class="title function_ invoke__">push_str</span>(<span class="string">"!"</span>);</span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{}"</span>, s);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="限制二"><a href="#限制二" class="headerlink" title="限制二"></a>限制二</h4><mark class="hl-label red">对一个数据可以同时存在多个不可变引用,但不能同时存在可变引用与不可变引用</mark> ,因为这会导致不可变引用的作用失效<figure class="highlight rust"><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"><span class="keyword">fn</span> <span class="title function_">main</span>() {</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mut </span><span class="variable">s</span> = <span class="string">"hello"</span>.<span class="title function_ invoke__">to_string</span>();</span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s1</span> = &s;</span><br><span class="line"> <span class="comment">// 此处会发生错误,因为同时存在对变量s的不可变引用和可变引用</span></span><br><span class="line"> <span class="keyword">let</span> <span class="variable">s2</span> =&<span class="keyword">mut</span> s;</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">println!</span>(<span class="string">"{} {}"</span>, s1, s2)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Rust学习笔记 </tag>
</tags>
</entry>
<entry>
<title>对asyncio的一点理解</title>
<link href="/posts/56ecg2fs.html"/>
<url>/posts/56ecg2fs.html</url>
<content type="html"><![CDATA[<h2 id="Python异步编程的几个概念"><a href="#Python异步编程的几个概念" class="headerlink" title="Python异步编程的几个概念"></a>Python异步编程的几个概念</h2><h3 id="协程函数-Coroutine-Function"><a href="#协程函数-Coroutine-Function" class="headerlink" title="协程函数(Coroutine Function)"></a>协程函数(Coroutine Function)</h3><p>在Python中,使用<code>async def</code>定义的函数就是协程函数,下面这段代码中,我们定义了一个名为<code>asyncsleep</code>的协程函数</p><figure class="highlight python"><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"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">asyncsleep</span>(<span class="params">sec</span>):</span><br><span class="line"> <span class="keyword">await</span> asyncio.sleep(sec)</span><br></pre></td></tr></table></figure><h3 id="协程对象-Coroutine-Object"><a href="#协程对象-Coroutine-Object" class="headerlink" title="协程对象(Coroutine Object)"></a>协程对象(Coroutine Object)</h3><p>在Python中,当我们直接调用协程函数时,Python不会直接运行这个函数内的任何代码,而是返回一个协程对象,以下代码中的<code>crt</code>变量就是调用<code>asyncsleep(1)</code>后返回的协程对象</p><figure class="highlight python"><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"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">asyncsleep</span>(<span class="params">sec</span>):</span><br><span class="line"> <span class="keyword">await</span> asyncio.sleep(sec)</span><br><span class="line"></span><br><span class="line">crt = asyncsleep(<span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(crt)</span><br><span class="line"></span><br><span class="line">输出:<coroutine <span class="built_in">object</span> asyncsleep at <span class="number">0x7f5dcec10270</span>></span><br></pre></td></tr></table></figure><h3 id="Task对象"><a href="#Task对象" class="headerlink" title="Task对象"></a>Task对象</h3><p>在这里我们可以先看一下Task对象是怎么运行的</p><figure class="highlight python"><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"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">tsk</span>(<span class="params">num</span>):</span><br><span class="line"> <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">return</span> num</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> crt = tsk(<span class="number">10</span>)</span><br><span class="line"> <span class="built_in">print</span>(crt)</span><br><span class="line"></span><br><span class="line"> task = asyncio.create_task(crt)</span><br><span class="line"> <span class="built_in">print</span>(task)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">await</span> task</span><br><span class="line"> <span class="built_in">print</span>(task)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> asyncio.run(main())</span><br><span class="line"></span><br><span class="line">输出:</span><br><span class="line"><coroutine <span class="built_in">object</span> tsk at <span class="number">0x7f32908051c0</span>></span><br><span class="line"><Task pending name=<span class="string">'Task-2'</span> coro=<tsk() running at /workspace/PythonProject/main.py:<span class="number">3</span>>></span><br><span class="line"><Task finished name=<span class="string">'Task-2'</span> coro=<tsk() done, defined at /workspace/PythonProject/main.py:<span class="number">3</span>> result=<span class="number">10</span>></span><br></pre></td></tr></table></figure><p>我们可以看出,在main函数中,<code>crt</code>首先被包装为处于<code>pending</code>状态的Task对象,在经过<code>await</code>后,其状态变成了<code>finished</code>,同时返回了一个值为10的<code>result</code>。其实,Task对象就是被<code>asyncio.create_task()</code>封装、注册进事件循环中的协程对象,以便当事件循环获得线程控制权时,可以异步执行这个特殊对象</p><h3 id="事件循环-Event-Loop"><a href="#事件循环-Event-Loop" class="headerlink" title="事件循环(Event Loop)"></a>事件循环(Event Loop)</h3><p>事件循环是asyncio的核心,是asyncio中的低层级API。它的主要功能是运行异步任务和回调,执行网络 IO 操作,以及运行子进程,见下面的示意图<br><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P_J3O6Am--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://thepracticaldev.s3.amazonaws.com/i/wrtzmt2ty03ksew7ehvx.jpg" alt="事件循环示意图"><br>结合之前的内容我们可以知道,对于协程对象,我们是没法直接运行它的内部功能的,必须通过<code>await</code>运行。比如在下面这段代码中,我们使用<code>await</code>直接运行了两个协程对象中的功能,最终耗时3秒。在这里,<code>await</code>的作用可以理解为阻塞运行了<code>asyncsleep(1)</code>和<code>asyncsleep(2)</code>两个协程对象</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">asyncsleep</span>(<span class="params">sec</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f"sleep time: <span class="subst">{sec}</span>"</span>)</span><br><span class="line"> <span class="keyword">await</span> asyncio.sleep(sec)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> <span class="built_in">print</span>(time.strftime(<span class="string">"%X"</span>))</span><br><span class="line"> <span class="comment"># await运行的是协程对象</span></span><br><span class="line"> <span class="keyword">await</span> asyncsleep(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">await</span> asyncsleep(<span class="number">2</span>)</span><br><span class="line"> <span class="built_in">print</span>(time.strftime(<span class="string">"%X"</span>))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> asyncio.run(main())</span><br><span class="line"> </span><br><span class="line">输出:</span><br><span class="line">03:<span class="number">15</span>:<span class="number">38</span></span><br><span class="line">sleep time: <span class="number">1</span></span><br><span class="line">sleep time: <span class="number">2</span></span><br><span class="line">03:<span class="number">15</span>:<span class="number">41</span></span><br></pre></td></tr></table></figure><p>以上的代码显然不能满足我们的要求,如果我们想让程序异步等待,就必须将协程对象封装成Task对象,将其注册进事件循环中,再通过<code>await</code>运行。比如在下面这段代码的main()函数中,我们使用<code>create_task()</code>创建了task1、task2两个Task对象,然后使用<code>asyncio.run()</code>运行事件循环,可以发现,程序只消耗了2秒就完成了运行</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">asyncsleep</span>(<span class="params">sec</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f"sleep time: <span class="subst">{sec}</span>"</span>)</span><br><span class="line"> <span class="keyword">await</span> asyncio.sleep(sec)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> task1 = asyncio.create_task(asyncsleep(<span class="number">1</span>))</span><br><span class="line"> task2 = asyncio.create_task(asyncsleep(<span class="number">2</span>))</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Start Tasks"</span>)</span><br><span class="line"> <span class="built_in">print</span>(time.strftime(<span class="string">"%X"</span>))</span><br><span class="line"> <span class="comment"># await运行的是Task对象</span></span><br><span class="line"> <span class="keyword">await</span> task1</span><br><span class="line"> <span class="keyword">await</span> task2</span><br><span class="line"> <span class="built_in">print</span>(time.strftime(<span class="string">"%X"</span>))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> asyncio.run(main())</span><br><span class="line"> </span><br><span class="line">输出:</span><br><span class="line">Start Tasks</span><br><span class="line">03:<span class="number">11</span>:<span class="number">40</span></span><br><span class="line">sleep time: <span class="number">1</span></span><br><span class="line">sleep time: <span class="number">2</span></span><br><span class="line">03:<span class="number">11</span>:<span class="number">42</span></span><br></pre></td></tr></table></figure><p>其实事件循环本质上是一个无限循环,我们可以使用<code>asyncio.get_event_loop()</code>等方法获取或创建它。它主要工作就是当我们使用<code>await</code>关键字处理Task对象时执行Task内部的功能,当遇到Task内部的阻塞等待时,便将Task挂起,执行其他的处于<code>pending</code>状态的Task,同时它会在循环中检查挂起的Task是否处于<code>finished</code>状态了,如果是就收集返回的<code>result</code>。当所有await的Task都完成后,事件循环便终止,将线程控制权交出</p><h2 id="asyncio的应用场景"><a href="#asyncio的应用场景" class="headerlink" title="asyncio的应用场景"></a>asyncio的应用场景</h2><p>需要注意的是,使用asyncio的程序仍是单线程运行的,它只是利用了CPU在处理IO密集型任务时的等待时间,让CPU可以处理其他任务,所以当程序需要阻塞线程或处理计算密集型任务时,异步编程就没有优势了。比如下面的代码</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">blockingsleep</span>(<span class="params">sec</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f"blocking sleep <span class="subst">{sec}</span>"</span>)</span><br><span class="line"> time.sleep(sec)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> task1 = asyncio.create_task(blockingsleep(<span class="number">1</span>))</span><br><span class="line"> task2 = asyncio.create_task(blockingsleep(<span class="number">2</span>))</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">print</span>(time.strftime(<span class="string">"%X"</span>))</span><br><span class="line"> <span class="comment"># sleep会阻塞当前线程,task2必须等待task1完成后才能执行</span></span><br><span class="line"> <span class="keyword">await</span> task1</span><br><span class="line"> <span class="keyword">await</span> task2</span><br><span class="line"> <span class="built_in">print</span>(time.strftime(<span class="string">"%X"</span>))</span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> asyncio.run(main())</span><br><span class="line"></span><br><span class="line">输出:</span><br><span class="line">03:<span class="number">14</span>:02</span><br><span class="line">blocking sleep <span class="number">1</span></span><br><span class="line">blocking sleep <span class="number">2</span></span><br><span class="line">03:<span class="number">14</span>:05</span><br></pre></td></tr></table></figure><figure class="highlight python"><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"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">addup</span>(<span class="params">num</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f"addup: <span class="subst">{num}</span>"</span>)</span><br><span class="line"> res = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, num): </span><br><span class="line"> res = res + i</span><br><span class="line"> <span class="built_in">print</span>(res)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> task1 = asyncio.create_task(addup(<span class="number">100000001</span>))</span><br><span class="line"> task2 = asyncio.create_task(addup(<span class="number">100001</span>))</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 在运行task1时,CPU在处理计算密集型任务而不是处于等待状态,此时task2没法异步运行</span></span><br><span class="line"> <span class="keyword">await</span> task1</span><br><span class="line"> <span class="keyword">await</span> task2</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> asyncio.run(main())</span><br></pre></td></tr></table></figure><p>所以我们可以看出asyncio的应用场景主要是IO密集型任务,如网络读取、硬盘读取、数据库读取等</p><h2 id="asyncio并发限制"><a href="#asyncio并发限制" class="headerlink" title="asyncio并发限制"></a>asyncio并发限制</h2><p>有时我们需要对协程数量进行一定限制,比如在网络请求时,应该考虑站点的并发能力来设置请求规模,否则可能会被目标站点识别为网络攻击。在asyncio中提供了信号量对象<code>Semaphore()</code>来限制协程数量,具体使用可参考下面的代码</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">tsk</span>(<span class="params">sem, x</span>):</span><br><span class="line"> <span class="keyword">async</span> <span class="keyword">with</span> sem:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f"Start Coroutine: <span class="subst">{x}</span>"</span>)</span><br><span class="line"> <span class="keyword">await</span> asyncio.sleep(<span class="number">1</span>)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Coroutine Done"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> <span class="comment"># 设置协程数量为10</span></span><br><span class="line"> sem = asyncio.Semaphore(<span class="number">10</span>)</span><br><span class="line"> tasks = [tsk(sem, n) <span class="keyword">for</span> n <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">50</span>)]</span><br><span class="line"> <span class="keyword">await</span> asyncio.gather(*tasks)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> asyncio.run(main())</span><br></pre></td></tr></table></figure><p>运行以上代码可以发现,同时运行的协程对象数量会被限制在10个,这样就可以很方便地限制并发数量。但是在使用<code> asyncio.Semaphore()</code>也有一些点需要注意,具体见下面的内容</p><h3 id="不能使用async-with-…-as-…管理信号量对象"><a href="#不能使用async-with-…-as-…管理信号量对象" class="headerlink" title="不能使用async with … as …管理信号量对象"></a>不能使用async with … as …管理信号量对象</h3><p>在使用<code>asyncio.Semaphore()</code>时不能使用<code>async with ... as ...</code>上下文管理语法,比如</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> <span class="keyword">async</span> <span class="keyword">with</span> (sem:=asyncio.Semaphore(<span class="number">2</span>)) <span class="keyword">as</span> from_aenter:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f'<span class="subst">{sem}</span>'</span>)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f'<span class="subst">{from_aenter}</span>'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> asyncio.run(main())</span><br><span class="line"></span><br><span class="line">输出:</span><br><span class="line"><asyncio.locks.Semaphore <span class="built_in">object</span> at <span class="number">0x7f197fc47550</span> [unlocked, value:<span class="number">1</span>]></span><br><span class="line"><span class="literal">None</span></span><br></pre></td></tr></table></figure><p>这是因为使用<code>async with ... as ...</code>语法的对象需要实现<code>__aenter__</code>方法,而在asyncio中<a href="https://github.com/python/cpython/blob/main/Lib/asyncio/locks.py#L12">信号量对象</a>的<code>__aenter__</code>方法返回值为<code>None</code></p><h3 id="asyncio-Semaphore-的使用位置"><a href="#asyncio-Semaphore-的使用位置" class="headerlink" title="asyncio.Semaphore()的使用位置"></a><code>asyncio.Semaphore()</code>的使用位置</h3><p>在下面的代码中,我们设置了同时等待的协程对象最多为10个,预计需要时间为5秒。但是运行代码可以发现,完成所有任务只消耗了1秒</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> asyncio</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> sem = asyncio.Semaphore(<span class="number">10</span>) </span><br><span class="line"> <span class="keyword">async</span> <span class="keyword">with</span> sem:</span><br><span class="line"> tasks = [asyncio.sleep(<span class="number">1</span>) <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">50</span>)]</span><br><span class="line"> <span class="built_in">print</span>(time.strftime(<span class="string">"%X"</span>))</span><br><span class="line"> <span class="keyword">await</span> asyncio.gather(*tasks)</span><br><span class="line"> <span class="built_in">print</span>(time.strftime(<span class="string">"%X"</span>))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> asyncio.run(main())</span><br></pre></td></tr></table></figure><p><a href="https://docs.python.org/3/library/asyncio-sync.html#semaphore">查看文档</a>我们可以发现和下面演示代码一样的内容。即在使用<code>async with ...</code>时实际是<mark class="hl-label red">协程对象</mark> 执行<code>acquire()</code>向信号量对象申请使用闲置队列使计数器减值,协程对象内部功能完成后执行<code>release()</code>向信号量对象释放队列使计数器增值。所以,在使用<code>Semaphore()</code>时,应该在每一个需要执行的<mark class="hl-label orange">协程对象内部</mark> 使用,由协程对象向信号量对象<mark class="hl-label orange">主动请求闲置队列</mark> </p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="string">"""1"""</span></span><br><span class="line">sem = asyncio.Semaphore(<span class="number">10</span>)</span><br><span class="line"><span class="comment"># ... later</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">with</span> sem:</span><br><span class="line"> <span class="comment"># work with shared resource</span></span><br><span class="line"></span><br><span class="line">which <span class="keyword">is</span> equivalent to:</span><br><span class="line"></span><br><span class="line"><span class="string">"""2"""</span></span><br><span class="line">sem = asyncio.Semaphore(<span class="number">10</span>)</span><br><span class="line"><span class="comment"># ... later</span></span><br><span class="line"><span class="keyword">await</span> sem.acquire()</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># work with shared resource</span></span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line"> sem.release()</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Python </tag>
<tag> asyncio </tag>
<tag> 异步编程 </tag>
</tags>
</entry>
<entry>
<title>使用Python调用C语言动态链接库</title>
<link href="/posts/bd214e51.html"/>
<url>/posts/bd214e51.html</url>
<content type="html"><![CDATA[<div class="note icon-padding simple"><i class="note-icon fa-brands fa-python"></i><p>Python以其开发简单、快捷和良好的生态受到全世界众多开发者的喜爱,但Python也因其较慢的运行速度被很多人诟病。同时,C语言以其能直接控制底层资源、运行速度快而广泛被用于操作系统和嵌入式设备的相关开发中,但C语言的缺点就是开发效率较低。那是否可以将两者结合起来?答案是可以的。关于Python调用C代码有好几种方式,在这里只举例介绍我个人常用且较为简单的使用Python的<a href="https://docs.python.org/3/library/ctypes.html">ctypes</a>库调用C语言动态链接库的方法</p></div><h2 id="实例:使用Python调用C语言编写的累加函数"><a href="#实例:使用Python调用C语言编写的累加函数" class="headerlink" title="实例:使用Python调用C语言编写的累加函数"></a>实例:使用Python调用C语言编写的累加函数</h2><h3 id="C代码"><a href="#C代码" class="headerlink" title="C代码"></a>C代码</h3><figure class="highlight c"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// add.c(输入一个整数并返回1+2+3+...+num的值)</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">add</span><span class="params">(<span class="type">long</span> num)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">long</span> result = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">long</span> i=<span class="number">1</span>; i<=num; i++)</span><br><span class="line"> {</span><br><span class="line"> result += i;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><div class="note warning modern"><p>C代码需要通过编译生成动态链接库文件(Linux系统下为.so文件,Windows系统下为.dll文件),这里以Linux为例</p></div><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gcc -fPIC -O3 -shared add.c -o add.so</span><br></pre></td></tr></table></figure><h3 id="Python代码"><a href="#Python代码" class="headerlink" title="Python代码"></a>Python代码</h3><div class="note default modern"><p>使用Python调用C语言动态链接库</p></div><figure class="highlight python"><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"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> ctypes <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">num_add_up</span>(<span class="params">num: <span class="built_in">int</span></span>) -> <span class="literal">None</span>:</span><br><span class="line"></span><br><span class="line"> result = <span class="number">0</span></span><br><span class="line"> start = time.time()</span><br><span class="line"> lib = CDLL(<span class="string">'./add.so'</span>)</span><br><span class="line"> lib.add.argtypes = [c_long] <span class="comment"># 定义传入参数的类型</span></span><br><span class="line"> lib.add.restype = c_long <span class="comment"># 定义返回值的类型</span></span><br><span class="line"> result = lib.add(num)</span><br><span class="line"> <span class="built_in">print</span>(result)</span><br><span class="line"> <span class="built_in">print</span>(time.time()-start)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> num_add_up(<span class="number">100000000</span>)</span><br><span class="line"></span><br><span class="line">输出:</span><br><span class="line"><span class="number">5000000050000000</span></span><br><span class="line"><span class="number">0.03809380531311035</span></span><br></pre></td></tr></table></figure><div class="note default modern"><p>使用Python实现</p></div><figure class="highlight python"><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"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">num_add_up</span>(<span class="params">num: <span class="built_in">int</span></span>) -> <span class="literal">None</span>:</span><br><span class="line"></span><br><span class="line"> result = <span class="number">0</span></span><br><span class="line"> start = time.time()</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, num+<span class="number">1</span>):</span><br><span class="line"> result += i</span><br><span class="line"> <span class="built_in">print</span>(result)</span><br><span class="line"> <span class="built_in">print</span>(time.time()-start)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> num_add_up(<span class="number">100000000</span>)</span><br><span class="line"></span><br><span class="line">输出:</span><br><span class="line"><span class="number">5000000050000000</span></span><br><span class="line"><span class="number">5.233763933181763</span></span><br></pre></td></tr></table></figure><div class="note warning modern"><p>可以看出采用动态链接库的代码速度优势明显,可以通过这种方式对Python程序进行局部优化,提高程序的运行速度</p></div><h2 id="ctypes参数类型"><a href="#ctypes参数类型" class="headerlink" title="ctypes参数类型"></a>ctypes参数类型</h2><p>使用C语言动态链接库与Python的参数传递时,是无法直接传递Python中的变量的。需要ctypes进行相关的封装,ctypes在此定义了一些和C兼容的基本数据类型以供使用</p><table><thead><tr><th align="center">ctypes 类型</th><th align="center">C 类型</th><th align="center">Python 类型</th></tr></thead><tbody><tr><td align="center">c_bool</td><td align="center">_Bool</td><td align="center">bool (1)</td></tr><tr><td align="center">c_char</td><td align="center">char</td><td align="center">单字符字节串对象</td></tr><tr><td align="center">c_wchar</td><td align="center">wchar_t</td><td align="center">单字符字符串</td></tr><tr><td align="center">c_byte</td><td align="center">char</td><td align="center">int</td></tr><tr><td align="center">c_ubyte</td><td align="center">unsigned char</td><td align="center">int</td></tr><tr><td align="center">c_short</td><td align="center">short</td><td align="center">int</td></tr><tr><td align="center">c_ushort</td><td align="center">unsigned short</td><td align="center">int</td></tr><tr><td align="center">c_int</td><td align="center">int</td><td align="center">int</td></tr><tr><td align="center">c_uint</td><td align="center">unsigned int</td><td align="center">int</td></tr><tr><td align="center">c_long</td><td align="center">long</td><td align="center">int</td></tr><tr><td align="center">c_ulong</td><td align="center">unsigned long</td><td align="center">int</td></tr><tr><td align="center">c_longlong</td><td align="center">__int64 or long long</td><td align="center">int</td></tr><tr><td align="center">c_ulonglong</td><td align="center">unsigned __int64 or unsigned long long</td><td align="center">int</td></tr><tr><td align="center">c_size_t</td><td align="center">size_t</td><td align="center">int</td></tr><tr><td align="center">c_ssize_t</td><td align="center">ssize_t or Py_ssize_t</td><td align="center">int</td></tr><tr><td align="center">c_float</td><td align="center">float</td><td align="center">float</td></tr><tr><td align="center">c_double</td><td align="center">double</td><td align="center">float</td></tr><tr><td align="center">c_longdouble</td><td align="center">long double</td><td align="center">float</td></tr><tr><td align="center">c_char_p</td><td align="center">char* (NUL terminated)</td><td align="center">字节串对象或 None</td></tr><tr><td align="center">c_wchar_p</td><td align="center">wchar_t* (NUL terminated)</td><td align="center">字符串或 None</td></tr><tr><td align="center">c_void_p</td><td align="center">void*</td><td align="center">int 或 None</td></tr></tbody></table>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Python </tag>
<tag> C </tag>
</tags>
</entry>
<entry>
<title>根据BV号爬取哔哩哔哩视频弹幕</title>
<link href="/posts/e22de85.html"/>
<url>/posts/e22de85.html</url>
<content type="html"><![CDATA[<div class="note icon-padding modern"><i class="note-icon fa-brands fa-bilibili fa-bounce"></i><p>喜欢逛B站的同学都知道,B站的最有意思就是“弹幕”,视频的播放量和弹幕数量基本上是成正比的。分析一个视频最好的方法就是看弹幕的情况,今天闲得无聊写一个爬弹幕的程序玩玩,后面可以搭配一些其他库进行词频分析,我这就只写爬取弹幕的部分了…</p></div><h3 id="所需依赖库"><a href="#所需依赖库" class="headerlink" title="所需依赖库"></a>所需依赖库</h3><figure class="highlight python"><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"><span class="comment"># 请求库</span></span><br><span class="line">requests</span><br><span class="line"><span class="comment"># 解析库</span></span><br><span class="line">pyquery</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"># 获取视频cid</span><br><span class="line">https://api.bilibili.com/x/player/pagelist?bvid=BV号</span><br><span class="line"># 具体弹幕列表页</span><br><span class="line">https://comment.bilibili.com/cid号.xml</span><br></pre></td></tr></table></figure><h3 id="具体代码"><a href="#具体代码" class="headerlink" title="具体代码"></a>具体代码</h3><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python3</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 爬取哔哩哔哩BV号对应视频的弹幕</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> pyquery <span class="keyword">import</span> PyQuery <span class="keyword">as</span> pq</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_url_page</span>(<span class="params">url</span>):</span><br><span class="line"> header = {</span><br><span class="line"> <span class="string">"User-Agent"</span>: <span class="string">"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0"</span>,</span><br><span class="line"> <span class="string">"Cookie"</span>: <span class="string">""</span></span><br><span class="line"> }</span><br><span class="line"> response = requests.get(url, headers=header)</span><br><span class="line"> <span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line"> response.encoding = response.apparent_encoding</span><br><span class="line"> <span class="keyword">return</span> response</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_cid</span>(<span class="params">bv</span>):</span><br><span class="line"> cid_url = <span class="string">f"https://api.bilibili.com/x/player/pagelist?bvid=<span class="subst">{bv}</span>"</span></span><br><span class="line"> cid_page = get_url_page(cid_url).json()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> cid_page[<span class="string">"data"</span>][<span class="number">0</span>][<span class="string">"cid"</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>():</span><br><span class="line"> bv_str = <span class="built_in">input</span>(<span class="string">"input the BV:"</span>)</span><br><span class="line"> danmaku_url = <span class="string">f"https://comment.bilibili.com/<span class="subst">{get_cid(bv_str)}</span>.xml"</span></span><br><span class="line"> danmaku_page = get_url_page(danmaku_url)</span><br><span class="line"> doc = pq(<span class="built_in">bytes</span>(danmaku_page.text, encoding=<span class="string">"utf-8"</span>))</span><br><span class="line"> danmakus = doc(<span class="string">"d"</span>).items()</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">"danmaku_list.txt"</span>, <span class="string">"a"</span>, encoding=<span class="string">"utf-8"</span>) <span class="keyword">as</span> f:</span><br><span class="line"> <span class="keyword">for</span> danmaku <span class="keyword">in</span> danmakus:</span><br><span class="line"> f.write(<span class="string">f"<span class="subst">{danmaku.text()}</span>\n"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> main()</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Python </tag>
<tag> 爬虫 </tag>
<tag> bilibili </tag>
</tags>
</entry>
<entry>
<title>使用Python操作Docker</title>
<link href="/posts/56c3d658.html"/>
<url>/posts/56c3d658.html</url>
<content type="html"><![CDATA[<div class="note blue icon-padding flat"><i class="note-icon fa-brands fa-docker"></i><p>在使用Selenium进行自动化测试的时候,为了到达能够简单部署的目的,可以直接选用selenium/standalone-chrome的<a href="https://hub.docker.com/r/selenium/standalone-chrome">官方Docker镜像</a>来搭建服务,这样就可以不用考虑Webdriver和浏览器的版本适配问题。但是我每天需要进行自动化测试的时间只需要一小会儿,如果让selenium/standalone-chrome容器一直运行在后台,对我的1C2G的小服务器来说占用了太多资源。针对这个问题,Docker提供了<a href="https://docker-py.readthedocs.io/en/latest/index.html">SDK</a>可以让Python程序在需要的时候启用容器,测试完成后再停止容器来节约服务器资源</p></div><h3 id="docker库的基本配置"><a href="#docker库的基本配置" class="headerlink" title="docker库的基本配置"></a>docker库的基本配置</h3><figure class="highlight python"><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"><span class="comment"># docker库安装</span></span><br><span class="line">sudo pip install docker</span><br><span class="line"><span class="comment"># 使用CPyhon解释器运行</span></span><br><span class="line"><span class="keyword">import</span> docker</span><br><span class="line"><span class="comment"># 如果没有报错就说明安装完成</span></span><br></pre></td></tr></table></figure><h3 id="docker库的常见用法"><a href="#docker库的常见用法" class="headerlink" title="docker库的常见用法"></a>docker库的常见用法</h3><figure class="highlight python"><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"><span class="comment"># 实例化</span></span><br><span class="line">client = docker.from_env()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行一个容器</span></span><br><span class="line">client.containers.run(<span class="string">'镜像名称'</span>, detach=<span class="literal">True</span>, **kwargs) <span class="comment"># detach:后台运行 **kwargs:需要根据镜像所需的变量设置</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建一个容器但不运行</span></span><br><span class="line">client.containers.create(<span class="string">'镜像名称'</span>, detach=<span class="literal">True</span>, **kwargs) <span class="comment"># 用法同.run</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 停止容器</span></span><br><span class="line">container = client.containers.get(<span class="string">'容器名称/容器ID'</span>)</span><br><span class="line">container.stop()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启用容器</span></span><br><span class="line">container = client.containers.get(<span class="string">'容器名称/容器ID'</span>)</span><br><span class="line">container.start()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查询容器并遍历相关信息</span></span><br><span class="line">containers = client.containers.<span class="built_in">list</span>(**kwargs) <span class="comment"># 无参数:默认只显示正在运行的容器 all:显示所有容器</span></span><br><span class="line"><span class="keyword">for</span> container <span class="keyword">in</span> containers:</span><br><span class="line"><span class="built_in">print</span>(<span class="string">'容器ID:'</span>+container.short_id)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">'容器名称:'</span>+container.name)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">'容器状态:'</span>+container.status)</span><br></pre></td></tr></table></figure><div class="note red icon-padding simple"><i class="note-icon fa-solid fa-book-skull"></i><p>需要注意的是,因为Docker的运行需要root权限,所以当在Python程序代码中添加操作Docker的相关功能后,Python程序也需要使用root权限运行</p></div>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 备忘 </tag>
<tag> Python </tag>
<tag> Docker </tag>
</tags>
</entry>
<entry>
<title>Selenium及Webdriver环境安装</title>
<link href="/posts/9cf09888.html"/>
<url>/posts/9cf09888.html</url>
<content type="html"><![CDATA[<div class="note blue icon-padding simple"><i class="note-icon fa-brands fa-chrome fa-spin fa-spin-reverse"></i><p><a href="https://www.selenium.dev/selenium/docs/api/py/api.html">Selenium</a>是一个非常简单好用的WEB自动化工具,它可以模仿人类用户操作浏览器对网页进行自动化的测试,在实际使用中也能用于网络爬虫,以下内容是部署Python的Selenium基本测试环境教程</p></div><h3 id="Selenium环境配置"><a href="#Selenium环境配置" class="headerlink" title="Selenium环境配置"></a>Selenium环境配置</h3><figure class="highlight python"><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"><span class="comment"># Selenium安装</span></span><br><span class="line">pip install selenium</span><br><span class="line"><span class="comment"># 使用CPython解释器运行</span></span><br><span class="line"><span class="keyword">import</span> selenium</span><br><span class="line"><span class="comment"># 如果没有报错证明已经安装完成</span></span><br></pre></td></tr></table></figure><h3 id="浏览器和Webdriver安装配置"><a href="#浏览器和Webdriver安装配置" class="headerlink" title="浏览器和Webdriver安装配置"></a>浏览器和Webdriver安装配置</h3><p>Selenium本身不含浏览器和Webdriver,其主要原理是模仿用户对浏览器进行操作,而浏览器本身和浏览器配套的Webdriver由浏览器厂商提供和维护。所以使用Selenium需要在电脑上安装浏览器环境,这里以谷歌Chrome浏览器为例</p><ul><li>Chrome浏览器安装<figure class="highlight bash"><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"><span class="comment"># 更新软件包</span></span><br><span class="line">sudo apt update</span><br><span class="line"><span class="comment"># 下载Chorme浏览器</span></span><br><span class="line"><span class="comment"># Ubuntu</span></span><br><span class="line">wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb</span><br><span class="line"><span class="comment"># AlmaLinux</span></span><br><span class="line">wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm</span><br><span class="line"><span class="comment"># 安装浏览器(安装需root权限)</span></span><br><span class="line"><span class="comment"># Ubuntu</span></span><br><span class="line">sudo apt install ./google-chrome-stable_current_amd64.deb</span><br><span class="line"><span class="comment"># AlmaLinux</span></span><br><span class="line">sudo dnf install google-chrome-stable_current_x86_64.rpm</span><br><span class="line"><span class="comment"># 检查安装成功</span></span><br><span class="line">google-chrome --version</span><br><span class="line">输出:Google Chrome 104.0.5112.101</span><br></pre></td></tr></table></figure></li><li>Webdriver安装<br>在<code>https://registry.npmmirror.com/binary.html?path=chromedriver/</code>网站中选取与浏览器版本对应版本和系统的Webdriver,这里以Linux的104.0.5112.79版本的为例<figure class="highlight bash"><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"><span class="comment"># 下载Webdriver</span></span><br><span class="line">wget https://registry.npmmirror.com/-/binary/chromedriver/104.0.5112.79/chromedriver_linux64.zip</span><br><span class="line"><span class="comment"># 解压文件</span></span><br><span class="line">unzip chromedriver_linux64.zip</span><br><span class="line"><span class="comment"># 配置Webdriver</span></span><br><span class="line">sudo <span class="built_in">mv</span> -f chromedriver /usr/local/share/chromedriver</span><br><span class="line">sudo <span class="built_in">ln</span> -s /usr/local/share/chromedriver /usr/local/bin/chromedriver</span><br><span class="line">sudo <span class="built_in">ln</span> -s /usr/local/share/chromedriver /usr/bin/chromedriver</span><br></pre></td></tr></table></figure><div class="note info modern"><p>其他主流浏览器Webdriver<br>Firefox:<a href="https://github.com/mozilla/geckodriver/releases">https://github.com/mozilla/geckodriver/releases</a><br>Microsoft Edge:<a href="https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/">https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/</a></p></div></li></ul><h3 id="环境完成检查"><a href="#环境完成检查" class="headerlink" title="环境完成检查"></a>环境完成检查</h3><p>使用以下Python代码测试Selenium自动化测试环境是否部署完成</p><figure class="highlight python"><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"><span class="keyword">from</span> selenium <span class="keyword">import</span> webdriver</span><br><span class="line"></span><br><span class="line">driver = webdriver.Chrome()</span><br><span class="line">driver.get(<span class="string">'https://www.baidu.com/'</span>)</span><br><span class="line">driver.close()</span><br><span class="line"><span class="comment"># 如果正常的话,浏览器会自动打开百度网站然后浏览器关闭</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 环境配置 </tag>
<tag> 备忘 </tag>
<tag> Python </tag>
<tag> Selenium </tag>
</tags>
</entry>
<entry>
<title>将Vim配置为Rust的简易IDE</title>
<link href="/posts/fcf74e02.html"/>
<url>/posts/fcf74e02.html</url>
<content type="html"><![CDATA[<h2 id="所需Vim插件"><a href="#所需Vim插件" class="headerlink" title="所需Vim插件"></a>所需Vim插件</h2><figure class="highlight text"><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"># vim-plug</span><br><span class="line">库地址: https://github.com/junegunn/vim-plug</span><br><span class="line"></span><br><span class="line"># rust.vim</span><br><span class="line">库地址: https://github.com/rust-lang/rust.vim</span><br><span class="line"></span><br><span class="line"># coc.nvim</span><br><span class="line">库地址: https://github.com/neoclide/coc.nvim</span><br></pre></td></tr></table></figure><h2 id="安装教程"><a href="#安装教程" class="headerlink" title="安装教程"></a>安装教程</h2><div class="note info modern"><p>此处安装环境以AlmaLinux9为例</p></div><div class="note warning modern"><p>在安装这类工具时,应当注意插件所需Vim的版本,如果Vim的版本太低可能无法安装</p></div><h3 id="vim-plug安装"><a href="#vim-plug安装" class="headerlink" title="vim-plug安装"></a>vim-plug安装</h3><figure class="highlight bash"><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"><span class="comment"># 下载源代码</span></span><br><span class="line">curl -fLo ~/.vim/autoload/plug.vim --create-dirs \</span><br><span class="line"> https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim</span><br></pre></td></tr></table></figure><h3 id="添加rust-vim和coc-nvim的配置到-vimrc文件"><a href="#添加rust-vim和coc-nvim的配置到-vimrc文件" class="headerlink" title="添加rust.vim和coc.nvim的配置到~/.vimrc文件"></a>添加rust.vim和coc.nvim的配置到~/.vimrc文件</h3><p>最终编辑好的~/.vimrc文件应该是下面这样</p><figure class="highlight vim"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">" vim-plug setup</span></span><br><span class="line"><span class="keyword">call</span> plug#begin(<span class="string">'~/.vim/plugged'</span>)</span><br><span class="line"></span><br><span class="line">Plug <span class="string">'rust-lang/rust.vim'</span></span><br><span class="line">Plug <span class="string">'neoclide/coc.nvim'</span>, {<span class="string">'branch'</span>: <span class="string">'release'</span>}</span><br><span class="line"></span><br><span class="line"><span class="keyword">call</span> plug#end()</span><br><span class="line"></span><br><span class="line"><span class="comment">" rust.vim setup</span></span><br><span class="line"><span class="keyword">syntax</span> enable</span><br><span class="line"><span class="keyword">filetype</span> plugin <span class="built_in">indent</span> <span class="keyword">on</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">g:rustfmt_autosave</span> = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">" coc.nvim setup</span></span><br><span class="line"><span class="keyword">set</span> updatetime=<span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">inoremap</span> <span class="symbol"><silent></span><span class="symbol"><expr></span> <span class="symbol"><TAB></span></span><br><span class="line"> \ coc#pum#visible() ? coc#pum#next(<span class="number">1</span>) :</span><br><span class="line"> \ CheckBackspace() ? <span class="string">"\<Tab>"</span> :</span><br><span class="line"> \ coc#refresh()</span><br><span class="line"><span class="keyword">inoremap</span> <span class="symbol"><expr></span><span class="symbol"><S-TAB></span> coc#pum#visible() ? coc#pum#prev(<span class="number">1</span>) : <span class="string">"\<C-h>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">inoremap</span> <span class="symbol"><silent></span><span class="symbol"><expr></span> <span class="symbol"><CR></span> coc#pum#visible() ? coc#pum#confirm()</span><br><span class="line"> \: <span class="string">"\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function!</span> <span class="title">CheckBackspace</span><span class="params">()</span> abort</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">col</span> = <span class="keyword">col</span>(<span class="string">'.'</span>) - <span class="number">1</span></span><br><span class="line"> <span class="keyword">return</span> !col || <span class="built_in">getline</span>(<span class="string">'.'</span>)[<span class="keyword">col</span> - <span class="number">1</span>] =~# <span class="string">'\s'</span></span><br><span class="line"><span class="keyword">endfunction</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> [g <span class="symbol"><Plug></span>(coc-diagnostic-<span class="keyword">prev</span>)</span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> ]g <span class="symbol"><Plug></span>(coc-diagnostic-<span class="keyword">next</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> gd <span class="symbol"><Plug></span>(coc-definition)</span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> gy <span class="symbol"><Plug></span>(coc-<span class="built_in">type</span>-definition)</span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> gi <span class="symbol"><Plug></span>(coc-implementation)</span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> <span class="keyword">gr</span> <span class="symbol"><Plug></span>(coc-references)</span><br><span class="line"></span><br><span class="line"><span class="keyword">autocmd</span> CursorHold * <span class="keyword">silent</span> <span class="keyword">call</span> CocActionAsync(<span class="string">'highlight'</span>)</span><br></pre></td></tr></table></figure><h3 id="Vim插件安装"><a href="#Vim插件安装" class="headerlink" title="Vim插件安装"></a>Vim插件安装</h3><p>Vim控制台输入</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:PlugInstall</span><br></pre></td></tr></table></figure><h3 id="coc-rust-analyzer安装"><a href="#coc-rust-analyzer安装" class="headerlink" title="coc-rust-analyzer安装"></a>coc-rust-analyzer安装</h3><p>等待rust.vim和coc.nvim安装完成后,在Vim控制台输入</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:CocInstall coc-rust-analyzer</span><br></pre></td></tr></table></figure><h3 id="配置coc-settings-json文件"><a href="#配置coc-settings-json文件" class="headerlink" title="配置coc-settings.json文件"></a>配置coc-settings.json文件</h3><p>在Vim控制台输入命令来编辑coc-settings.json</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:CocConfig</span><br></pre></td></tr></table></figure><p>写入以下内容</p><figure class="highlight json"><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"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"suggest.noselect"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"suggest.enablePreselect"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><div class="note warning modern"><p><a href="https://github.com/neoclide/coc.nvim">coc.nvim</a>需要Node.js才能正常运行,所以在使用时应检查是否安装了Node.js且其版本也应满足要求。在AlmaLinux下使用<code>sudo dnf install nodejs</code>安装Node.js</p></div><h2 id="可能的问题"><a href="#可能的问题" class="headerlink" title="可能的问题"></a>可能的问题</h2><h3 id="下载失败"><a href="#下载失败" class="headerlink" title="下载失败"></a>下载失败</h3><p>因为众所周知的网络原因,在国内下载安装插件时可能会出现无法成功下载的情况,这时需要多次尝试下载或使用代理下载</p><h3 id="启动rust-analyzer"><a href="#启动rust-analyzer" class="headerlink" title="启动rust-analyzer"></a>启动rust-analyzer</h3><p>进入cargo项目时,coc.nvim会提示需下载rust-analyzer,根据提示选择即可。如果没有自动跳出安装提示,则可以使用<code>rustup component add rust-analyzer</code>手动安装</p><h2 id="vimrc文件配置"><a href="#vimrc文件配置" class="headerlink" title="~/.vimrc文件配置"></a>~/.vimrc文件配置</h2><p>顺便记录一下自己的~/.vimrc文件内容,以便今后重新配置的时候使用</p><figure class="highlight vim"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">" init setup</span></span><br><span class="line"><span class="keyword">set</span> <span class="keyword">nu</span></span><br><span class="line"><span class="keyword">set</span> <span class="keyword">ts</span>=<span class="number">4</span></span><br><span class="line"><span class="keyword">set</span> expandtab</span><br><span class="line"><span class="keyword">set</span> listchars=<span class="keyword">tab</span>:>-,trail:.</span><br><span class="line"><span class="keyword">set</span> <span class="keyword">list</span></span><br><span class="line"><span class="keyword">set</span> backspace=<span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">" vim-plug setup</span></span><br><span class="line"><span class="keyword">call</span> plug#begin(<span class="string">'~/.vim/plugged'</span>)</span><br><span class="line"></span><br><span class="line">Plug <span class="string">'rust-lang/rust.vim'</span></span><br><span class="line">Plug <span class="string">'neoclide/coc.nvim'</span>, {<span class="string">'branch'</span>: <span class="string">'release'</span>}</span><br><span class="line">Plug <span class="string">'preservim/nerdtree'</span></span><br><span class="line">Plug <span class="string">'vim-airline/vim-airline'</span></span><br><span class="line">Plug <span class="string">'morhetz/gruvbox'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">call</span> plug#end()</span><br><span class="line"></span><br><span class="line"><span class="comment">" rust.vim setup</span></span><br><span class="line"><span class="keyword">syntax</span> enable</span><br><span class="line"><span class="keyword">filetype</span> plugin <span class="built_in">indent</span> <span class="keyword">on</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">g:rustfmt_autosave</span> = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">" coc.nvim setup</span></span><br><span class="line"><span class="keyword">set</span> updatetime=<span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">inoremap</span> <span class="symbol"><silent></span><span class="symbol"><expr></span> <span class="symbol"><TAB></span></span><br><span class="line"> \ coc#pum#visible() ? coc#pum#next(<span class="number">1</span>) :</span><br><span class="line"> \ CheckBackspace() ? <span class="string">"\<Tab>"</span> :</span><br><span class="line"> \ coc#refresh()</span><br><span class="line"><span class="keyword">inoremap</span> <span class="symbol"><expr></span><span class="symbol"><S-TAB></span> coc#pum#visible() ? coc#pum#prev(<span class="number">1</span>) : <span class="string">"\<C-h>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">inoremap</span> <span class="symbol"><silent></span><span class="symbol"><expr></span> <span class="symbol"><CR></span> coc#pum#visible() ? coc#pum#confirm()</span><br><span class="line"> \: <span class="string">"\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function!</span> <span class="title">CheckBackspace</span><span class="params">()</span> abort</span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">col</span> = <span class="keyword">col</span>(<span class="string">'.'</span>) - <span class="number">1</span></span><br><span class="line"> <span class="keyword">return</span> !col || <span class="built_in">getline</span>(<span class="string">'.'</span>)[<span class="keyword">col</span> - <span class="number">1</span>] =~# <span class="string">'\s'</span></span><br><span class="line"><span class="keyword">endfunction</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> <span class="symbol"><C-q></span> <span class="symbol"><Plug></span>(coc-diagnostic-<span class="keyword">prev</span>)</span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> <span class="symbol"><C-a></span> <span class="symbol"><Plug></span>(coc-diagnostic-<span class="keyword">next</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> gd <span class="symbol"><Plug></span>(coc-definition)</span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> gy <span class="symbol"><Plug></span>(coc-<span class="built_in">type</span>-definition)</span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> gi <span class="symbol"><Plug></span>(coc-implementation)</span><br><span class="line"><span class="keyword">nmap</span> <span class="symbol"><silent></span> <span class="keyword">gr</span> <span class="symbol"><Plug></span>(coc-references)</span><br><span class="line"></span><br><span class="line"><span class="keyword">autocmd</span> CursorHold * <span class="keyword">silent</span> <span class="keyword">call</span> CocActionAsync(<span class="string">'highlight'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">" nerdtree setup</span></span><br><span class="line"><span class="keyword">map</span> <span class="symbol"><F3></span> :NERDTreeMirror<span class="symbol"><CR></span></span><br><span class="line"><span class="keyword">map</span> <span class="symbol"><F3></span> :NERDTreeToggle<span class="symbol"><CR></span></span><br><span class="line"></span><br><span class="line"><span class="comment">" airline setup</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">g:airline</span>#extensions#tabline#enabled = <span class="number">1</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">g:airline</span>#extensions#tabline#left_sep = <span class="string">' '</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">g:airline</span>#extensions#tabline#left_alt_sep = <span class="string">'|'</span></span><br><span class="line"><span class="keyword">let</span> <span class="variable">g:airline_powerline_fonts</span> = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">" theme setup</span></span><br><span class="line"><span class="keyword">set</span> background=dark</span><br><span class="line"><span class="keyword">colorscheme</span> gruvbox</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 环境配置 </tag>
<tag> 备忘 </tag>
<tag> Vim </tag>
</tags>
</entry>
<entry>
<title>Vim的编译安装及使用入门</title>
<link href="/posts/58ea93b5.html"/>
<url>/posts/58ea93b5.html</url>
<content type="html"><![CDATA[<div class="note icon-padding flat"><i class="note-icon fa-solid fa-laptop-code"></i><p>Vim文本编辑器,是由Vi发展演变过来的文本编辑器,因其具有使用简单、功能强大等特点,是Linux 众多发行版的默认文本编辑器。如果你喜欢,你甚至可以把它配置成一个IDE</p></div><h2 id="编译安装"><a href="#编译安装" class="headerlink" title="编译安装"></a>编译安装</h2><div class="note info modern"><p>此处安装环境以AlmaLinux9为例</p></div><figure class="highlight bash"><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"><span class="comment"># 编译环境安装</span></span><br><span class="line">sudo dnf install git gcc ncurses-devel -y</span><br><span class="line"><span class="comment"># 克隆源代码</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/vim/vim.git</span><br><span class="line"><span class="comment"># 编译安装(逐步执行)</span></span><br><span class="line"><span class="built_in">cd</span> vim/</span><br><span class="line">./configure</span><br><span class="line">make -j8</span><br><span class="line">sudo make install</span><br><span class="line"><span class="built_in">cd</span> src</span><br><span class="line">./vim</span><br><span class="line"><span class="comment"># 替换原有版本</span></span><br><span class="line">sudo dnf remove vim -y</span><br><span class="line">sudo <span class="built_in">rm</span> /usr/bin/vim</span><br><span class="line">sudo <span class="built_in">cp</span> ./vim /usr/bin/</span><br></pre></td></tr></table></figure><h2 id="简单使用入门"><a href="#简单使用入门" class="headerlink" title="简单使用入门"></a>简单使用入门</h2><h3 id="跳转操作"><a href="#跳转操作" class="headerlink" title="跳转操作"></a>跳转操作</h3><figure class="highlight vim"><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></pre></td><td class="code"><pre><span class="line">上:<span class="keyword">k</span> 下:<span class="keyword">j</span> 左:h 右:<span class="keyword">l</span></span><br><span class="line"></span><br><span class="line">滚动翻页:Ctrl+<span class="keyword">e</span> 向下翻页:Ctrl+<span class="keyword">f</span> 向上翻页:Ctrl+<span class="keyword">b</span> 向下半页:Ctrl+d 向上半页:Ctrl+<span class="keyword">u</span></span><br><span class="line"></span><br><span class="line">跳转至下一个单词或标点首位:<span class="keyword">w</span> 跳转至下一个单词或标点末位:<span class="keyword">e</span></span><br><span class="line">跳转至上一个单词或标点首位:<span class="keyword">b</span></span><br><span class="line">跳转至行首:<span class="number">0</span> 跳转至行尾:$ 跳转至本行第一个非空字符:^</span><br><span class="line">跳转至文首:gg 跳转至文尾:G</span><br><span class="line">跳转至n行:ngg/nG/:n</span><br><span class="line"></span><br><span class="line">寻找光标所在行的<span class="keyword">x</span>字符:fx 重复上一个<span class="keyword">f</span>指令:;</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><figure class="highlight vim"><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">删除光标所在行:dd 删除光标至行尾(不包括回车):d$ 删除光标所在单词:dw</span><br><span class="line">删除n行到<span class="keyword">xn</span>行::n,xnd</span><br><span class="line"></span><br><span class="line">复制光标所在行:yy 复制光标至行尾(不包括回车):<span class="keyword">y</span>$ 复制光标及以下n行:nyy</span><br><span class="line"></span><br><span class="line">粘贴剪切板内容部至光标后:<span class="keyword">p</span> 粘贴剪切板内容部至光标前:<span class="keyword">P</span></span><br><span class="line"></span><br><span class="line">撤销操作:<span class="keyword">u</span> 前进操作:Ctrl+r</span><br></pre></td></tr></table></figure><h3 id="插入模式"><a href="#插入模式" class="headerlink" title="插入模式"></a>插入模式</h3><figure class="highlight vim"><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">在当前光标处插入:i 在当前光标后插入:<span class="keyword">a</span></span><br><span class="line">在当前行首处插入:I 在当前行末处插入:A</span><br><span class="line">在当前行下下新增一行:<span class="keyword">o</span> 在当前行上新增一行:O</span><br></pre></td></tr></table></figure><h3 id="查找替换操作"><a href="#查找替换操作" class="headerlink" title="查找替换操作"></a>查找替换操作</h3><figure class="highlight vim"><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">向后搜索字符串:/字符串 向前搜索字符串:?字符串</span><br><span class="line">匹配下一个搜索字符串:n 匹配上一个搜索字符串:<span class="keyword">N</span></span><br></pre></td></tr></table></figure><h3 id="保存退出操作"><a href="#保存退出操作" class="headerlink" title="保存退出操作"></a>保存退出操作</h3><figure class="highlight vim"><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">保存::<span class="keyword">w</span></span><br><span class="line">退出::q 强制退出(放弃修改)::q!</span><br><span class="line">保存并退出::<span class="keyword">wq</span>/:<span class="keyword">x</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 备忘 </tag>
<tag> Vim </tag>
</tags>
</entry>
<entry>
<title>AlmaLinux初上手</title>
<link href="/posts/f793f9ae.html"/>
<url>/posts/f793f9ae.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><div class="note icon-padding disabled"><i class="note-icon fa-brands fa-centos"></i><p>随着CentOS转为RHEL的上游滚动发行版,原CentOS的生态位出现了空缺,而目前CentOS的替代产品主要有<a href="https://almalinux.org/">AlmaLinux</a>和<a href="https://rockylinux.org/">RockyLinux</a>。本人是Debian系的长期用户,对RedHat系不太熟悉,所以写这篇博客记录下AlmaLinux9的初始配置,以便后续查阅</p></div><h2 id="具体配置"><a href="#具体配置" class="headerlink" title="具体配置"></a>具体配置</h2><h3 id="安装EPEL软件源"><a href="#安装EPEL软件源" class="headerlink" title="安装EPEL软件源"></a>安装EPEL软件源</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo dnf install epel-release</span><br></pre></td></tr></table></figure><h3 id="Rust环境配置"><a href="#Rust环境配置" class="headerlink" title="Rust环境配置"></a>Rust环境配置</h3><figure class="highlight bash"><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"><span class="comment"># Rust安装脚本</span></span><br><span class="line">curl --proto <span class="string">'=https'</span> --tlsv1.2 -sSf https://sh.rustup.rs | sh</span><br></pre></td></tr></table></figure><h3 id="Docker配置"><a href="#Docker配置" class="headerlink" title="Docker配置"></a>Docker配置</h3><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 卸载podman</span></span><br><span class="line">sudo dnf remove podman buidah</span><br><span class="line"></span><br><span class="line"><span class="comment"># docker安装</span></span><br><span class="line">sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo</span><br><span class="line">sudo dnf install docker-ce docker-ce-cli containerd.io</span><br><span class="line"><span class="comment"># 设置开机启动</span></span><br><span class="line">sudo systemctl start docker.service</span><br><span class="line">sudo systemctl <span class="built_in">enable</span> docker.service</span><br><span class="line"><span class="comment"># 安装完成检测</span></span><br><span class="line">sudo systemctl status docker</span><br><span class="line"><span class="comment"># 显示active(running),则说明docker安装完成</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置docker国内镜像库</span></span><br><span class="line">依次操作:前往阿里云容器镜像服务网站[https://cr.console.aliyun.com] -> 登录阿里云 -> 选择镜像工具 -> 选择镜像加速器 -> 复制加速器地址</span><br><span class="line"><span class="comment"># 进入目录</span></span><br><span class="line"><span class="built_in">cd</span> /etc/docker/</span><br><span class="line">sudo vi daemon.json</span><br><span class="line"><span class="comment"># 粘贴以下代码</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"registry-mirrors"</span>: [<span class="string">"加速器地址"</span>]</span><br><span class="line">}</span><br><span class="line"><span class="comment"># 重启docker让镜像配置生效</span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line">sudo systemctl restart docker</span><br><span class="line"><span class="comment"># 查看镜像配置是否生效</span></span><br><span class="line">sudo docker info</span><br></pre></td></tr></table></figure><h3 id="防火墙相关操作"><a href="#防火墙相关操作" class="headerlink" title="防火墙相关操作"></a>防火墙相关操作</h3><figure class="highlight bash"><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"><span class="comment"># 查看状态</span></span><br><span class="line">sudo systemctl status firewalld</span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line">sudo systemctl start firewalld</span><br><span class="line"><span class="comment"># 关闭</span></span><br><span class="line">sudo systemctl stop firewalld</span><br><span class="line"><span class="comment"># 开机启用</span></span><br><span class="line">sudo systemctl <span class="built_in">enable</span> firewalld</span><br><span class="line"><span class="comment"># 开机禁用</span></span><br><span class="line">sudo systemctl <span class="built_in">disable</span> firewalld</span><br></pre></td></tr></table></figure><h3 id="创建、删除用户"><a href="#创建、删除用户" class="headerlink" title="创建、删除用户"></a>创建、删除用户</h3><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 新建用户</span></span><br><span class="line">sudo adduser 用户名</span><br><span class="line"><span class="comment"># 配置用户密码</span></span><br><span class="line">sudo passwd 用户名</span><br><span class="line"></span><br><span class="line"><span class="comment"># 赋予用户sudo权限</span></span><br><span class="line">sudo usermod -aG wheel 用户名</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除用户但保留用户文件夹</span></span><br><span class="line">sudo userdel 用户名</span><br><span class="line"><span class="comment"># 删除用户及用户文件夹</span></span><br><span class="line">sudo userdel -r 用户名</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Linux </tag>
<tag> 环境配置 </tag>
<tag> 备忘 </tag>
</tags>
</entry>
<entry>
<title>Ubuntu初始配置</title>
<link href="/posts/18d22756.html"/>
<url>/posts/18d22756.html</url>
<content type="html"><![CDATA[<h3 id="更换国内镜像源"><a href="#更换国内镜像源" class="headerlink" title="更换国内镜像源"></a>更换国内镜像源</h3><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入目录</span></span><br><span class="line"><span class="built_in">cd</span> /etc/apt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 备份原文件</span></span><br><span class="line">sudo <span class="built_in">cp</span> sources.list sources.list.bk</span><br><span class="line">sudo vi sources.list</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除原来的文本,将下面的代码复制进去(以Ubuntu20.04阿里源为例)</span></span><br><span class="line">deb https://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse</span><br><span class="line">deb-src https://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb https://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse</span><br><span class="line">deb-src https://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb https://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse</span><br><span class="line">deb-src https://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse</span><br><span class="line"></span><br><span class="line"><span class="comment"># deb https://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse</span></span><br><span class="line"><span class="comment"># deb-src https://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse</span></span><br><span class="line"></span><br><span class="line">deb https://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse</span><br><span class="line">deb-src https://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse</span><br></pre></td></tr></table></figure><div class="note warning modern"><p>软件源的选用需要根据系统的版本进行选择,否则可能会出现错误 </p></div><h3 id="Docker配置"><a href="#Docker配置" class="headerlink" title="Docker配置"></a>Docker配置</h3><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装docker</span></span><br><span class="line">sudo apt update</span><br><span class="line">curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun</span><br><span class="line"></span><br><span class="line"><span class="comment"># docker安装完成检测</span></span><br><span class="line">sudo systemctl status docker</span><br><span class="line"><span class="comment"># 显示active(running),则说明docker安装完成</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置docker国内镜像库</span></span><br><span class="line">依次操作:前往阿里云容器镜像服务网站[https://cr.console.aliyun.com] -> 登录阿里云 -> 选择镜像工具 -> 选择镜像加速器 -> 复制加速器地址</span><br><span class="line"><span class="comment"># 进入目录</span></span><br><span class="line"><span class="built_in">cd</span> /etc/docker/</span><br><span class="line">sudo nano daemon.json</span><br><span class="line"><span class="comment"># 粘贴以下代码</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"registry-mirrors"</span>:[<span class="string">"加速器地址"</span>]</span><br><span class="line">}</span><br><span class="line"><span class="comment"># 重启docker让镜像配置生效</span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line">sudo systemctl restart docker</span><br><span class="line"><span class="comment"># 查看镜像配置是否生效</span></span><br><span class="line">sudo docker info</span><br></pre></td></tr></table></figure><h3 id="卸载Snap"><a href="#卸载Snap" class="headerlink" title="卸载Snap"></a>卸载Snap</h3><figure class="highlight bash"><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">sudo apt update</span><br><span class="line">sudo apt autoremove --purge snapd</span><br></pre></td></tr></table></figure><h3 id="配置swappiness"><a href="#配置swappiness" class="headerlink" title="配置swappiness"></a>配置swappiness</h3><figure class="highlight bash"><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"><span class="comment"># 参看当前swappiness的值</span></span><br><span class="line"><span class="built_in">cat</span> /proc/sys/vm/swappiness</span><br><span class="line"></span><br><span class="line"><span class="comment"># 临时修改(重启后失效)</span></span><br><span class="line">sudo sysctl vm.swappiness=10</span><br><span class="line"></span><br><span class="line"><span class="comment"># 永久修改</span></span><br><span class="line">在/etc/sysctl.conf文件中添加vm.swappiness=10后重启系统</span><br></pre></td></tr></table></figure><h3 id="LVM扩容"><a href="#LVM扩容" class="headerlink" title="LVM扩容"></a>LVM扩容</h3><figure class="highlight bash"><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"><span class="comment"># 查看磁盘占用</span></span><br><span class="line"><span class="built_in">df</span> -h</span><br><span class="line"><span class="comment"># 查看物理实际磁盘空间</span></span><br><span class="line">lsblk</span><br><span class="line"><span class="comment"># 如果上面两个命令显示的磁盘大小明显不一致,说明系统没有挂载所有的磁盘空间</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看LVM与实际磁盘区别</span></span><br><span class="line">sudo lvdisplay</span><br><span class="line"><span class="comment"># 查看物理磁盘</span></span><br><span class="line">sudo vgdisplay</span><br><span class="line"></span><br><span class="line"><span class="comment"># 扩展LVM文件(/dev/mapper/ubuntu--vg-ubuntu--lv)</span></span><br><span class="line">sudo lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv</span><br><span class="line"><span class="comment"># 重新计算磁盘容量</span></span><br><span class="line">sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv</span><br></pre></td></tr></table></figure><h3 id="创建用户"><a href="#创建用户" class="headerlink" title="创建用户"></a>创建用户</h3><figure class="highlight bash"><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"><span class="comment"># 新建一个用户</span></span><br><span class="line">sudo adduser 用户名</span><br><span class="line"></span><br><span class="line"><span class="comment"># 赋予用户sudo权限</span></span><br><span class="line">sudo usermod -a -G sudo 用户名</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除用户但保留用户文件夹</span></span><br><span class="line">sudo userdel 用户名</span><br><span class="line"><span class="comment"># 删除用户及用户文件夹</span></span><br><span class="line">sudo userdel -r 用户名</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看系统中的用户</span></span><br><span class="line">sudo <span class="built_in">cat</span> /etc/passwd</span><br><span class="line"><span class="comment"># 查看用户密码</span></span><br><span class="line">sudo <span class="built_in">cat</span> /etc/shadow <span class="comment"># 用户密码是以加密的形式储存的,如:$6$Gj81xm...</span></span><br></pre></td></tr></table></figure><h3 id="设置时区"><a href="#设置时区" class="headerlink" title="设置时区"></a>设置时区</h3><figure class="highlight bash"><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"><span class="comment"># 查看本机时间</span></span><br><span class="line"><span class="built_in">date</span></span><br><span class="line"><span class="comment"># 查看时区目录</span></span><br><span class="line">timedatectl list-timezones</span><br><span class="line"><span class="comment"># 设置时区,如北京时间</span></span><br><span class="line">timedatectl set-timezone Asia/Shanghai</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> Linux </tag>
<tag> 环境配置 </tag>
<tag> 备忘 </tag>
</tags>
</entry>
<entry>
<title>Requests库常见用法</title>
<link href="/posts/5c9a9528.html"/>
<url>/posts/5c9a9528.html</url>
<content type="html"><![CDATA[<div class="note icon-padding modern"><i class="note-icon fa-solid fa-bug"></i><p>在使用Python的urllib库进行爬虫操作时非常繁琐,比如处理网页验证和Cookies时,需要编写Opener和Handler来处理。为了更加方便的实现这些操作,就有了更为强大的Requests库。<a href="https://requests.readthedocs.io/en/latest/">Requests库</a>是一个网页请求库,是基于urllib和urllib3封装的网络请求库,是目前公认的爬取网页最好的库,代码非常简洁,甚至一行代码就能爬取到网页</p></div><h2 id="Requests环境配置"><a href="#Requests环境配置" class="headerlink" title="Requests环境配置"></a>Requests环境配置</h2><figure class="highlight python"><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"><span class="comment"># requests库安装</span></span><br><span class="line">pip install requests</span><br><span class="line"><span class="comment"># 使用CPython解释器运行</span></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="comment"># 如果没有报错证明已经安装完成</span></span><br></pre></td></tr></table></figure><h2 id="Requests使用"><a href="#Requests使用" class="headerlink" title="Requests使用"></a>Requests使用</h2><h3 id="requests-get"><a href="#requests-get" class="headerlink" title="requests.get"></a>requests.get</h3><p>语法:<code>requests.get(url, params=None, **kwargs)</code></p><figure class="highlight python"><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">实例</span><br><span class="line">response = requests.get(<span class="string">'www.baidu.com'</span>)</span><br><span class="line"><span class="built_in">print</span>(response.status_code) </span><br><span class="line">输出:<span class="number">200</span></span><br></pre></td></tr></table></figure><table><thead><tr><th align="center">属性</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">.status_code</td><td align="center">HTTP请求返回的请求码</td></tr><tr><td align="center">.encoding</td><td align="center">从HTTP Header中推测出的网页编码</td></tr><tr><td align="center">.apparent_encoding</td><td align="center">从内容中分析出的网页编码</td></tr><tr><td align="center">.text</td><td align="center">HTTP响应内容的字符串形式,网页源代码</td></tr><tr><td align="center">.content</td><td align="center">HTTP响应内容的二进制形式</td></tr><tr><td align="center">.json()</td><td align="center">对响应的内容进行json解析</td></tr><tr><td align="center">.url</td><td align="center">get请求目标url</td></tr><tr><td align="center">.headers</td><td align="center">响应头中的相关信息</td></tr><tr><td align="center">.request.headers</td><td align="center">请求头中的相关信息</td></tr><tr><td align="center">.cookies</td><td align="center">请求头中的cookie</td></tr><tr><td align="center">.history</td><td align="center">获取所有重定向的记录</td></tr></tbody></table><ul><li><p>headers参数<br>一般使用中,为了避免目标网站发现爬虫程序,一般会在请求头中设置User-Agent来让爬虫程序伪装成浏览器</p><figure class="highlight python"><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">header = {</span><br><span class="line"> <span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0'</span></span><br><span class="line">}</span><br><span class="line">response = requests.get(<span class="string">'www.baidu.com'</span>, headers=header)</span><br><span class="line"><span class="built_in">print</span>(response.status_code)</span><br><span class="line">输出:<span class="number">200</span></span><br></pre></td></tr></table></figure></li><li><p>params参数<br>Requests在请求中允许使用params关键字参数,以一个字符串字典来提供这些参数。在发起请求的时候,程序会自动地将字典里的参数拼接到URL后</p><figure class="highlight python"><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">param = {<span class="string">'q'</span>: <span class="string">'python'</span>}</span><br><span class="line">response = requests.get(<span class="string">'https://search.gitee.com/'</span>, params=param)</span><br><span class="line"><span class="built_in">print</span>(response.url)</span><br><span class="line">输出:https://search.gitee.com/?q=python</span><br></pre></td></tr></table></figure></li><li><p>timeout参数<br>在爬虫中,如果没有timeout,代码可能会挂起很长时间,这个时候我们可以对请求设置timeout,让它必须在特定的时间内返回结果,否则就抛出异常</p><figure class="highlight python"><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"><span class="comment"># timeout可以传入浮点数将作为连接、读取的超时总时间</span></span><br><span class="line">response = requests.get(<span class="string">'https://www.baidu.com/'</span>, timeout=<span class="number">0.05</span>)</span><br><span class="line"><span class="built_in">print</span>(response.status_code)</span><br><span class="line">输出:requests.exceptions.ConnectTimeout</span><br></pre></td></tr></table></figure><figure class="highlight python"><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"><span class="comment"># timeout也可以传入元组将分别作为连接、读取的超时时间</span></span><br><span class="line">response = requests.get(<span class="string">'https://www.baidu.com/'</span>, timeout=(<span class="number">0.5</span>, <span class="number">0.01</span>))</span><br><span class="line"><span class="built_in">print</span>(response.status_code)</span><br><span class="line">输出:requests.exceptions.ReadTimeout</span><br></pre></td></tr></table></figure></li><li><p>proxies参数<br>有时,在短时间内同一ip多次访问目标URL的时候会触发网站的反爬机制,这时可以通过在发起请求时设置代理来进行规避</p><figure class="highlight python"><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">response = requests.get(<span class="string">'http://myip.ipip.net/'</span>)</span><br><span class="line"><span class="built_in">print</span>(response.text)</span><br><span class="line">输出:当前 IP:<span class="number">111.111</span><span class="number">.111</span><span class="number">.111</span> 来自于:日本 东京 KDDI</span><br></pre></td></tr></table></figure><figure class="highlight python"><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">proxy = {<span class="string">'http'</span>: <span class="string">'http://183.89.147.172:8080'</span>}</span><br><span class="line">response = requests.get(<span class="string">'http://myip.ipip.net/'</span>, proxies=proxy)</span><br><span class="line"><span class="built_in">print</span>(response.text)</span><br><span class="line">输出:当前 IP:<span class="number">183.89</span><span class="number">.147</span><span class="number">.172</span> 来自于:泰国 曼谷 3bb.co.th</span><br></pre></td></tr></table></figure></li><li><p>verify参数<br>Requests在发送网络请求的时候,默认会验证网站的CA证书。如果当前网站没有CA证书,那么就出现<code>SSLError</code>错误,我们可以用verify关键字参数,在请求的时候不验证网站的CA证书</p><div class="note warning disabled"><p>添加<code>verify=False</code>会出现<code>InsecureRequestWarning</code>警告,但是不会影响后续代码的执行</p></div><figure class="highlight python"><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">response = requests.get(<span class="string">'http://data.stats.gov.cn'</span>, verify=<span class="literal">False</span>)</span><br><span class="line"><span class="built_in">print</span>(response.status_code)</span><br><span class="line">输出:<span class="number">200</span></span><br></pre></td></tr></table></figure><div class="note icon-padding modern"><i class="note-icon fa-solid fa-gear fa-spin"></i><p>未完待续…</p></div></li></ul>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 备忘 </tag>
<tag> Python </tag>
<tag> 爬虫 </tag>
</tags>
</entry>
<entry>
<title>分类</title>
<link href="/categories/index.html"/>
<url>/categories/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>链接</title>
<link href="/link/index.html"/>
<url>/link/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>标签</title>
<link href="/tags/index.html"/>
<url>/tags/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
</search>