-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathch12-04-testing-the-librarys-functionality.html
645 lines (583 loc) · 62.4 KB
/
ch12-04-testing-the-librarys-functionality.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
<!DOCTYPE HTML>
<html lang="uk" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Розробка Функціонала Бібліотеки із Test-Driven Development - Мова програмування Rust</title>
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="title-page.html">Мова Програмування Rust</a></li><li class="chapter-item expanded affix "><a href="foreword.html">Передмова</a></li><li class="chapter-item expanded affix "><a href="ch00-00-introduction.html">Вступ</a></li><li class="chapter-item expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> Початок</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> Встановлення</a></li><li class="chapter-item expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="chapter-item expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Привіт, Cargo!</a></li></ol></li><li class="chapter-item expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> Програмування Гри Відгадайки</a></li><li class="chapter-item expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> Загальні Концепції Програмування</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> Змінні і Мутабельність</a></li><li class="chapter-item expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> Типи Даних</a></li><li class="chapter-item expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> Функції</a></li><li class="chapter-item expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> Коментарі</a></li><li class="chapter-item expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> Потік Виконання</a></li></ol></li><li class="chapter-item expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> Розуміння Володіння</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> Що Таке Володіння?</a></li><li class="chapter-item expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> Посилання та Позичання</a></li><li class="chapter-item expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> Слайси</a></li></ol></li><li class="chapter-item expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> Використання Структур для Групування Пов'язаних Даних</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> Визначення та Створення Екземпляра Структури</a></li><li class="chapter-item expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> Приклад Програми з Використанням Структур</a></li><li class="chapter-item expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> Синтаксис Методів</a></li></ol></li><li class="chapter-item expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> Енуми та Зіставлення зі Шаблоном</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> Визначення Енума</a></li><li class="chapter-item expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> Конструкція Потоку Виконання match</a></li><li class="chapter-item expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> Лаконічний Потік Виконання з if let</a></li></ol></li><li class="chapter-item expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> Керування Щораз Більшими Проєктами із Пакетами, Крейтами та Модулями</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> Пакети та Крейти</a></li><li class="chapter-item expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> Визначення Модулів для Контролю Області Видимості та Приватності</a></li><li class="chapter-item expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><strong aria-hidden="true">7.3.</strong> Шлях для Доступу до Елементів у Дереві Модулів</a></li><li class="chapter-item expanded "><a href="ch07-04-bringing-paths-into-scope-with-the-use-keyword.html"><strong aria-hidden="true">7.4.</strong> Введення Шляхів до Області Видимості з Ключовим Словом use</a></li><li class="chapter-item expanded "><a href="ch07-05-separating-modules-into-different-files.html"><strong aria-hidden="true">7.5.</strong> Розподіл Модулів на Різні Файли</a></li></ol></li><li class="chapter-item expanded "><a href="ch08-00-common-collections.html"><strong aria-hidden="true">8.</strong> Звичайні Колекції</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch08-01-vectors.html"><strong aria-hidden="true">8.1.</strong> Зберігання Списків Значень з Векторами</a></li><li class="chapter-item expanded "><a href="ch08-02-strings.html"><strong aria-hidden="true">8.2.</strong> Зберігання Тексту у Кодуванні UTF-8 в Стрічках</a></li><li class="chapter-item expanded "><a href="ch08-03-hash-maps.html"><strong aria-hidden="true">8.3.</strong> Зберігання Ключів з Асоційованими Значеннями у Хеш-Мапах</a></li></ol></li><li class="chapter-item expanded "><a href="ch09-00-error-handling.html"><strong aria-hidden="true">9.</strong> Обробка Помилок</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch09-01-unrecoverable-errors-with-panic.html"><strong aria-hidden="true">9.1.</strong> Невідновлювані Помилки з panic!</a></li><li class="chapter-item expanded "><a href="ch09-02-recoverable-errors-with-result.html"><strong aria-hidden="true">9.2.</strong> Відновлювані Помилки з Result</a></li><li class="chapter-item expanded "><a href="ch09-03-to-panic-or-not-to-panic.html"><strong aria-hidden="true">9.3.</strong> panic! чи не panic!</a></li></ol></li><li class="chapter-item expanded "><a href="ch10-00-generics.html"><strong aria-hidden="true">10.</strong> Узагальнені Типи, Трейти та Часи Існування</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch10-01-syntax.html"><strong aria-hidden="true">10.1.</strong> Узагальнені Типи Даних</a></li><li class="chapter-item expanded "><a href="ch10-02-traits.html"><strong aria-hidden="true">10.2.</strong> Трейти: Визначення Спільної Поведінки</a></li><li class="chapter-item expanded "><a href="ch10-03-lifetime-syntax.html"><strong aria-hidden="true">10.3.</strong> Перевірка Коректності Посилань із Часами Існування</a></li></ol></li><li class="chapter-item expanded "><a href="ch11-00-testing.html"><strong aria-hidden="true">11.</strong> Написання Автоматизованих Тестів</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch11-01-writing-tests.html"><strong aria-hidden="true">11.1.</strong> Як Писати Тести</a></li><li class="chapter-item expanded "><a href="ch11-02-running-tests.html"><strong aria-hidden="true">11.2.</strong> Керування Запуском Тестів</a></li><li class="chapter-item expanded "><a href="ch11-03-test-organization.html"><strong aria-hidden="true">11.3.</strong> Організація Тестів</a></li></ol></li><li class="chapter-item expanded "><a href="ch12-00-an-io-project.html"><strong aria-hidden="true">12.</strong> Проєкт з Вводом/Виводом: Створення Програми з Інтерфейсом Командного Рядка</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch12-01-accepting-command-line-arguments.html"><strong aria-hidden="true">12.1.</strong> Приймання Аргументів Командного Рядка</a></li><li class="chapter-item expanded "><a href="ch12-02-reading-a-file.html"><strong aria-hidden="true">12.2.</strong> Читання Файлу</a></li><li class="chapter-item expanded "><a href="ch12-03-improving-error-handling-and-modularity.html"><strong aria-hidden="true">12.3.</strong> Рефакторинг для Покращення Модульності та Обробки Помилок</a></li><li class="chapter-item expanded "><a href="ch12-04-testing-the-librarys-functionality.html" class="active"><strong aria-hidden="true">12.4.</strong> Розробка Функціонала Бібліотеки із Test-Driven Development</a></li><li class="chapter-item expanded "><a href="ch12-05-working-with-environment-variables.html"><strong aria-hidden="true">12.5.</strong> Робота зі Змінними Середовища</a></li><li class="chapter-item expanded "><a href="ch12-06-writing-to-stderr-instead-of-stdout.html"><strong aria-hidden="true">12.6.</strong> Написання Повідомлень про Помилки у Помилковий Вивід замість Стандартного Виводу</a></li></ol></li><li class="chapter-item expanded "><a href="ch13-00-functional-features.html"><strong aria-hidden="true">13.</strong> Функціональні Можливості Мови: Ітератори та Замикання</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch13-01-closures.html"><strong aria-hidden="true">13.1.</strong> Замикання: Анонімні Функції, що Захоплюють Своє Середовище</a></li><li class="chapter-item expanded "><a href="ch13-02-iterators.html"><strong aria-hidden="true">13.2.</strong> Обробка Послідовностей Елементів з Ітераторами</a></li><li class="chapter-item expanded "><a href="ch13-03-improving-our-io-project.html"><strong aria-hidden="true">13.3.</strong> Покращення Нашого Проєкту з Вводом/Виводом</a></li><li class="chapter-item expanded "><a href="ch13-04-performance.html"><strong aria-hidden="true">13.4.</strong> Порівняння Швидкодії: Цикли Проти Ітераторів</a></li></ol></li><li class="chapter-item expanded "><a href="ch14-00-more-about-cargo.html"><strong aria-hidden="true">14.</strong> Більше про Cargo та Crates.io</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch14-01-release-profiles.html"><strong aria-hidden="true">14.1.</strong> Налаштування Збірок з Release Профілями</a></li><li class="chapter-item expanded "><a href="ch14-02-publishing-to-crates-io.html"><strong aria-hidden="true">14.2.</strong> Публікація Крейта на Crates.io</a></li><li class="chapter-item expanded "><a href="ch14-03-cargo-workspaces.html"><strong aria-hidden="true">14.3.</strong> Робочі Області Cargo</a></li><li class="chapter-item expanded "><a href="ch14-04-installing-binaries.html"><strong aria-hidden="true">14.4.</strong> Встановлення Двійкових Файлів з cargo install</a></li><li class="chapter-item expanded "><a href="ch14-05-extending-cargo.html"><strong aria-hidden="true">14.5.</strong> Розширення Cargo із Користувацькими Командами</a></li></ol></li><li class="chapter-item expanded "><a href="ch15-00-smart-pointers.html"><strong aria-hidden="true">15.</strong> Розумні Вказівники</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch15-01-box.html"><strong aria-hidden="true">15.1.</strong> Використання Box<T> для Вказування на Дані в Купі</a></li><li class="chapter-item expanded "><a href="ch15-02-deref.html"><strong aria-hidden="true">15.2.</strong> Ставлення до Розумних Вказівників як до Звичайних Посилань з Трейтом Deref</a></li><li class="chapter-item expanded "><a href="ch15-03-drop.html"><strong aria-hidden="true">15.3.</strong> Виконання Коду при Очищенні з Трейтом Drop</a></li><li class="chapter-item expanded "><a href="ch15-04-rc.html"><strong aria-hidden="true">15.4.</strong> Rc<T> - Розумний Вказівник з Лічильником Посилань</a></li><li class="chapter-item expanded "><a href="ch15-05-interior-mutability.html"><strong aria-hidden="true">15.5.</strong> RefCell<T> та Шаблон Внутрішньої Мутабельності</a></li><li class="chapter-item expanded "><a href="ch15-06-reference-cycles.html"><strong aria-hidden="true">15.6.</strong> Цикли Посилань Можуть Спричинити Витік Пам'яті</a></li></ol></li><li class="chapter-item expanded "><a href="ch16-00-concurrency.html"><strong aria-hidden="true">16.</strong> Безстрашна Конкурентність</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch16-01-threads.html"><strong aria-hidden="true">16.1.</strong> Використання Потоків для Одночасного Виконання Коду</a></li><li class="chapter-item expanded "><a href="ch16-02-message-passing.html"><strong aria-hidden="true">16.2.</strong> Застосування Обміну Повідомлень для Передавання Даних між Потоками</a></li><li class="chapter-item expanded "><a href="ch16-03-shared-state.html"><strong aria-hidden="true">16.3.</strong> Конкурентність зі Спільним Станом</a></li><li class="chapter-item expanded "><a href="ch16-04-extensible-concurrency-sync-and-send.html"><strong aria-hidden="true">16.4.</strong> Розширювана Конкурентність із Трейтами Sync та Send</a></li></ol></li><li class="chapter-item expanded "><a href="ch17-00-oop.html"><strong aria-hidden="true">17.</strong> Особливості Об'єктоорієнтованого Програмування в Rust</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch17-01-what-is-oo.html"><strong aria-hidden="true">17.1.</strong> Характеристики Об'єктоорієнтованих Мов</a></li><li class="chapter-item expanded "><a href="ch17-02-trait-objects.html"><strong aria-hidden="true">17.2.</strong> Використання Трейт-Об'єктів, які Допускають Значення Різних Типів</a></li><li class="chapter-item expanded "><a href="ch17-03-oo-design-patterns.html"><strong aria-hidden="true">17.3.</strong> Реалізація Об'єктоорієнтованого Шаблону Проєктування</a></li></ol></li><li class="chapter-item expanded "><a href="ch18-00-patterns.html"><strong aria-hidden="true">18.</strong> Шаблони та Зіставлення Шаблонів</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch18-01-all-the-places-for-patterns.html"><strong aria-hidden="true">18.1.</strong> Усі Місця Можливого Використання Шаблонів</a></li><li class="chapter-item expanded "><a href="ch18-02-refutability.html"><strong aria-hidden="true">18.2.</strong> Спростовуваність: Чи Може Шаблон Бути Невідповідним</a></li><li class="chapter-item expanded "><a href="ch18-03-pattern-syntax.html"><strong aria-hidden="true">18.3.</strong> Синтаксис Шаблонів</a></li></ol></li><li class="chapter-item expanded "><a href="ch19-00-advanced-features.html"><strong aria-hidden="true">19.</strong> Просунуті Можливості</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch19-01-unsafe-rust.html"><strong aria-hidden="true">19.1.</strong> Небезпечний Rust</a></li><li class="chapter-item expanded "><a href="ch19-03-advanced-traits.html"><strong aria-hidden="true">19.2.</strong> Поглиблено про Трейти</a></li><li class="chapter-item expanded "><a href="ch19-04-advanced-types.html"><strong aria-hidden="true">19.3.</strong> Поглиблено про Типи</a></li><li class="chapter-item expanded "><a href="ch19-05-advanced-functions-and-closures.html"><strong aria-hidden="true">19.4.</strong> Поглиблено про Функції та Замикання</a></li><li class="chapter-item expanded "><a href="ch19-06-macros.html"><strong aria-hidden="true">19.5.</strong> Макроси</a></li></ol></li><li class="chapter-item expanded "><a href="ch20-00-final-project-a-web-server.html"><strong aria-hidden="true">20.</strong> Останній Проєкт: Збірка Багатопотокового Вебсервера</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch20-01-single-threaded.html"><strong aria-hidden="true">20.1.</strong> Збірка Однопотокового Вебсервера</a></li><li class="chapter-item expanded "><a href="ch20-02-multithreaded.html"><strong aria-hidden="true">20.2.</strong> Перетворюємо Наш Однопотоковий Сервер на Багатопотоковий</a></li><li class="chapter-item expanded "><a href="ch20-03-graceful-shutdown-and-cleanup.html"><strong aria-hidden="true">20.3.</strong> Плавне Вимкнення та Очищення</a></li></ol></li><li class="chapter-item expanded "><a href="appendix-00.html"><strong aria-hidden="true">21.</strong> Додатки</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="appendix-01-keywords.html"><strong aria-hidden="true">21.1.</strong> A - Ключові Слова</a></li><li class="chapter-item expanded "><a href="appendix-02-operators.html"><strong aria-hidden="true">21.2.</strong> B - Оператори та Символи</a></li><li class="chapter-item expanded "><a href="appendix-03-derivable-traits.html"><strong aria-hidden="true">21.3.</strong> C - Похідні Трейти</a></li><li class="chapter-item expanded "><a href="appendix-04-useful-development-tools.html"><strong aria-hidden="true">21.4.</strong> D - Корисні Інструменти Розробки</a></li><li class="chapter-item expanded "><a href="appendix-05-editions.html"><strong aria-hidden="true">21.5.</strong> E - Видання</a></li><li class="chapter-item expanded "><a href="appendix-06-translation.html"><strong aria-hidden="true">21.6.</strong> F - Переклади Книги</a></li><li class="chapter-item expanded "><a href="appendix-07-nightly-rust.html"><strong aria-hidden="true">21.7.</strong> G - як Розробляється Rust і "Нічний Rust"</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Мова програмування Rust</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h2 id="Розробка-Функціонала-Бібліотеки-із-test-driven-development"><a class="header" href="#Розробка-Функціонала-Бібліотеки-із-test-driven-development">Розробка Функціонала Бібліотеки із Test-Driven Development</a></h2>
<p>Тепер, коли ми перенесли логіку в <em>src/lib.rs</em> та залишили збір аргументів та обробку помилок в <em>src/main.rs</em>, стало набагато простіше писати тести для основного функціонала нашого коду. Ми можемо викликати функції напряму із різноманітними аргументами та перевіряти повернуті значення без потреби виклику нашого двійкового файлу із командного рядка.</p>
<p>У цій секції ми додамо пошукову логіку до програми <code>minigrep</code>, використовуючи стиль розробки через тестування (TDD) із наступними кроками:</p>
<ol>
<li>Напишіть тест, який дає збій і запустить його, щоб переконатися, що він це робить через очікувану причину.</li>
<li>Напишіть або змініть мінімум коду, щоб новий тест пройшов.</li>
<li>Відрефакторіть щойно доданий або змінений код та впевніться, що тести продовжують проходити.</li>
<li>Повторіть з першого кроку!</li>
</ol>
<p>Хоча це лише один з багатьох способів написання програмного забезпечення, TDD може допомагати надавати потрібного напрямку оформленню коду. Створення тесту перед тим, як написати код, який забезпечить проходження тесту, допомагає підтримувати високий рівень покриття тестуванням протягом всього процесу розробки.</p>
<p>Ми протестуємо імплементацію функціоналу який буде робити пошуковий запит стрічки у вмісті файлу та створювати список рядків, які відповідають запиту. Ми додамо цей функціонал у функцію під назвою <code>search</code>.</p>
<h3 id="Написання-Провального-Тесту"><a class="header" href="#Написання-Провального-Тесту">Написання Провального Тесту</a></h3>
<p>Видалімо інструкції <code>println!</code> які ми використовували для перевірки поведінки програми з <em>src/lib.rs</em> та <em>src/main.rs</em>, бо нам вони більше не потрібні. Потім додамо в <em>src/lib.rs</em> модуль <code>tests</code> із тестовою функцією, як ми зробили в <a href="ch11-01-writing-tests.html#the-anatomy-of-a-test-function">Розділі 11</a><!-- ignore -->. Тестова функція визначає бажану поведінку функції <code>search</code>: вона отримає запит та текст для пошуку, і вона буде повертати лише рядки з тексту, які містять запит. Блок коду 12-15 показує цей тест, який ще не компілюється.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Config {
</span><span class="boring"> pub fn build(args: &[String]) -> Result<Config, &'static str> {
</span><span class="boring"> if args.len() < 3 {
</span><span class="boring"> return Err("not enough arguments");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let query = args[1].clone();
</span><span class="boring"> let file_path = args[2].clone();
</span><span class="boring">
</span><span class="boring"> Ok(Config { query, file_path })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span>#[cfg(test)]
mod tests {
use super::*;
#[test]
fn one_result() {
let query = "duct";
let contents = "\
Rust:
safe, fast, productive.
Pick three.";
assert_eq!(vec!["safe, fast, productive."], search(query, contents));
}
}
</code></pre>
<p><span class="caption">Блок коду 12-15: Створення невдалого тесту для функції <code>search</code>, яку ми хотіли б мати</span></p>
<p>Цей тест шукає рядок <code>"duct"</code>. Текст, в якому ми робимо пошук, це три рядки, лише один з яких містить <code>"duct"</code> (Зауважте, що зворотний слеш після першої подвійної лапки каже Rust не розміщувати символ нового рядку на початку цієї стрічки). Ми стверджуємо, що значення, повернене з функції <code>search</code> містить тільки рядки, які ми очікуємо.</p>
<p>Ми ще не готові запустити цей тест та подивитися, як він дає збій, бо тест навіть не компілюється: функція <code>search</code> ще не існує! Згідно з принципами TDD, ми додамо лише мінімум коду, щоб тест почав компілюватися та виконуватися, додав визначення функції <code>search</code>, яке завжди повертає порожній вектор, як показано в Блоці коду 12-16. Тоді тест повинен скомпілюватися та провалитися, бо порожній вектор не зіставляється з вектором, який містить рядок <code>"safe, fast, productive."</code></p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust noplayground"><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Config {
</span><span class="boring"> pub fn build(args: &[String]) -> Result<Config, &'static str> {
</span><span class="boring"> if args.len() < 3 {
</span><span class="boring"> return Err("not enough arguments");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let query = args[1].clone();
</span><span class="boring"> let file_path = args[2].clone();
</span><span class="boring">
</span><span class="boring"> Ok(Config { query, file_path })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span>pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
vec![]
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn one_result() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p><span class="caption">Блок коду 12-16: Визначення функції <code>search</code>, якого досить для проходження тесту</span></p>
<p>Зауважте, що нам потрібно явно визначити час існування <code>'a</code> в сигнатурі <code>search</code> та використати цей час існування з аргументом <code>contents</code> та поверненим значенням. Згадаємо <a href="ch10-03-lifetime-syntax.html">Розділ 10</a><!-- ignore --> де час існування параметрів вказував, який час існування аргументу пов'язаний з поверненим значенням. У цьому випадку, ми вказуємо, що повернутий вектор має містити слайси стрічки, які посилаються на слайси аргументу <code>contents</code> (замість аргументу <code>query</code>).</p>
<p>Інакше кажучи, ми повідомляємо Rust, що дані, отримані <code>search</code> функцією будуть існувати допоки вони передаються в <code>search</code> функцію аргументом <code>contents</code>. Це важливо! Дані, на які посилається <em>слайс</em> мають бути валідними, щоб посилання було валідним; якщо компілятор вважає, що ми робимо строкові слайси <code>query</code> замість <code>contents</code>, він зробить перевірку безпеки некоректно.</p>
<p>Якщо ми забудемо анотації часу існування і спробуємо скомпілювати цю функцію, ми отримаємо цю помилку:</p>
<pre><code class="language-console">$ cargo build
Compiling minigrep v0.1.0 (file:///projects/minigrep)
error[E0106]: missing lifetime specifier
--> src/lib.rs:28:51
|
28 | pub fn search(query: &str, contents: &str) -> Vec<&str> {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `query` or `contents`
help: consider introducing a named lifetime parameter
|
28 | pub fn search<'a>(query: &'a str, contents: &'a str) -> Vec<&'a str> {
| ++++ ++ ++ ++
For more information about this error, try `rustc --explain E0106`.
error: could not compile `minigrep` due to previous error
</code></pre>
<p>Rust не має можливості дізнатися, який з двох аргументів нам потрібен, тому ми маємо явно це вказати. Оскільки <code>contents</code> це аргумент, який містить увесь наш текст і ми хочемо повертати відповідні частини цього тексту, ми розуміємо, що <code>contents</code> це аргумент який має бути пов'язаний з поверненим значенням використовуючи синтаксис часу існування.</p>
<p>Інші мови програмування не вимагають від вас пов'язувати аргументи із поверненим значенням в сигнатурі функції, але ця практика з часом стане легшою. Ви можете захотіти порівняти цей приклад із <a href="">“Validating References with Lifetimes”</a><!-- ignore --> Розділу 10.</p>
<p>Тепер запустимо тест:</p>
<pre><code class="language-console">$ cargo test
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished test [unoptimized + debuginfo] target(s) in 0.97s
Running unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)
running 1 test
test tests::one_result ... FAILED
failures:
---- tests::one_result stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `["safe, fast, productive."]`,
right: `[]`', src/lib.rs:44:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::one_result
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--lib`
</code></pre>
<p>Чудово, тест провалюється, як ми й очікували. Нумо зробимо тест, який пройде!</p>
<h3 id="Написання-Коду-для-Проходження-Тесту"><a class="header" href="#Написання-Коду-для-Проходження-Тесту">Написання Коду для Проходження Тесту</a></h3>
<p>Наразі наш тест провалюється, бо він завжди повертає порожній вектор. Щоб виправити це та імплементувати <code>search</code>, наша програма має виконати такі дії:</p>
<ul>
<li>Ітерувати через кожний рядок вмісту.</li>
<li>Перевірити, чи містить цей рядок нашу стрічку запиту.</li>
<li>Якщо так, то додати його до списку значень який ми повертаємо.</li>
<li>Якщо ні, то нічого не робити.</li>
<li>Повернути отриманий список рядків, які збігаються.</li>
</ul>
<p>Пройдімо кожен крок, починаючи з ітерації по рядках.</p>
<h4 id="Ітерація-над-Рядками-із-Методом-lines"><a class="header" href="#Ітерація-над-Рядками-із-Методом-lines">Ітерація над Рядками із Методом <code>lines</code></a></h4>
<p>Rust має корисний метод для керування ітерацією по стрічці рядок за рядком який зручно названий <code>lines</code>, який працює як показано в Блоці Коду 12-17. Зверніть увагу, це ще не буде компілюватися.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Config {
</span><span class="boring"> pub fn build(args: &[String]) -> Result<Config, &'static str> {
</span><span class="boring"> if args.len() < 3 {
</span><span class="boring"> return Err("not enough arguments");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let query = args[1].clone();
</span><span class="boring"> let file_path = args[2].clone();
</span><span class="boring">
</span><span class="boring"> Ok(Config { query, file_path })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span>pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
for line in contents.lines() {
// do something with line
}
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn one_result() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p><span class="caption">Блок коду 12-17: Ітерація по кожному рядку в <code>contents</code> </span></p>
<p>Метод <code>lines</code> повертає ітератор. Ми поговоримо про ітератори більш детально в <a href="ch13-02-iterators.html">Розділі 13</a><!-- ignore -->, але пригадайте, що ви бачили цей спосіб використання ітератора в <a href="ch03-05-control-flow.html#looping-through-a-collection-with-for">Блоці Коду 3-5</a><!-- ignore -->, де ми використовували цикл <code>for</code> з ітератором для виконання деякого коду на кожному елементі колекції.</p>
<h4 id="Пошук-Запиту-в-Кожному-Рядку"><a class="header" href="#Пошук-Запиту-в-Кожному-Рядку">Пошук Запиту в Кожному Рядку</a></h4>
<p>Далі, ми перевіримо, чи містить поточний рядок стрічку запиту. На щастя, стрічки мають корисний метод названий <code>contains</code>, який робить це для нас! Додайте виклик методу <code>contains</code> в функцію <code>search</code>, як показано в Блоці Коду 12-18. Зауважте, що це ще не буде компілюватися.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Config {
</span><span class="boring"> pub fn build(args: &[String]) -> Result<Config, &'static str> {
</span><span class="boring"> if args.len() < 3 {
</span><span class="boring"> return Err("not enough arguments");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let query = args[1].clone();
</span><span class="boring"> let file_path = args[2].clone();
</span><span class="boring">
</span><span class="boring"> Ok(Config { query, file_path })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span>pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
for line in contents.lines() {
if line.contains(query) {
// do something with line
}
}
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn one_result() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p><span class="caption">Блок коду 12-18: Додавання перевірки наявності <code>query</code> в рядку</span></p>
<p>Ми зараз створюємо функціонал. Щоб він компілювався, нам потрібно повертати значення з вмісту функції, як ми вказали в її сигнатурі.</p>
<h4 id="Зберігання-Відповідних-Рядків"><a class="header" href="#Зберігання-Відповідних-Рядків">Зберігання Відповідних Рядків</a></h4>
<p>Щоб завершити цю функцію, нам потрібен спосіб зберігання зіставлених рядків, які ми хочемо повертати. Для цього, ми можемо створити мутабельний вектор перед циклом <code>for</code> та викликати метод <code>push</code>, щоб зберегти <code>line</code> в векторі. Після циклу <code>for</code>, ми повертаємо вектор, як показано в Блоці Коду 12-19.</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust ignore"><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Config {
</span><span class="boring"> pub fn build(args: &[String]) -> Result<Config, &'static str> {
</span><span class="boring"> if args.len() < 3 {
</span><span class="boring"> return Err("not enough arguments");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let query = args[1].clone();
</span><span class="boring"> let file_path = args[2].clone();
</span><span class="boring">
</span><span class="boring"> Ok(Config { query, file_path })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
</span><span class="boring"> let contents = fs::read_to_string(config.file_path)?;
</span><span class="boring">
</span><span class="boring"> Ok(())
</span><span class="boring">}
</span><span class="boring">
</span>pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let mut results = Vec::new();
for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
}
results
}
<span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn one_result() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p><span class="caption">Listing 12-19: Storing the lines that match so we can return them</span></p>
<p>Тепер функція <code>search</code> повинна повертати тільки рядки, що містять <code>query</code>, і наш тест повинен пройти. Запустимо тест:</p>
<pre><code class="language-console">$ cargo test
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished test [unoptimized + debuginfo] target(s) in 1.22s
Running unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)
running 1 test
test tests::one_result ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests minigrep
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</code></pre>
<p>Наш тест пройшов, тому ми знаємо, що він працює!</p>
<p>На цьому етапі ми могли б розглянути можливості рефакторингу імплементації функції пошуку, зберігаючи проходження тестів та зберігаючи той самий функціонал. Код функції пошуку не настільки й поганий, але він не використовує переваги деяких корисних особливостей ітераторів. Ми повернемось до цього прикладу в <a href="ch13-02-iterators.html">Розділі 13</a><!-- ignore -->, де ми дослідимо ітератори детальніше та розглянемо, як ми можемо їх вдосконалити.</p>
<h4 id="Використання-Функції-search-в-Функції-run"><a class="header" href="#Використання-Функції-search-в-Функції-run">Використання Функції <code>search</code> в Функції <code>run</code></a></h4>
<p>Тепер, коли функція <code>search</code> працює та протестована, нам потрібно викликати <code>search</code> з нашої функції <code>run</code>. Нам потрібно передати значення <code>config.query</code> та <code>contents</code> яке <code>run</code> читає з файлу в функцію <code>search</code>. Потім <code>run</code> виведе в консолі кожен рядок повернений з <code>search</code>:</p>
<p><span class="filename">Файл: src/lib.rs</span></p>
<pre><code class="language-rust ignore"><span class="boring">use std::error::Error;
</span><span class="boring">use std::fs;
</span><span class="boring">
</span><span class="boring">pub struct Config {
</span><span class="boring"> pub query: String,
</span><span class="boring"> pub file_path: String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Config {
</span><span class="boring"> pub fn build(args: &[String]) -> Result<Config, &'static str> {
</span><span class="boring"> if args.len() < 3 {
</span><span class="boring"> return Err("not enough arguments");
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let query = args[1].clone();
</span><span class="boring"> let file_path = args[2].clone();
</span><span class="boring">
</span><span class="boring"> Ok(Config { query, file_path })
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span>pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.file_path)?;
for line in search(&config.query, &contents) {
println!("{line}");
}
Ok(())
}
<span class="boring">
</span><span class="boring">pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
</span><span class="boring"> let mut results = Vec::new();
</span><span class="boring">
</span><span class="boring"> for line in contents.lines() {
</span><span class="boring"> if line.contains(query) {
</span><span class="boring"> results.push(line);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> results
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[cfg(test)]
</span><span class="boring">mod tests {
</span><span class="boring"> use super::*;
</span><span class="boring">
</span><span class="boring"> #[test]
</span><span class="boring"> fn one_result() {
</span><span class="boring"> let query = "duct";
</span><span class="boring"> let contents = "\
</span><span class="boring">Rust:
</span><span class="boring">safe, fast, productive.
</span><span class="boring">Pick three.";
</span><span class="boring">
</span><span class="boring"> assert_eq!(vec!["safe, fast, productive."], search(query, contents));
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre>
<p>Ми досі використовуємо цикл <code>for</code> для повернення кожного рядка із <code>search</code> та його виводу в консолі.</p>
<p>Тепер вся програма має працювати! Нумо спробуємо, спочатку зі словом, яке має повертати річно один рядок із поеми Емілі Дікінсон, "frog":</p>
<pre><code class="language-console">$ cargo run -- frog poem.txt
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `target/debug/minigrep frog poem.txt`
How public, like a frog
</code></pre>
<p>Круто! Спробуємо слово, яке зіставлятиметься з кількома рядками, наприклад "body":</p>
<pre><code class="language-console">$ cargo run -- body poem.txt
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 0.0s
Running `target/debug/minigrep body poem.txt`
I'm nobody! Who are you?
Are you nobody, too?
How dreary to be somebody!
</code></pre>
<p>І, нарешті, переконаймось, що ми не отримуємо жодних рядків, коли ми шукаємо слово, якого немає ніде в поемі, наприклад, "monomorphization":</p>
<pre><code class="language-console">$ cargo run -- monomorphization poem.txt
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 0.0s
Running `target/debug/minigrep monomorphization poem.txt`
</code></pre>
<p>Блискуче! Ми побудували нашу власну мініверсію класичного інструменту та багато дізналися про структурування застосунків. Ми також дізналися дещо про ввід у файл, вивід файлу, часи існування, тестування та парсинг командного рядка.</p>
<p>To round out this project, we’ll briefly demonstrate how to work with environment variables and how to print to standard error, both of which are useful when you’re writing command line programs. ch10-03-lifetime-syntax.html#validating-references-with-lifetimes
ch10-03-lifetime-syntax.html#validating-references-with-lifetimes</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="ch12-03-improving-error-handling-and-modularity.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="ch12-05-working-with-environment-variables.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="ch12-03-improving-error-handling-and-modularity.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="ch12-05-working-with-environment-variables.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>