diff --git a/CONTRIBUTING-en.md b/CONTRIBUTING-en.md new file mode 100644 index 0000000000..71e85cabfa --- /dev/null +++ b/CONTRIBUTING-en.md @@ -0,0 +1,107 @@ +# Contributing + +
+ +[English](CONTRIBUTING-EN.md) | [فارسی](CONTRIBUTING.md) + +
+ +We'd love your help! Thanks for caring about the book. + +## Where to Edit + +All edits should be made in the `src` directory. + +The `nostarch` directory contains snapshots for sending edits to the publishers +of the print version. The snapshot files reflect what has been sent or not, so +they only get updated when edits are sent to No Starch. **Do not submit pull +requests changing files in the `nostarch` directory, they will be closed.** + +We use [`rustfmt`][rustfmt] to apply standard formatting to Rust code in the +repo and [`dprint`][dprint] to apply standing formatting to the Markdown source +and the non-Rust code in the project. + +[rustfmt]: https://github.com/rust-lang/rustfmt +[dprint]: https://dprint.dev + +You will normally have `rustfmt` installed if you have a Rust toolchain +installed; if for some reason you do not have a copy of `rustfmt`, you can add +it by running the following command: + +```sh +rustup component add rustfmt +``` + +To install `dprint`, you can run the following command: + +```sh +cargo install dprint +``` + +Or follow the [instructions][install-dprint] on the `dprint` website. + +[install-dprint]: https://dprint.dev/install/ + +To format Rust code, you can run `rustfmt `, and to format other +files, you can pass `dprint `. Many text editors also have native +support or extensions for both `rustfmt` and `dprint`. + +## Checking for Fixes + +The book rides the Rust release trains. Therefore, if you see a problem on +https://doc.rust-lang.org/stable/book, it may already be fixed on the `main` +branch in this repo, but the fix hasn't gone through nightly -> beta -> stable +yet. Please check the `main` branch in this repo before reporting an issue. + +Looking at the history for a particular file can also give more information on +how or whether an issue has been fixed or not if you're trying to figure that +out. + +Please also search open and closed issues and open and closed PRs before +reporting a new issue or opening a new PR. + +## Licensing + +This repository is under the same license as Rust itself, MIT/Apache2. You +can find the full text of each license in the `LICENSE-*` files in this +repository. + +## Code of Conduct + +The Rust project has [a code of conduct](http://rust-lang.org/policies/code-of-conduct) +that governs all sub-projects, including this one. Please respect it! + +## Expectations + +Because the book is [printed][nostarch], and because we want +to keep the online version of the book close to the print version when +possible, it may take longer than you're used to for us to address your issue +or pull request. + +[nostarch]: https://nostarch.com/rust-programming-language-2nd-edition + +So far, we've been doing a larger revision to coincide with [Rust Editions](https://doc.rust-lang.org/edition-guide/). Between those larger +revisions, we will only be correcting errors. If your issue or pull request +isn't strictly fixing an error, it might sit until the next time that we're +working on a large revision: expect on the order of months or years. Thank you +for your patience! + +## Help wanted + +If you're looking for ways to help that don't involve large amounts of +reading or writing, check out the [open issues with the E-help-wanted +label][help-wanted]. These might be small fixes to the text, Rust code, +frontend code, or shell scripts that would help us be more efficient or +enhance the book in some way! + +[help-wanted]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3AE-help-wanted + +## Translations + +We'd love help translating the book! See the [Translations] label to join in +efforts that are currently in progress. Open a new issue to start working on +a new language! We're waiting on [mdbook support] for multiple languages +before we merge any in, but feel free to start! + +[Translations]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations +[mdbook support]: https://github.com/rust-lang/mdBook/issues/5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c3f4252a5..e7e6ed1ed1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,101 +1,66 @@ -# Contributing +
-We'd love your help! Thanks for caring about the book. +# مشارکت -## Where to Edit +
+ +[English](CONTRIBUTING-EN.md) | [فارسی](CONTRIBUTING.md) + +
-All edits should be made in the `src` directory. +ما از کمک شما استقبال می‌کنیم! از اینکه به کتاب اهمیت می‌دهید سپاسگزاریم. -The `nostarch` directory contains snapshots for sending edits to the publishers -of the print version. The snapshot files reflect what has been sent or not, so -they only get updated when edits are sent to No Starch. **Do not submit pull -requests changing files in the `nostarch` directory, they will be closed.** +## کجا باید ویرایش شود؟ -We use [`rustfmt`][rustfmt] to apply standard formatting to Rust code in the -repo and [`dprint`][dprint] to apply standing formatting to the Markdown source -and the non-Rust code in the project. +تمام ویرایش‌ها باید در دایرکتوری `src` انجام شوند. + +دایرکتوری `nostarch` شامل عکس‌هایی برای ارسال ویرایش‌ها به ناشران نسخه چاپی است. فایل‌های عکس بازتابی از موارد ارسال‌شده یا نشده هستند، بنابراین فقط زمانی به‌روز می‌شوند که ویرایش‌ها به No Starch ارسال شوند. **لطفاً درخواست‌های pull که فایل‌های داخل دایرکتوری `nostarch` را تغییر می‌دهند ارسال نکنید، این درخواست‌ها بسته خواهند شد.** + +ما از [`rustfmt`][rustfmt] برای اعمال قالب‌بندی استاندارد بر روی کد Rust در مخزن و از [`dprint`][dprint] برای اعمال قالب‌بندی استاندارد بر روی منبع Markdown و کدهای غیر Rust در پروژه استفاده می‌کنیم. [rustfmt]: https://github.com/rust-lang/rustfmt [dprint]: https://dprint.dev -You will normally have `rustfmt` installed if you have a Rust toolchain -installed; if for some reason you do not have a copy of `rustfmt`, you can add -it by running the following command: +اگر ابزار Rust را نصب کرده باشید، معمولاً `rustfmt` نصب خواهد شد؛ اما اگر به هر دلیلی نسخه‌ای از `rustfmt` ندارید، می‌توانید با اجرای دستور زیر آن را اضافه کنید: ```sh rustup component add rustfmt ``` -To install `dprint`, you can run the following command: +برای نصب `dprint` می‌توانید دستور زیر را اجرا کنید: ```sh cargo install dprint ``` -Or follow the [instructions][install-dprint] on the `dprint` website. +یا دستورالعمل‌های [وب‌سایت `dprint`][install-dprint] را دنبال کنید. [install-dprint]: https://dprint.dev/install/ -To format Rust code, you can run `rustfmt `, and to format other -files, you can pass `dprint fmt `. Many text editors also have native -support or extensions for both `rustfmt` and `dprint`. +برای قالب‌بندی کدهای Rust، می‌توانید دستور `rustfmt ` را اجرا کنید. برای قالب‌بندی فایل‌های دیگر، می‌توانید از دستور `dprint fmt ` استفاده کنید. بسیاری از ویرایشگرهای متن نیز به‌صورت داخلی یا از طریق افزونه‌ها از هر دو ابزار `rustfmt` و `dprint` پشتیبانی می‌کنند. -## Checking for Fixes +## بررسی issueها -The book rides the Rust release trains. Therefore, if you see a problem on -https://doc.rust-lang.org/stable/book, it may already be fixed on the `main` -branch in this repo, but the fix hasn't gone through nightly -> beta -> stable -yet. Please check the `main` branch in this repo before reporting an issue. +کتاب با نسخه‌های منتشرشده Rust حرکت می‌کند. بنابراین، اگر مشکلی در https://doc.rust-lang.org/stable/book مشاهده کردید، ممکن است قبلاً در شاخه `main` این مخزن رفع شده باشد، اما رفع آن هنوز از مسیر nightly -> beta -> stable عبور نکرده باشد. لطفاً شاخه `main` این مخزن را پیش از گزارش مشکل بررسی کنید. -Looking at the history for a particular file can also give more information on -how or whether an issue has been fixed or not if you're trying to figure that -out. +با نگاهی به تاریخچه یک فایل خاص نیز می‌تواند اطلاعات بیشتری در مورد چگونگی یا وضعیت رفع یک مشکل ارائه دهد. -Please also search open and closed issues and open and closed PRs before -reporting a new issue or opening a new PR. +همچنین لطفاً قبل از گزارش یک مشکل جدید یا باز کردن یک pull request جدید، در میان مشکلات باز و بسته و pull request‌های باز و بسته جستجو کنید. -## Licensing +## مجوزها -This repository is under the same license as Rust itself, MIT/Apache2. You -can find the full text of each license in the `LICENSE-*` files in this -repository. +این مخزن تحت همان مجوزهای Rust، یعنی MIT/Apache2 است. متن کامل هر مجوز در فایل‌های `LICENSE-*` این مخزن قرار دارد. -## Code of Conduct +## کد اخلاق -The Rust project has [a code of conduct](http://rust-lang.org/policies/code-of-conduct) -that governs all sub-projects, including this one. Please respect it! +پروژه Rust دارای [کد اخلاقی](http://rust-lang.org/policies/code-of-conduct) است که تمام زیرپروژه‌ها، از جمله این پروژه را شامل می‌شود. لطفاً به آن احترام بگذارید! -## Expectations +## انتظارات -Because the book is [printed][nostarch], and because we want -to keep the online version of the book close to the print version when -possible, it may take longer than you're used to for us to address your issue -or pull request. +از آنجا که کتاب [چاپ شده است][nostarch] و ما می‌خواهیم نسخه آنلاین کتاب تا حد امکان نزدیک به نسخه چاپی باشد، ممکن است مدت زمان بیشتری طول بکشد تا ما به مشکل یا pull request شما رسیدگی کنیم. [nostarch]: https://nostarch.com/rust-programming-language-2nd-edition -So far, we've been doing a larger revision to coincide with [Rust Editions](https://doc.rust-lang.org/edition-guide/). Between those larger -revisions, we will only be correcting errors. If your issue or pull request -isn't strictly fixing an error, it might sit until the next time that we're -working on a large revision: expect on the order of months or years. Thank you -for your patience! - -## Help wanted - -If you're looking for ways to help that don't involve large amounts of -reading or writing, check out the [open issues with the E-help-wanted -label][help-wanted]. These might be small fixes to the text, Rust code, -frontend code, or shell scripts that would help us be more efficient or -enhance the book in some way! - -[help-wanted]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3AE-help-wanted - -## Translations - -We'd love help translating the book! See the [Translations] label to join in -efforts that are currently in progress. Open a new issue to start working on -a new language! We're waiting on [mdbook support] for multiple languages -before we merge any in, but feel free to start! +تا کنون، ما یک بازبینی بزرگ‌تر را برای همزمانی با [نسخه‌های Rust](https://doc.rust-lang.org/edition-guide/) انجام داده‌ایم. بین این بازبینی‌های بزرگ، فقط خطاها را اصلاح می‌کنیم. اگر مشکل یا pull request شما به طور دقیق یک خطا را اصلاح نمی‌کند، ممکن است تا زمانی که روی یک بازبینی بزرگ دیگر کار کنیم منتظر بماند: انتظار داشته باشید که این مدت ممکن است ماه‌ها یا سال‌ها باشد. از صبر شما سپاسگزاریم! -[Translations]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations -[mdbook support]: https://github.com/rust-lang/mdBook/issues/5 +
\ No newline at end of file diff --git a/COPYRIGHT b/COPYRIGHT index 0fc3ea43fe..013275d622 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,3 +1,15 @@ -This repository is licensed under the Apache License, Version 2.0 - or the MIT -license , at your option. +
+ +[English](COPYRIGHT-EN) | [فارسی](COPYRIGHT) + +
+ +
+ +این مخزن تحت مجوز Apache، نسخه 2.0 + +یا مجوز MIT + +به انتخاب شما مجاز است. + +
\ No newline at end of file diff --git a/COPYRIGHT-EN b/COPYRIGHT-EN new file mode 100644 index 0000000000..c309aa4cb2 --- /dev/null +++ b/COPYRIGHT-EN @@ -0,0 +1,9 @@ +
+ +[English](COPYRIGHT-EN) | [فارسی](COPYRIGHT) + +
+ +This repository is licensed under the Apache License, Version 2.0 + or the MIT +license , at your option. diff --git a/README-EN.md b/README-EN.md new file mode 100644 index 0000000000..f119b15fff --- /dev/null +++ b/README-EN.md @@ -0,0 +1,126 @@ +# The Rust Programming Language + +![Build Status](https://github.com/rust-lang/book/workflows/CI/badge.svg) + +
+ +[English](README-EN.md) | [فارسی](README.md) + +
+ +**Please note, this translation is not yet finished and requires thorough reviewing and editing, which should be completed soon. You can find ongoing translations in the [`persian-draft` branch](https://github.com/drunkleen/rust-book-persian/tree/persian-draft) of the repository.** + + + +This repository contains the source of "The Rust Programming Language" book. + +[The book is available in dead-tree form from No Starch Press][nostarch]. + +[nostarch]: https://nostarch.com/rust-programming-language-2nd-edition + +You can also read the book for free online. Please see the book as shipped with +the latest [stable], [beta], or [nightly] Rust releases. Be aware that issues +in those versions may have been fixed in this repository already, as those +releases are updated less frequently. + +[stable]: https://doc.rust-lang.org/stable/book/ +[beta]: https://doc.rust-lang.org/beta/book/ +[nightly]: https://doc.rust-lang.org/nightly/book/ + +See the [releases] to download just the code of all the code listings that appear in the book. + +[releases]: https://github.com/rust-lang/book/releases + +## Requirements + +Building the book requires [mdBook], ideally the same version that +rust-lang/rust uses in [this file][rust-mdbook]. To get it: + +[mdBook]: https://github.com/rust-lang/mdBook +[rust-mdbook]: https://github.com/rust-lang/rust/blob/master/src/tools/rustbook/Cargo.toml + +```bash +$ cargo install mdbook --locked --version +``` + +The book also uses two mdbook plugins which are part of this repository. If you +do not install them, you will see warnings when building and the output will not +look right, but you _will_ still be able to build the book. To use the plugins, +you should run: + +```bash +$ cargo install --locked --path packages/mdbook-trpl +``` + +## Building + +To build the book, type: + +```bash +cd persian +$ mdbook build +``` + +The output will be in the `book` subdirectory. To check it out, open it in +your web browser. + +_Firefox:_ + +```bash +$ firefox book/index.html # Linux +$ open -a "Firefox" book/index.html # OS X +$ Start-Process "firefox.exe" .\book\index.html # Windows (PowerShell) +$ start firefox.exe .\book\index.html # Windows (Cmd) +``` + +_Chrome:_ + +```bash +$ google-chrome book/index.html # Linux +$ open -a "Google Chrome" book/index.html # OS X +$ Start-Process "chrome.exe" .\book\index.html # Windows (PowerShell) +$ start chrome.exe .\book\index.html # Windows (Cmd) +``` + +To run the tests: + +```bash +$ mdbook test --library-path packages/trpl/target/debug/deps +``` + +## Contributing + +We'd love your help! Please see [CONTRIBUTING-EN.md][contrib] to learn about the +kinds of contributions we're looking for. + +[contrib]: https://github.com/rust-lang/book/blob/main/CONTRIBUTING.md + +Because the book is [printed][nostarch], and because we want +to keep the online version of the book close to the print version when +possible, it may take longer than you're used to for us to address your issue +or pull request. + +So far, we've been doing a larger revision to coincide with [Rust Editions](https://doc.rust-lang.org/edition-guide/). Between those larger +revisions, we will only be correcting errors. If your issue or pull request +isn't strictly fixing an error, it might sit until the next time that we're +working on a large revision: expect on the order of months or years. Thank you +for your patience! + +### Translations + +We'd love help translating the book! See the [Translations] label to join in +efforts that are currently in progress. Open a new issue to start working on +a new language! We're waiting on [mdbook support] for multiple languages +before we merge any in, but feel free to start! + +[Translations]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations +[mdbook support]: https://github.com/rust-lang/mdBook/issues/5 + +## Spellchecking + +To scan source files for spelling errors, you can use the `spellcheck.sh` +script available in the `ci` directory. It needs a dictionary of valid words, +which is provided in `ci/dictionary.txt`. If the script produces a false +positive (say, you used the word `BTreeMap` which the script considers invalid), +you need to add this word to `ci/dictionary.txt` (keep the sorted order for +consistency). diff --git a/README.md b/README.md index 29c9083adb..9f5e09c77e 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,72 @@ -# The Rust Programming Language -![Build Status](https://github.com/rust-lang/book/workflows/CI/badge.svg) +# زبان برنامه‌نویسی راست -This repository contains the source of "The Rust Programming Language" book. +
+ +![وضعیت ساخت](https://github.com/rust-lang/book/workflows/CI/badge.svg) +
-[The book is available in dead-tree form from No Starch Press][nostarch]. +
+ +[English](README-EN.md) | [فارسی](README.md) + +
+ +
+ +**لطفاً توجه داشته باشید که این ترجمه هنوز کامل نشده است و نیاز به بازبینی و ویرایش دقیق دارد که به زودی تکمیل خواهد شد. شما می‌توانید ترجمه‌های در حال انجام را در شاخه [`persian-draft`](https://github.com/drunkleen/rust-book-persian/tree/persian-draft) مخزن پیدا کنید.** + + + + +این مخزن شامل منبع کتاب "زبان برنامه‌نویسی راست" به زبان فارسی است. + +[کتاب انگلیسی به صورت نسخه چاپی از No Starch Press در دسترس است][nostarch]. [nostarch]: https://nostarch.com/rust-programming-language-2nd-edition -You can also read the book for free online. Please see the book as shipped with -the latest [stable], [beta], or [nightly] Rust releases. Be aware that issues -in those versions may have been fixed in this repository already, as those -releases are updated less frequently. +همچنین می‌توانید کتاب را به صورت رایگان آنلاین بخوانید. لطفاً کتاب را به صورت ارسال شده با آخرین نسخه‌های [stable]، [beta]، یا [nightly] راست مشاهده کنید. توجه داشته باشید که مشکلات در آن نسخه‌ها ممکن است در این مخزن قبلاً رفع شده باشد، زیرا آن نسخه‌ها به صورت مکرر به‌روز نمی‌شوند. [stable]: https://doc.rust-lang.org/stable/book/ [beta]: https://doc.rust-lang.org/beta/book/ [nightly]: https://doc.rust-lang.org/nightly/book/ -See the [releases] to download just the code of all the code listings that appear in the book. +فقط برای دانلود تمام لیست‌های کد که در کتاب ظاهر می‌شوند، به [releases] مراجعه کنید. -[releases]: https://github.com/rust-lang/book/releases +[releases]: https://github.com/drunkleen/rust-book-persian/releases -## Requirements +## نیازمندی‌ها -Building the book requires [mdBook], ideally the same version that -rust-lang/rust uses in [this file][rust-mdbook]. To get it: +برای ساخت کتاب نیاز به [mdBook] دارید، ترجیحاً همان نسخه‌ای که rust-lang/rust در [این فایل][rust-mdbook] استفاده می‌کند. برای دریافت آن: [mdBook]: https://github.com/rust-lang/mdBook [rust-mdbook]: https://github.com/rust-lang/rust/blob/master/src/tools/rustbook/Cargo.toml ```bash -$ cargo install mdbook --locked --version +cargo install mdbook --locked --version ``` - -The book also uses two mdbook plugins which are part of this repository. If you -do not install them, you will see warnings when building and the output will not -look right, but you _will_ still be able to build the book. To use the plugins, -you should run: +Example: ```bash $ cargo install --locked --path packages/mdbook-trpl --force ``` -## Building +این کتاب همچنین از دو افزونه mdbook استفاده می‌کند که بخشی از این مخزن هستند. اگر آنها را نصب نکنید، هنگام ساخت هشدارهایی مشاهده خواهید کرد و خروجی به درستی نمایش داده نمی‌شود، اما هنوز هم می‌توانید کتاب را بسازید. برای استفاده از افزونه‌ها، باید دستور زیر را اجرا کنید: -To build the book, type: +```bash +cargo install --locked --path packages/mdbook-trpl +``` + +## ساخت + +برای ساختن کتاب عبارات زیر را تایپ کنید: ```bash $ mdbook build ``` -The output will be in the `book` subdirectory. To check it out, open it in -your web browser. + +خروجی در زیرمجموعه `book` خواهد بود. برای مشاهده، آن را در مرورگر وب خود باز کنید. _Firefox:_ @@ -71,46 +86,21 @@ $ Start-Process "chrome.exe" .\book\index.html # Windows (PowerShell) $ start chrome.exe .\book\index.html # Windows (Cmd) ``` -To run the tests: +برای اجرای تست‌ها: ```bash -$ cd packages/trpl $ mdbook test --library-path packages/trpl/target/debug/deps ``` +## مشارکت -## Contributing - -We'd love your help! Please see [CONTRIBUTING.md][contrib] to learn about the -kinds of contributions we're looking for. - -[contrib]: https://github.com/rust-lang/book/blob/main/CONTRIBUTING.md - -Because the book is [printed][nostarch], and because we want -to keep the online version of the book close to the print version when -possible, it may take longer than you're used to for us to address your issue -or pull request. - -So far, we've been doing a larger revision to coincide with [Rust Editions](https://doc.rust-lang.org/edition-guide/). Between those larger -revisions, we will only be correcting errors. If your issue or pull request -isn't strictly fixing an error, it might sit until the next time that we're -working on a large revision: expect on the order of months or years. Thank you -for your patience! +ما از کمک شما خوشحال می‌شویم! لطفاً [CONTRIBUTING.md][contrib] را ببینید تا با انواع مشارکت‌هایی که به دنبال آنها هستیم آشنا شوید. -### Translations +[contrib]: https://github.com/drunkleen/rust-book-persian/blob/main/CONTRIBUTING.md -We'd love help translating the book! See the [Translations] label to join in -efforts that are currently in progress. Open a new issue to start working on -a new language! We're waiting on [mdbook support] for multiple languages -before we merge any in, but feel free to start! +به دلیل اینکه کتاب [چاپ شده است][nostarch] و همچنین می‌خواهیم نسخه آنلاین کتاب را تا حد ممکن نزدیک به نسخه چاپی نگه داریم، ممکن است برای ما بیشتر از حد معمول طول بکشد تا به مشکل یا درخواست کشش شما رسیدگی کنیم. -[Translations]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations -[mdbook support]: https://github.com/rust-lang/mdBook/issues/5 +## بررسی املاء -## Spellchecking +برای اسکن فایل‌های منبع برای خطاهای املایی، می‌توانید از اسکریپت `spellcheck.sh` که در پوشه `ci` موجود است استفاده کنید. این اسکریپت به یک فرهنگ لغت از کلمات معتبر نیاز دارد که در `ci/dictionary.txt` ارائه شده است. اگر اسکریپت یک مثبت کاذب تولید کرد (مثلاً شما از کلمه `BTreeMap` استفاده کرده‌اید که اسکریپت آن را نامعتبر می‌داند)، باید این کلمه را به `ci/dictionary.txt` اضافه کنید (ترتیب مرتب‌شده را برای سازگاری حفظ کنید). -To scan source files for spelling errors, you can use the `spellcheck.sh` -script available in the `ci` directory. It needs a dictionary of valid words, -which is provided in `ci/dictionary.txt`. If the script produces a false -positive (say, you used the word `BTreeMap` which the script considers invalid), -you need to add this word to `ci/dictionary.txt` (keep the sorted order for -consistency). +
\ No newline at end of file diff --git a/TODO-en.md b/TODO-en.md new file mode 100644 index 0000000000..1e7f01387a --- /dev/null +++ b/TODO-en.md @@ -0,0 +1,23 @@ +# In each chapter + +
+ +[English](TODO-EN.md) | [فارسی](TODO.md) + +
+ +- [ ] Manual regeneration +- [ ] Check for upstream changes from last snapshot +- [ ] Propagate updated output to docx +- [ ] Extract docx and check diff +- [ ] Answer all comments +- [ ] Check cross references +- [ ] Check indentation of --snip-- +- [ ] Numbered lines, Gray out unchanged lines +- [ ] Check line wrapping +- [ ] Check for unneeded command/compiling/running output +- [ ] Check println style and error messages +- [ ] Add alt text to images +- [ ] Index tags + - [ ] search for "convention" conventions:naming:of blah + - [ ] check for double spaces, spaces at the end of paragraphs diff --git a/TODO.md b/TODO.md index b61f853145..2268eabc71 100644 --- a/TODO.md +++ b/TODO.md @@ -1,17 +1,33 @@ -# In each chapter +
-- [ ] Manual regeneration -- [ ] Check for upstream changes from last snapshot -- [ ] Propagate updated output to docx -- [ ] Extract docx and check diff -- [ ] Answer all comments -- [ ] Check cross references -- [ ] Check indentation of --snip-- -- [ ] Numbered lines, Gray out unchanged lines -- [ ] Check line wrapping -- [ ] Check for unneeded command/compiling/running output -- [ ] Check println style and error messages -- [ ] Add alt text to images -- [ ] Index tags - - [ ] search for "convention" conventions:naming:of blah - - [ ] check for double spaces, spaces at the end of paragraphs + +# در هر فصل + +
+ +[English](TODO-EN.md) | [فارسی](TODO.md) + +
+ +
    +
  • [ ] بازتولید دستی
  • +
  • [ ] بررسی تغییرات بالادستی از آخرین اسنپ‌شات
  • +
  • [ ] انتقال خروجی به‌روزشده به docx
  • +
  • [ ] استخراج docx و بررسی تفاوت‌ها
  • +
  • [ ] پاسخ به تمام نظرات
  • +
  • [ ] بررسی ارجاعات متقابل
  • +
  • [ ] بررسی تورفتگی --snip--
  • +
  • [ ] خطوط شماره‌گذاری‌شده، خاکستری‌کردن خطوط بدون تغییر
  • +
  • [ ] بررسی پیچیدن خطوط
  • +
  • [ ] بررسی خروجی‌های دستوری/کامپایل/اجرا که نیاز نیستند
  • +
  • [ ] بررسی سبک println و پیام‌های خطا
  • +
  • [ ] افزودن متن جایگزین به تصاویر
  • +
  • [ ] برچسب‌های شاخص +
      +
    • [ ] جستجوی "convention" conventions:naming:of blah
    • +
    • [ ] بررسی وجود دو فاصله یا فاصله در انتهای پاراگراف‌ها
    • +
    +
  • +
+ +
\ No newline at end of file diff --git a/book.toml b/book.toml index 928a44453f..fae9703e5c 100644 --- a/book.toml +++ b/book.toml @@ -4,11 +4,12 @@ [book] title = "The Rust Programming Language" authors = ["Steve Klabnik", "Carol Nichols", "Chris Krycho", "Contributions from the Rust Community"] +language = "fa" [output.html] -additional-css = ["ferris.css", "theme/2018-edition.css", "theme/semantic-notes.css", "theme/listing.css"] +additional-css = ["ferris.css", "theme/2018-edition.css", "theme/semantic-notes.css", "theme/listing.css", "theme/custom.css"] additional-js = ["ferris.js"] -git-repository-url = "https://github.com/rust-lang/book" +git-repository-url = "https://github.com/persian-rust/book" [output.html.redirect] "ch17-00-oop.html" = "ch18-00-oop.html" diff --git a/listings/ch02-guessing-game-tutorial/listing-02-03/Cargo.toml b/listings/ch02-guessing-game-tutorial/listing-02-03/Cargo.toml index eba27a883c..6c2b31e767 100644 --- a/listings/ch02-guessing-game-tutorial/listing-02-03/Cargo.toml +++ b/listings/ch02-guessing-game-tutorial/listing-02-03/Cargo.toml @@ -6,4 +6,4 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = "0.8.5" +rand = "0.9.2" diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 286cb46db8..7a27c5aac0 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,143 +1,144 @@ -# The Rust Programming Language - -[The Rust Programming Language](title-page.md) -[Foreword](foreword.md) -[Introduction](ch00-00-introduction.md) - -## Getting started - -- [Getting Started](ch01-00-getting-started.md) - - [Installation](ch01-01-installation.md) - - [Hello, World!](ch01-02-hello-world.md) - - [Hello, Cargo!](ch01-03-hello-cargo.md) - -- [Programming a Guessing Game](ch02-00-guessing-game-tutorial.md) - -- [Common Programming Concepts](ch03-00-common-programming-concepts.md) - - [Variables and Mutability](ch03-01-variables-and-mutability.md) - - [Data Types](ch03-02-data-types.md) - - [Functions](ch03-03-how-functions-work.md) - - [Comments](ch03-04-comments.md) - - [Control Flow](ch03-05-control-flow.md) - -- [Understanding Ownership](ch04-00-understanding-ownership.md) - - [What is Ownership?](ch04-01-what-is-ownership.md) - - [References and Borrowing](ch04-02-references-and-borrowing.md) - - [The Slice Type](ch04-03-slices.md) - -- [Using Structs to Structure Related Data](ch05-00-structs.md) - - [Defining and Instantiating Structs](ch05-01-defining-structs.md) - - [An Example Program Using Structs](ch05-02-example-structs.md) - - [Method Syntax](ch05-03-method-syntax.md) - -- [Enums and Pattern Matching](ch06-00-enums.md) - - [Defining an Enum](ch06-01-defining-an-enum.md) - - [The `match` Control Flow Construct](ch06-02-match.md) - - [Concise Control Flow with `if let` and `let else`](ch06-03-if-let.md) - -## Basic Rust Literacy - -- [Managing Growing Projects with Packages, Crates, and Modules](ch07-00-managing-growing-projects-with-packages-crates-and-modules.md) - - [Packages and Crates](ch07-01-packages-and-crates.md) - - [Defining Modules to Control Scope and Privacy](ch07-02-defining-modules-to-control-scope-and-privacy.md) - - [Paths for Referring to an Item in the Module Tree](ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md) - - [Bringing Paths Into Scope with the `use` Keyword](ch07-04-bringing-paths-into-scope-with-the-use-keyword.md) - - [Separating Modules into Different Files](ch07-05-separating-modules-into-different-files.md) - -- [Common Collections](ch08-00-common-collections.md) - - [Storing Lists of Values with Vectors](ch08-01-vectors.md) - - [Storing UTF-8 Encoded Text with Strings](ch08-02-strings.md) - - [Storing Keys with Associated Values in Hash Maps](ch08-03-hash-maps.md) - -- [Error Handling](ch09-00-error-handling.md) - - [Unrecoverable Errors with `panic!`](ch09-01-unrecoverable-errors-with-panic.md) - - [Recoverable Errors with `Result`](ch09-02-recoverable-errors-with-result.md) - - [To `panic!` or Not to `panic!`](ch09-03-to-panic-or-not-to-panic.md) - -- [Generic Types, Traits, and Lifetimes](ch10-00-generics.md) - - [Generic Data Types](ch10-01-syntax.md) - - [Traits: Defining Shared Behavior](ch10-02-traits.md) - - [Validating References with Lifetimes](ch10-03-lifetime-syntax.md) - -- [Writing Automated Tests](ch11-00-testing.md) - - [How to Write Tests](ch11-01-writing-tests.md) - - [Controlling How Tests Are Run](ch11-02-running-tests.md) - - [Test Organization](ch11-03-test-organization.md) - -- [An I/O Project: Building a Command Line Program](ch12-00-an-io-project.md) - - [Accepting Command Line Arguments](ch12-01-accepting-command-line-arguments.md) - - [Reading a File](ch12-02-reading-a-file.md) - - [Refactoring to Improve Modularity and Error Handling](ch12-03-improving-error-handling-and-modularity.md) - - [Developing the Library’s Functionality with Test Driven Development](ch12-04-testing-the-librarys-functionality.md) - - [Working with Environment Variables](ch12-05-working-with-environment-variables.md) - - [Writing Error Messages to Standard Error Instead of Standard Output](ch12-06-writing-to-stderr-instead-of-stdout.md) - -## Thinking in Rust - -- [Functional Language Features: Iterators and Closures](ch13-00-functional-features.md) - - [Closures: Anonymous Functions that Capture Their Environment](ch13-01-closures.md) - - [Processing a Series of Items with Iterators](ch13-02-iterators.md) - - [Improving Our I/O Project](ch13-03-improving-our-io-project.md) - - [Comparing Performance: Loops vs. Iterators](ch13-04-performance.md) - -- [More about Cargo and Crates.io](ch14-00-more-about-cargo.md) - - [Customizing Builds with Release Profiles](ch14-01-release-profiles.md) - - [Publishing a Crate to Crates.io](ch14-02-publishing-to-crates-io.md) - - [Cargo Workspaces](ch14-03-cargo-workspaces.md) - - [Installing Binaries from Crates.io with `cargo install`](ch14-04-installing-binaries.md) - - [Extending Cargo with Custom Commands](ch14-05-extending-cargo.md) - -- [Smart Pointers](ch15-00-smart-pointers.md) - - [Using `Box` to Point to Data on the Heap](ch15-01-box.md) - - [Treating Smart Pointers Like Regular References with `Deref`](ch15-02-deref.md) - - [Running Code on Cleanup with the `Drop` Trait](ch15-03-drop.md) - - [`Rc`, the Reference Counted Smart Pointer](ch15-04-rc.md) - - [`RefCell` and the Interior Mutability Pattern](ch15-05-interior-mutability.md) - - [Reference Cycles Can Leak Memory](ch15-06-reference-cycles.md) - -- [Fearless Concurrency](ch16-00-concurrency.md) - - [Using Threads to Run Code Simultaneously](ch16-01-threads.md) - - [Using Message Passing to Transfer Data Between Threads](ch16-02-message-passing.md) - - [Shared-State Concurrency](ch16-03-shared-state.md) - - [Extensible Concurrency with the `Send` and `Sync` Traits](ch16-04-extensible-concurrency-sync-and-send.md) - -- [Fundamentals of Asynchronous Programming: Async, Await, Futures, and Streams](ch17-00-async-await.md) - - [Futures and the Async Syntax](ch17-01-futures-and-syntax.md) - - [Applying Concurrency with Async](ch17-02-concurrency-with-async.md) - - [Working With Any Number of Futures](ch17-03-more-futures.md) - - [Streams: Futures in Sequence](ch17-04-streams.md) - - [A Closer Look at the Traits for Async](ch17-05-traits-for-async.md) - - [Futures, Tasks, and Threads](ch17-06-futures-tasks-threads.md) - -- [Object Oriented Programming Features of Rust](ch18-00-oop.md) - - [Characteristics of Object-Oriented Languages](ch18-01-what-is-oo.md) - - [Using Trait Objects That Allow for Values of Different Types](ch18-02-trait-objects.md) - - [Implementing an Object-Oriented Design Pattern](ch18-03-oo-design-patterns.md) - -## Advanced Topics - -- [Patterns and Matching](ch19-00-patterns.md) - - [All the Places Patterns Can Be Used](ch19-01-all-the-places-for-patterns.md) - - [Refutability: Whether a Pattern Might Fail to Match](ch19-02-refutability.md) - - [Pattern Syntax](ch19-03-pattern-syntax.md) - -- [Advanced Features](ch20-00-advanced-features.md) - - [Unsafe Rust](ch20-01-unsafe-rust.md) - - [Advanced Traits](ch20-02-advanced-traits.md) - - [Advanced Types](ch20-03-advanced-types.md) - - [Advanced Functions and Closures](ch20-04-advanced-functions-and-closures.md) - - [Macros](ch20-05-macros.md) - -- [Final Project: Building a Multithreaded Web Server](ch21-00-final-project-a-web-server.md) - - [Building a Single-Threaded Web Server](ch21-01-single-threaded.md) - - [Turning Our Single-Threaded Server into a Multithreaded Server](ch21-02-multithreaded.md) - - [Graceful Shutdown and Cleanup](ch21-03-graceful-shutdown-and-cleanup.md) - -- [Appendix](appendix-00.md) - - [A - Keywords](appendix-01-keywords.md) - - [B - Operators and Symbols](appendix-02-operators.md) - - [C - Derivable Traits](appendix-03-derivable-traits.md) - - [D - Useful Development Tools](appendix-04-useful-development-tools.md) - - [E - Editions](appendix-05-editions.md) - - [F - Translations of the Book](appendix-06-translation.md) - - [G - How Rust is Made and “Nightly Rust”](appendix-07-nightly-rust.md) +# زبان برنامه‌نویسی Rust + +[زبان برنامه‌نویسی راست](title-page.md) +[پیش‌گفتار](foreword.md) +[مقدمه](ch00-00-introduction.md) + +## شروع کار + +- [شروع کار](ch01-00-getting-started.md) + - [نصب](ch01-01-installation.md) + - [سلام، دنیا!](ch01-02-hello-world.md) + - [سلام، Cargo!](ch01-03-hello-cargo.md) + +- [برنامه‌نویسی یک بازی حدس زدن](ch02-00-guessing-game-tutorial.md) + +- [مفاهیم رایج برنامه‌نویسی](ch03-00-common-programming-concepts.md) + - [متغیرها و تغییرپذیری](ch03-01-variables-and-mutability.md) + - [انواع داده](ch03-02-data-types.md) + - [توابع](ch03-03-how-functions-work.md) + - [کامنت‌ها](ch03-04-comments.md) + - [کنترل جریان](ch03-05-control-flow.md) + +- [درک مالکیت](ch04-00-understanding-ownership.md) + - [مالکیت چیست؟](ch04-01-what-is-ownership.md) + - [ارجاعات و قرض گرفتن](ch04-02-references-and-borrowing.md) + - [نوع Slice](ch04-03-slices.md) + +- [استفاده از Structها برای سازماندهی داده‌های مرتبط](ch05-00-structs.md) + - [تعریف و نمونه‌سازی Structها](ch05-01-defining-structs.md) + - [یک برنامه نمونه با استفاده از Structها](ch05-02-example-structs.md) + - [متد](ch05-03-method-syntax.md) + +- [شمارنده‌ها و تطابق الگو](ch06-00-enums.md) + - [تعریف یک Enum](ch06-01-defining-an-enum.md) + - [ساختار جریان کنترل `match`](ch06-02-match.md) + - [جریان کنترل مختصر با `if let` و `let else`](ch06-03-if-let.md) + +## سواد پایه Rust + +- [مدیریت پروژه‌های در حال رشد با پکیج‌ها، جعبه‌ها (crates)، و ماژول‌ها](ch07-00-managing-growing-projects-with-packages-crates-and-modules.md) + - [پکیج‌ها و جعبه‌ها (crates)](ch07-01-packages-and-crates.md) + - [تعریف ماژول‌ها برای کنترل دامنه و حریم خصوصی](ch07-02-defining-modules-to-control-scope-and-privacy.md) + - [مسیرها برای ارجاع به یک آیتم در درخت ماژول](ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md) + - [وارد کردن مسیرها با کلمه کلیدی `use`](ch07-04-bringing-paths-into-scope-with-the-use-keyword.md) + - [جداسازی ماژول‌ها به فایل‌های مختلف](ch07-05-separating-modules-into-different-files.md) + +- [مجموعه‌های رایج](ch08-00-common-collections.md) + - [ذخیره لیست مقادیر با بردارها](ch08-01-vectors.md) + - [ذخیره متن رمزگذاری‌شده UTF-8 با رشته‌ها](ch08-02-strings.md) + - [ذخیره کلیدها با مقادیر مرتبط در نقشه‌های هش](ch08-03-hash-maps.md) + +- [مدیریت خطاها](ch09-00-error-handling.md) + - [خطاهای غیرقابل بازیابی با `panic!`](ch09-01-unrecoverable-errors-with-panic.md) + - [خطاهای قابل بازیابی با `Result`](ch09-02-recoverable-errors-with-result.md) + - [آیا از `panic!` استفاده کنیم یا نه؟](ch09-03-to-panic-or-not-to-panic.md) + +- [انواع جنریک، صفت‌ها، و طول عمرها](ch10-00-generics.md) + - [انواع داده جنریک](ch10-01-syntax.md) + - [صفت‌ها: تعریف رفتارهای مشترک](ch10-02-traits.md) + - [اعتبارسنجی ارجاعات با طول عمرها](ch10-03-lifetime-syntax.md) + +- [نوشتن تست‌های خودکار](ch11-00-testing.md) + - [چگونه تست بنویسیم](ch11-01-writing-tests.md) + - [کنترل نحوه اجرای تست‌ها](ch11-02-running-tests.md) + - [سازماندهی تست‌ها](ch11-03-test-organization.md) + +- [یک پروژه I/O: ساخت یک برنامه خط فرمان](ch12-00-an-io-project.md) + - [پذیرش آرگومان‌های خط فرمان](ch12-01-accepting-command-line-arguments.md) + - [خواندن یک فایل](ch12-02-reading-a-file.md) + - [بازسازی برای بهبود ماژولاریت و مدیریت خطا](ch12-03-improving-error-handling-and-modularity.md) + - [توسعه قابلیت‌های کتابخانه با توسعه مبتنی بر تست](ch12-04-testing-the-librarys-functionality.md) + - [کار با متغیرهای محیطی](ch12-05-working-with-environment-variables.md) + - [نوشتن پیام‌های خطا به خروجی خطا به جای خروجی استاندارد](ch12-06-writing-to-stderr-instead-of-stdout.md) + +## تفکر در Rust + +- [ویژگی‌های زبان‌های تابعی: Iteratorها و Closureها](ch13-00-functional-features.md) + - [Closureها: توابع ناشناخته که محیط خود را می‌گیرند](ch13-01-closures.md) + - [پردازش یک سری آیتم با Iteratorها](ch13-02-iterators.md) + - [بهبود پروژه I/O ما](ch13-03-improving-our-io-project.md) + - [مقایسه عملکرد: حلقه‌ها در مقابل Iteratorها](ch13-04-performance.md) + +- [بیشتر درباره Cargo و Crates.io](ch14-00-more-about-cargo.md) + - [سفارشی‌سازی بیلدها با پروفایل‌های انتشار](ch14-01-release-profiles.md) + - [منتشر کردن یک crate در Crates.io](ch14-02-publishing-to-crates-io.md) + - [محیط‌های کاری Cargo](ch14-03-cargo-workspaces.md) + - [نصب باینری‌ها از Crates.io با `cargo install`](ch14-04-installing-binaries.md) + - [گسترش Cargo با دستورات سفارشی](ch14-05-extending-cargo.md) + +- [اشاره‌گرهای هوشمند (Smart Pointers)](ch15-00-smart-pointers.md) + - [استفاده از `Box` برای اشاره به داده‌ها در Heap](ch15-01-box.md) + - [رفتار اشاره‌گر (Pointer)های هوشمند مانند ارجاعات معمولی با صفت `Deref`](ch15-02-deref.md) + - [اجرای کد هنگام پاک‌سازی با صفت `Drop`](ch15-03-drop.md) + - [`Rc`، اشاره‌گر (Pointer) هوشمند با شمارش ارجاعات](ch15-04-rc.md) + - [`RefCell` و الگوی تغییرپذیری داخلی](ch15-05-interior-mutability.md) + - [چرخه‌های ارجاع می‌توانند باعث نشت حافظه شوند](ch15-06-reference-cycles.md) + +- [همزمانی بدون ترس](ch16-00-concurrency.md) + - [استفاده از نخ‌ها برای اجرای همزمان کد](ch16-01-threads.md) + - [استفاده از پیام‌رسانی برای انتقال داده بین نخ‌ها](ch16-02-message-passing.md) + - [همزمانی با وضعیت مشترک](ch16-03-shared-state.md) + - [همزمانی قابل گسترش با صفت‌های `Sync` و `Send`](ch16-04-extensible-concurrency-sync-and-send.md) + +- [Async و Await](ch17-00-async-await.md) + - [Futures و نحو Async](ch17-01-futures-and-syntax.md) + - [همزمانی با Async](ch17-02-concurrency-with-async.md) + - [کار با تعداد دلخواهی از Futures](ch17-03-more-futures.md) + - [Streams](ch17-04-streams.md) + - [بررسی عمیق صفت‌ها برای Async](ch17-05-traits-for-async.md) + - [Futures، Tasks، و Threads](ch17-06-futures-tasks-threads.md) + +- [ویژگی‌های برنامه‌نویسی شی‌گرا در Rust](ch18-00-oop.md) + - [ویژگی‌های زبان‌های شی‌گرا](ch18-01-what-is-oo.md) + - [استفاده از صفت‌های شی‌گرا برای مقادیر از تایپ‌های مختلف](ch18-02-trait-objects.md) + - [پیاده‌سازی یک الگوی طراحی شی‌گرا](ch18-03-oo-design-patterns.md) + +## مباحث پیشرفته + +- [الگوها و تطبیق](ch19-00-patterns.md) + - [تمام مکان‌هایی که می‌توان از الگوها استفاده کرد](ch19-01-all-the-places-for-patterns.md) + - [قابلیت رد: آیا ممکن است یک الگو با مقدار مطابقت نداشته باشد؟](ch19-02-refutability.md) + - [نحو الگوها](ch19-03-pattern-syntax.md) + + +- [ویژگی‌های پیشرفته](ch20-00-advanced-features.md) + - [Rust ناایمن](ch20-01-unsafe-rust.md) + - [Traits پیشرفته](ch20-02-advanced-traits.md) + - [تایپ‌های پیشرفته](ch20-03-advanced-types.md) + - [توابع و Closures پیشرفته](ch20-04-advanced-functions-and-closures.md) + - [ماکروها](ch20-05-macros.md) + +- [پروژه نهایی: ساخت یک وب‌سرور چندنخی](ch21-00-final-project-a-web-server.md) + - [ساخت یک وب‌سرور single-threaded](ch21-01-single-threaded.md) + - [تبدیل وب‌سرور تک‌نخی به وب‌سرور چندنخی](ch21-02-multithreaded.md) + - [خاموشی ملایم و پاک‌سازی](ch21-03-graceful-shutdown-and-cleanup.md) + +- [ضمیمه](appendix-00.md) + - [الف - کلمات کلیدی](appendix-01-keywords.md) + - [ب - عملگرها و نمادها](appendix-02-operators.md) + - [ج - صفت‌های قابل اشتقاق](appendix-03-derivable-traits.md) + - [د - ابزارهای توسعه مفید](appendix-04-useful-development-tools.md) + - [ه - نسخه‌ها](appendix-05-editions.md) + - [و - ترجمه‌های کتاب](appendix-06-translation.md) + - [ی - چگونه Rust ساخته می‌شود و "Nightly Rust"](appendix-07-nightly-rust.md) diff --git a/src/appendix-00.md b/src/appendix-00.md index 83a7e91329..9bf891af51 100644 --- a/src/appendix-00.md +++ b/src/appendix-00.md @@ -1,4 +1,5 @@ -# Appendix +# ضمیمه -The following sections contain reference material you may find useful in your -Rust journey. +بخش‌های زیر حاوی مطالب مرجعی هستند که ممکن است در مسیر یادگیری Rust برای شما مفید باشند. + + \ No newline at end of file diff --git a/src/appendix-01-keywords.md b/src/appendix-01-keywords.md index 15f6f5b9ae..d913a20836 100644 --- a/src/appendix-01-keywords.md +++ b/src/appendix-01-keywords.md @@ -1,69 +1,58 @@ -## Appendix A: Keywords +## ضمیمه الف: کلمات کلیدی -The following list contains keywords that are reserved for current or future -use by the Rust language. As such, they cannot be used as identifiers (except -as raw identifiers as we’ll discuss in the “[Raw -Identifiers][raw-identifiers]” section). Identifiers are names -of functions, variables, parameters, struct fields, modules, crates, constants, -macros, static values, attributes, types, traits, or lifetimes. +لیست زیر شامل کلمات کلیدی است که برای استفاده فعلی یا آینده توسط زبان Rust رزرو شده‌اند. به همین دلیل، نمی‌توان از آن‌ها به عنوان شناسه استفاده کرد (مگر به عنوان شناسه خام، همان‌طور که در بخش “[شناسه‌های خام][raw-identifiers]” توضیح داده خواهد شد). شناسه‌ها نام‌هایی هستند که برای توابع، متغیرها، پارامترها، فیلدهای ساختار، ماژول‌ها، جعبه‌ها (crates)، ثابت‌ها، ماکروها، مقادیر استاتیک، ویژگی‌ها، انواع، ویژگی‌ها، یا طول عمرها استفاده می‌شوند. [raw-identifiers]: #raw-identifiers -### Keywords Currently in Use - -The following is a list of keywords currently in use, with their functionality -described. - -- `as` - perform primitive casting, disambiguate the specific trait containing - an item, or rename items in `use` statements -- `async` - return a `Future` instead of blocking the current thread -- `await` - suspend execution until the result of a `Future` is ready -- `break` - exit a loop immediately -- `const` - define constant items or constant raw pointers -- `continue` - continue to the next loop iteration -- `crate` - in a module path, refers to the crate root -- `dyn` - dynamic dispatch to a trait object -- `else` - fallback for `if` and `if let` control flow constructs -- `enum` - define an enumeration -- `extern` - link an external function or variable -- `false` - Boolean false literal -- `fn` - define a function or the function pointer type -- `for` - loop over items from an iterator, implement a trait, or specify a - higher-ranked lifetime -- `if` - branch based on the result of a conditional expression -- `impl` - implement inherent or trait functionality -- `in` - part of `for` loop syntax -- `let` - bind a variable -- `loop` - loop unconditionally -- `match` - match a value to patterns -- `mod` - define a module -- `move` - make a closure take ownership of all its captures -- `mut` - denote mutability in references, raw pointers, or pattern bindings -- `pub` - denote public visibility in struct fields, `impl` blocks, or modules -- `ref` - bind by reference -- `return` - return from function -- `Self` - a type alias for the type we are defining or implementing -- `self` - method subject or current module -- `static` - global variable or lifetime lasting the entire program execution -- `struct` - define a structure -- `super` - parent module of the current module -- `trait` - define a trait -- `true` - Boolean true literal -- `type` - define a type alias or associated type -- `union` - define a [union][union]; is only a keyword when used - in a union declaration -- `unsafe` - denote unsafe code, functions, traits, or implementations -- `use` - bring symbols into scope; specify precise captures for generic and - lifetime bounds -- `where` - denote clauses that constrain a type -- `while` - loop conditionally based on the result of an expression +### کلمات کلیدی در حال استفاده + +فهرست زیر شامل کلمات کلیدی است که در حال حاضر استفاده می‌شوند، همراه با توضیح عملکرد آن‌ها: + +- `as` - انجام تبدیل اولیه، مشخص کردن ویژگی خاص حاوی یک آیتم، یا تغییر نام آیتم‌ها در دستورات `use` +- `async` - بازگرداندن یک `Future` به جای مسدود کردن Thread فعلی +- `await` - متوقف کردن اجرا تا زمانی که نتیجه یک `Future` آماده شود +- `break` - خروج فوری از یک حلقه +- `const` - تعریف آیتم‌های ثابت یا اشاره‌گرهای خام ثابت +- `continue` - ادامه به تکرار بعدی حلقه +- `crate` - در مسیر ماژول، به ریشه crate اشاره دارد +- `dyn` - توزیع داینامیک به یک شیء ویژگی +- `else` - حالت جایگزین برای سازه‌های جریان کنترلی `if` و `if let` +- `enum` - تعریف یک شمارش +- `extern` - لینک کردن یک تابع یا متغیر خارجی +- `false` - مقدار بولی `false` +- `fn` - تعریف یک تابع یا نوع اشاره‌گر تابع +- `for` - حلقه بر روی آیتم‌های یک iterator، پیاده‌سازی یک ویژگی، یا مشخص کردن طول عمر سطح بالاتر +- `if` - انشعاب بر اساس نتیجه یک عبارت شرطی +- `impl` - پیاده‌سازی قابلیت‌های ذاتی یا ویژگی +- `in` - بخشی از سینتکس حلقه `for` +- `let` - متصل کردن یک متغیر +- `loop` - حلقه بدون شرط +- `match` - تطابق یک مقدار با الگوها +- `mod` - تعریف یک ماژول +- `move` - مالکیت متغیرهای گرفته‌شده توسط Closure را منتقل می‌کند +- `mut` - نشان دادن قابلیت تغییر در مراجع، اشاره‌گرهای خام، یا بایندینگ الگوها +- `pub` - نشان دادن قابلیت مشاهده عمومی در فیلدهای struct، بلوک‌های `impl`، یا ماژول‌ها +- `ref` - بایند کردن با مرجع +- `return` - بازگرداندن از تابع +- `Self` - یک نام مستعار برای نوعی که در حال تعریف یا پیاده‌سازی آن هستیم +- `self` - موضوع متد یا ماژول فعلی +- `static` - متغیر جهانی یا طول عمر طولانی تا زمان اجرای کامل برنامه +- `struct` - تعریف یک ساختار +- `super` - ماژول والد ماژول فعلی +- `trait` - تعریف یک ویژگی +- `true` - مقدار بولی `true` +- `type` - تعریف یک نام مستعار نوع یا نوع وابسته +- `union` - تعریف یک [union][union]؛ فقط هنگام استفاده در یک اعلان union یک کلمه کلیدی است +- `unsafe` - نشان دادن کد، توابع، ویژگی‌ها یا پیاده‌سازی‌های ناامن +- `use` - آوردن نمادها به دامنه؛ مشخص کردن برداشت دقیق برای محدودیت‌های عمومی و طول عمر +- `where` - مشخص کردن بندهایی که یک نوع را محدود می‌کنند +- `while` - حلقه شرطی بر اساس نتیجه یک عبارت [union]: ../reference/items/unions.html -### Keywords Reserved for Future Use +### کلمات کلیدی رزرو شده برای استفاده در آینده -The following keywords do not yet have any functionality but are reserved by -Rust for potential future use. +کلمات کلیدی زیر هنوز هیچ کاربردی ندارند اما توسط Rust برای استفاده احتمالی در آینده رزرو شده‌اند: - `abstract` - `become` @@ -80,13 +69,11 @@ Rust for potential future use. - `virtual` - `yield` -### Raw Identifiers +### شناسه‌های خام -_Raw identifiers_ are the syntax that lets you use keywords where they wouldn’t -normally be allowed. You use a raw identifier by prefixing a keyword with `r#`. +_شناسه‌های خام_ سینتکسی هستند که به شما اجازه می‌دهند از کلمات کلیدی در جایی که معمولاً مجاز نیستند استفاده کنید. برای استفاده از یک شناسه خام، یک `r#` قبل از کلمه کلیدی اضافه کنید. -For example, `match` is a keyword. If you try to compile the following function -that uses `match` as its name: +برای مثال، `match` یک کلمه کلیدی است. اگر بخواهید تابع زیر را که از `match` به عنوان نام خود استفاده می‌کند کامپایل کنید: Filename: src/main.rs @@ -96,7 +83,7 @@ fn match(needle: &str, haystack: &str) -> bool { } ``` -you’ll get this error: +شما این خطا را دریافت خواهید کرد: ```text error: expected identifier, found keyword `match` @@ -106,9 +93,13 @@ error: expected identifier, found keyword `match` | ^^^^^ expected identifier, found keyword ``` -The error shows that you can’t use the keyword `match` as the function -identifier. To use `match` as a function name, you need to use the raw -identifier syntax, like this: +این خطا نشان می‌دهد که نمی‌توانید از کلمه کلیدی `match` به عنوان شناسه تابع استفاده کنید. برای استفاده از `match` به عنوان نام تابع، باید از سینتکس شناسه خام به این شکل استفاده کنید: + +```rust +fn r#match(needle: &str, haystack: &str) -> bool { + haystack.contains(needle) +} +``` Filename: src/main.rs @@ -122,18 +113,32 @@ fn main() { } ``` -This code will compile without any errors. Note the `r#` prefix on the function -name in its definition as well as where the function is called in `main`. +این کد بدون هیچ خطایی کامپایل خواهد شد. به پیشوند `r#` روی نام تابع در تعریف آن و همچنین جایی که تابع در `main` فراخوانی می‌شود، توجه کنید. + +شناسه‌های خام (_Raw identifiers_) به شما این امکان را می‌دهند که از هر کلمه‌ای که انتخاب می‌کنید به عنوان شناسه استفاده کنید، حتی اگر آن کلمه به‌طور معمول یک کلمه کلیدی رزرو‌شده باشد. این ویژگی آزادی بیشتری برای انتخاب نام شناسه‌ها فراهم می‌کند و همچنین امکان ادغام با برنامه‌هایی که به زبانی نوشته شده‌اند که این کلمات در آن زبان کلمات کلیدی نیستند، را می‌دهد. علاوه بر این، شناسه‌های خام به شما اجازه می‌دهند از کتابخانه‌هایی استفاده کنید که با نسخه‌ای از Rust نوشته شده‌اند که با نسخه مورد استفاده شما متفاوت است. + +برای مثال، `try` در نسخه ۲۰۱۵ کلمه کلیدی نیست، اما در نسخه‌های ۲۰۱۸، ۲۰۲۱ و ۲۰۲۴ کلمه کلیدی است. اگر به کتابخانه‌ای وابسته باشید که با نسخه ۲۰۱۵ نوشته شده و یک تابع به نام `try` دارد، باید از سینتکس شناسه خام، یعنی `r#try`، برای فراخوانی آن تابع از کد نسخه ۲۰۱۸ خود استفاده کنید. برای اطلاعات بیشتر در مورد نسخه‌ها به [ضمیمه ه][appendix-e] مراجعه کنید. + +شناسه‌های خام (Raw identifiers) به شما این امکان +را می‌دهند که از هر واژه‌ای به‌عنوان یک شناسه +استفاده کنید، حتی اگر آن واژه یک کلمه‌ی +رزرو‌شده باشد. این قابلیت، آزادی عمل بیشتری +برای انتخاب نام شناسه‌ها به ما می‌دهد و همچنین +امکان یکپارچه‌سازی با برنامه‌هایی که به زبانی نوشته +شده‌اند که این کلمات در آن‌ها رزرو‌شده نیستند +را فراهم می‌کند. علاوه بر این، شناسه‌های خام +به شما اجازه می‌دهند تا از کتابخانه‌هایی استفاده +کنید که با نگارشی متفاوت از crate شما نوشته +شده‌اند. برای مثال، `try` در نگارش ۲۰۱۵ +کلمه‌ی کلیدی محسوب نمی‌شود، اما در نگارش‌های +۲۰۱۸، ۲۰۲۱ و ۲۰۲۴ یک کلمه‌ی کلیدی است. +اگر به کتابخانه‌ای وابسته باشید که با نگارش +۲۰۱۵ نوشته شده و تابعی با نام `try` دارد، برای +فراخوانی این تابع در کد خود (با نگارش‌های جدید)، +باید از نحو شناسه‌ی خام استفاده کنید، یعنی +`r#try`. برای اطلاعات بیشتر درباره‌ی نگارش‌ها +به [ضمیمه‌ی ه][appendix-e] مراجعه کنید. -Raw identifiers allow you to use any word you choose as an identifier, even if -that word happens to be a reserved keyword. This gives us more freedom to choose -identifier names, as well as lets us integrate with programs written in a -language where these words aren’t keywords. In addition, raw identifiers allow -you to use libraries written in a different Rust edition than your crate uses. -For example, `try` isn’t a keyword in the 2015 edition but is in the 2018, 2021, -and 2024 editions. If you depend on a library that is written using the 2015 -edition and has a `try` function, you’ll need to use the raw identifier syntax, -`r#try` in this case, to call that function from your code on later editions. -See [Appendix E][appendix-e] for more information on editions. [appendix-e]: appendix-05-editions.html + diff --git a/src/appendix-02-operators.md b/src/appendix-02-operators.md index 36482f8486..23ee3c753b 100644 --- a/src/appendix-02-operators.md +++ b/src/appendix-02-operators.md @@ -1,206 +1,201 @@ -## Appendix B: Operators and Symbols - -This appendix contains a glossary of Rust’s syntax, including operators and -other symbols that appear by themselves or in the context of paths, generics, -trait bounds, macros, attributes, comments, tuples, and brackets. - -### Operators - -Table B-1 contains the operators in Rust, an example of how the operator would -appear in context, a short explanation, and whether that operator is -overloadable. If an operator is overloadable, the relevant trait to use to -overload that operator is listed. - -Table B-1: Operators - -| Operator | Example | Explanation | Overloadable? | -| ------------------------- | ------------------------------------------------------- | --------------------------------------------------------------------- | -------------- | -| `!` | `ident!(...)`, `ident!{...}`, `ident![...]` | Macro expansion | | -| `!` | `!expr` | Bitwise or logical complement | `Not` | -| `!=` | `expr != expr` | Nonequality comparison | `PartialEq` | -| `%` | `expr % expr` | Arithmetic remainder | `Rem` | -| `%=` | `var %= expr` | Arithmetic remainder and assignment | `RemAssign` | -| `&` | `&expr`, `&mut expr` | Borrow | | -| `&` | `&type`, `&mut type`, `&'a type`, `&'a mut type` | Borrowed pointer type | | -| `&` | `expr & expr` | Bitwise AND | `BitAnd` | -| `&=` | `var &= expr` | Bitwise AND and assignment | `BitAndAssign` | -| `&&` | `expr && expr` | Short-circuiting logical AND | | -| `*` | `expr * expr` | Arithmetic multiplication | `Mul` | -| `*=` | `var *= expr` | Arithmetic multiplication and assignment | `MulAssign` | -| `*` | `*expr` | Dereference | `Deref` | -| `*` | `*const type`, `*mut type` | Raw pointer | | -| `+` | `trait + trait`, `'a + trait` | Compound type constraint | | -| `+` | `expr + expr` | Arithmetic addition | `Add` | -| `+=` | `var += expr` | Arithmetic addition and assignment | `AddAssign` | -| `,` | `expr, expr` | Argument and element separator | | -| `-` | `- expr` | Arithmetic negation | `Neg` | -| `-` | `expr - expr` | Arithmetic subtraction | `Sub` | -| `-=` | `var -= expr` | Arithmetic subtraction and assignment | `SubAssign` | -| `->` | `fn(...) -> type`, |...| -> type | Function and closure return type | | -| `.` | `expr.ident` | Field access | | -| `.` | `expr.ident(expr, ...)` | Method call | | -| `.` | `expr.0`, `expr.1`, etc. | Tuple indexing | | -| `..` | `..`, `expr..`, `..expr`, `expr..expr` | Right-exclusive range literal | `PartialOrd` | -| `..=` | `..=expr`, `expr..=expr` | Right-inclusive range literal | `PartialOrd` | -| `..` | `..expr` | Struct literal update syntax | | -| `..` | `variant(x, ..)`, `struct_type { x, .. }` | “And the rest” pattern binding | | -| `...` | `expr...expr` | (Deprecated, use `..=` instead) In a pattern: inclusive range pattern | | -| `/` | `expr / expr` | Arithmetic division | `Div` | -| `/=` | `var /= expr` | Arithmetic division and assignment | `DivAssign` | -| `:` | `pat: type`, `ident: type` | Constraints | | -| `:` | `ident: expr` | Struct field initializer | | -| `:` | `'a: loop {...}` | Loop label | | -| `;` | `expr;` | Statement and item terminator | | -| `;` | `[...; len]` | Part of fixed-size array syntax | | -| `<<` | `expr << expr` | Left-shift | `Shl` | -| `<<=` | `var <<= expr` | Left-shift and assignment | `ShlAssign` | -| `<` | `expr < expr` | Less than comparison | `PartialOrd` | -| `<=` | `expr <= expr` | Less than or equal to comparison | `PartialOrd` | -| `=` | `var = expr`, `ident = type` | Assignment/equivalence | | -| `==` | `expr == expr` | Equality comparison | `PartialEq` | -| `=>` | `pat => expr` | Part of match arm syntax | | -| `>` | `expr > expr` | Greater than comparison | `PartialOrd` | -| `>=` | `expr >= expr` | Greater than or equal to comparison | `PartialOrd` | -| `>>` | `expr >> expr` | Right-shift | `Shr` | -| `>>=` | `var >>= expr` | Right-shift and assignment | `ShrAssign` | -| `@` | `ident @ pat` | Pattern binding | | -| `^` | `expr ^ expr` | Bitwise exclusive OR | `BitXor` | -| `^=` | `var ^= expr` | Bitwise exclusive OR and assignment | `BitXorAssign` | -| | | pat | pat | Pattern alternatives | | -| | | expr | expr | Bitwise OR | `BitOr` | -| |= | var |= expr | Bitwise OR and assignment | `BitOrAssign` | -| || | expr || expr | Short-circuiting logical OR | | -| `?` | `expr?` | Error propagation | | - -### Non-operator Symbols - -The following list contains all symbols that don’t function as operators; that -is, they don’t behave like a function or method call. - -Table B-2 shows symbols that appear on their own and are valid in a variety of -locations. - -Table B-2: Stand-Alone Syntax - -| Symbol | Explanation | -| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | -| `'ident` | Named lifetime or loop label | -| Digits immediately followed by `u8`, `i32`, `f64`, `usize`, and so on | Numeric literal of specific type | -| `"..."` | String literal | -| `r"..."`, `r#"..."#`, `r##"..."##`, etc. | Raw string literal, escape characters not processed | -| `b"..."` | Byte string literal; constructs an array of bytes instead of a string | -| `br"..."`, `br#"..."#`, `br##"..."##`, etc. | Raw byte string literal, combination of raw and byte string literal | -| `'...'` | Character literal | -| `b'...'` | ASCII byte literal | -| |...| expr | Closure | -| `!` | Always empty bottom type for diverging functions | -| `_` | “Ignored” pattern binding; also used to make integer literals readable | - -Table B-3 shows symbols that appear in the context of a path through the module -hierarchy to an item. - -Table B-3: Path-Related Syntax - -| Symbol | Explanation | -| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| `ident::ident` | Namespace path | -| `::path` | Path relative to the extern prelude, where all other crates are rooted (i.e., an explicitly absolute path including crate name) | -| `self::path` | Path relative to the current module (i.e., an explicitly relative path). | -| `super::path` | Path relative to the parent of the current module | -| `type::ident`, `::ident` | Associated constants, functions, and types | -| `::...` | Associated item for a type that cannot be directly named (e.g., `<&T>::...`, `<[T]>::...`, etc.) | -| `trait::method(...)` | Disambiguating a method call by naming the trait that defines it | -| `type::method(...)` | Disambiguating a method call by naming the type for which it’s defined | -| `::method(...)` | Disambiguating a method call by naming the trait and type | - -Table B-4 shows symbols that appear in the context of using generic type -parameters. - -Table B-4: Generics - -| Symbol | Explanation | -| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | -| `path<...>` | Specifies parameters to generic type in a type (e.g., `Vec`) | -| `path::<...>`, `method::<...>` | Specifies parameters to generic type, function, or method in an expression; often referred to as turbofish (e.g., `"42".parse::()`) | -| `fn ident<...> ...` | Define generic function | -| `struct ident<...> ...` | Define generic structure | -| `enum ident<...> ...` | Define generic enumeration | -| `impl<...> ...` | Define generic implementation | -| `for<...> type` | Higher-ranked lifetime bounds | -| `type` | A generic type where one or more associated types have specific assignments (e.g., `Iterator`) | - -Table B-5 shows symbols that appear in the context of constraining generic type -parameters with trait bounds. - -Table B-5: Trait Bound Constraints - -| Symbol | Explanation | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -| `T: U` | Generic parameter `T` constrained to types that implement `U` | -| `T: 'a` | Generic type `T` must outlive lifetime `'a` (meaning the type cannot transitively contain any references with lifetimes shorter than `'a`) | -| `T: 'static` | Generic type `T` contains no borrowed references other than `'static` ones | -| `'b: 'a` | Generic lifetime `'b` must outlive lifetime `'a` | -| `T: ?Sized` | Allow generic type parameter to be a dynamically sized type | -| `'a + trait`, `trait + trait` | Compound type constraint | - -Table B-6 shows symbols that appear in the context of calling or defining -macros and specifying attributes on an item. - -Table B-6: Macros and Attributes - -| Symbol | Explanation | +## ضمیمه ب: عملگرها و نمادها + +این ضمیمه شامل واژه‌نامه‌ای از سینتکس زبان Rust است، از جمله عملگرها و سایر نمادهایی که به تنهایی یا در زمینه مسیرها، جنریک‌ها، محدودیت‌های ویژگی، ماکروها، ویژگی‌ها، نظرات، تاپل‌ها و براکت‌ها ظاهر می‌شوند. + +### عملگرها + +جدول B-1 عملگرهای موجود در Rust، یک مثال از چگونگی ظاهر شدن عملگر در زمینه، توضیح کوتاه و اینکه آیا آن عملگر قابل اضافه‌بارگذاری است یا نه را نشان می‌دهد. اگر یک عملگر قابل اضافه‌بارگذاری باشد، ویژگی مرتبط برای اضافه‌بارگذاری آن عملگر ذکر شده است. + +جدول B-1: عملگرها + +| عملگر | مثال | توضیح | قابلیت بارگذاری مجدد (Overload)? | | | | | +| ----- | ------------------------------------------------ | -------------------------------------------------------------- | -------------------------------- | --------------------------------------- | ------------- | --------------------- | - | +| `!` | `ident!(...)`, `ident!{...}`, `ident![...]` | گسترش ماکرو | | | | | | +| `!` | `!expr` | مکمل منطقی یا بیتی | `Not` | | | | | +| `!=` | `expr != expr` | مقایسه نابرابری | `PartialEq` | | | | | +| `%` | `expr % expr` | باقی‌مانده تقسیم | `Rem` | | | | | +| `%=` | `var %= expr` | باقی‌مانده تقسیم و اختصاص | `RemAssign` | | | | | +| `&` | `&expr`, `&mut expr` | قرض‌گیری | | | | | | +| `&` | `&type`, `&mut type`, `&'a type`, `&'a mut type` | نوع اشاره‌گر قرض‌گرفته‌شده | | | | | | +| `&` | `expr & expr` | AND بیتی | `BitAnd` | | | | | +| `&=` | `var &= expr` | AND بیتی و اختصاص | `BitAndAssign` | | | | | +| `&&` | `expr && expr` | AND منطقی با قطع کوتاه | | | | | | +| `*` | `expr * expr` | ضرب عددی | `Mul` | | | | | +| `*=` | `var *= expr` | ضرب عددی و اختصاص | `MulAssign` | | | | | +| `*` | `*expr` | dereference | `Deref` | | | | | +| `*` | `*const type`, `*mut type` | اشاره‌گر خام | | | | | | +| `+` | `trait + trait`, `'a + trait` | محدودیت ترکیبی برای نوع | | | | | | +| `+` | `expr + expr` | جمع عددی | `Add` | | | | | +| `+=` | `var += expr` | جمع عددی و اختصاص | `AddAssign` | | | | | +| `,` | `expr, expr` | جداکننده آرگومان یا عنصر | | | | | | +| `-` | `- expr` | منفی کردن عدد | `Neg` | | | | | +| `-` | `expr - expr` | تفریق عددی | `Sub` | | | | | +| `-=` | `var -= expr` | تفریق عددی و اختصاص | `SubAssign` | | | | | +| `->` | `fn(...) -> type`, ` | ... | -> type` | نوع بازگشتی تابع یا closure | | | | +| `.` | `expr.ident` | دسترسی به فیلد | | | | | | +| `.` | `expr.ident(expr, ...)` | فراخوانی متد | | | | | | +| `.` | `expr.0`, `expr.1`, ... | ایندکس‌گذاری tuple | | | | | | +| `..` | `..`, `expr..`, `..expr`, `expr..expr` | بازه‌ی راست-باز | `PartialOrd` | | | | | +| `..=` | `..=expr`, `expr..=expr` | بازه‌ی راست-بسته | `PartialOrd` | | | | | +| `..` | `..expr` | به‌روزرسانی literal ساختار | | | | | | +| `..` | `variant(x, ..)`, `struct_type { x, .. }` | الگوی «و بقیه» در pattern binding | | | | | | +| `...` | `expr...expr` | (منسوخ‌شده، به‌جای آن از `..=` استفاده کنید) الگوی بازه‌ی بسته | | | | | | +| `/` | `expr / expr` | تقسیم عددی | `Div` | | | | | +| `/=` | `var /= expr` | تقسیم عددی و اختصاص | `DivAssign` | | | | | +| `:` | `pat: type`, `ident: type` | محدودیت نوع | | | | | | +| `:` | `ident: expr` | مقداردهی به فیلد ساختار | | | | | | +| `:` | `'a: loop {...}` | برچسب حلقه | | | | | | +| `;` | `expr;` | پایان‌دهنده عبارت یا آیتم | | | | | | +| `;` | `[...; len]` | بخشی از نحوه تعریف آرایه با اندازه ثابت | | | | | | +| `<<` | `expr << expr` | شیفت به چپ بیتی | `Shl` | | | | | +| `<<=` | `var <<= expr` | شیفت به چپ بیتی و اختصاص | `ShlAssign` | | | | | +| `<` | `expr < expr` | مقایسه کوچکتر از | `PartialOrd` | | | | | +| `<=` | `expr <= expr` | مقایسه کوچکتر مساوی | `PartialOrd` | | | | | +| `=` | `var = expr`, `ident = type` | تخصیص یا تساوی | | | | | | +| `==` | `expr == expr` | مقایسه برابری | `PartialEq` | | | | | +| `=>` | `pat => expr` | بخشی از نگارش match | | | | | | +| `>` | `expr > expr` | مقایسه بزرگتر از | `PartialOrd` | | | | | +| `>=` | `expr >= expr` | مقایسه بزرگتر مساوی | `PartialOrd` | | | | | +| `>>` | `expr >> expr` | شیفت به راست بیتی | `Shr` | | | | | +| `>>=` | `var >>= expr` | شیفت به راست بیتی و اختصاص | `ShrAssign` | | | | | +| `@` | `ident @ pat` | pattern binding | | | | | | +| `^` | `expr ^ expr` | XOR بیتی | `BitXor` | | | | | +| `^=` | `var ^= expr` | XOR بیتی و اختصاص | `BitXorAssign` | | | | | +| ` | ` | `pat | pat` | جایگزین‌های الگو (pattern alternatives) | | | | +| ` | ` | `expr | expr` | OR بیتی | `BitOr` | | | +| ` | =` | `var | = expr` | OR بیتی و اختصاص | `BitOrAssign` | | | +| ` | | ` | `expr | | expr` | OR منطقی با قطع کوتاه | | +| `?` | `expr?` | انتشار خطا (error propagation) | | | | | | + + +### نمادهای غیرعملگری + +لیست زیر شامل تمام نمادهایی است که به عنوان عملگر عمل نمی‌کنند؛ یعنی مانند یک تابع یا فراخوانی متد رفتار نمی‌کنند. + +جدول B-2 نمادهایی را نشان می‌دهد که به تنهایی ظاهر می‌شوند و در مکان‌های مختلف معتبر هستند. + +جدول B-2: سینتکس مستقل + +| نماد | توضیح | | | +| -------------------------------------------------------------- | ------------------------------------------------------------------- | ----------- | ------- | +| `'ident` | lifetime نام‌گذاری‌شده یا برچسب حلقه | | | +| اعداد به‌همراه پسوندهایی مثل `u8`، `i32`، `f64`، `usize` و ... | عدد litteral با نوع مشخص | | | +| `"..."` | رشته litteral | | | +| `r"..."`، `r#"..."#`، `r##"..."##` و غیره | رشته خام؛ کاراکترهای escape تفسیر نمی‌شوند | | | +| `b"..."` | رشته byte؛ آرایه‌ای از بایت می‌سازد به‌جای رشته | | | +| `br"..."`، `br#"..."#`، `br##"..."##` و غیره | رشته byte خام؛ ترکیبی از رشته byte و رشته خام | | | +| `'...'` | litteral کاراکتری | | | +| `b'...'` | litteral بایت ASCII | | | +| | ... | expr | closure | +| `!` | نوع تهی همواره خالی برای توابع واگرا (diverging) | | | +| `_` | الگوی "نادیده‌گرفته‌شده"؛ همچنین برای خوانایی بهتر litteralهای عددی | | | + +جدول B-3 نمادهایی را نشان می‌دهد که در زمینه مسیریابی از سلسله‌مراتب ماژول به یک آیتم ظاهر می‌شوند. + +جدول B-3: سینتکس مرتبط با مسیر + +| نماد | توضیح | +| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `ident::ident` | مسیر فضای نام | +| `::path` | مسیر نسبی به پیش‌لود خارجی، جایی که تمام جعبه‌ها (crates)ی دیگر ریشه دارند (یعنی یک مسیر مطلق که به وضوح شامل نام جعبه (crate) است) | +| `self::path` | مسیر نسبی به ماژول جاری (یعنی یک مسیر نسبی به وضوح مشخص‌شده). | +| `super::path` | مسیر نسبی به والد ماژول جاری | +| `type::ident`, `::ident` | ثابت‌ها، توابع، و انواع مرتبط | +| `::...` | آیتم مرتبط برای نوعی که نمی‌توان به طور مستقیم آن را نام‌گذاری کرد (مثلاً `<&T>::...`، `<[T]>::...`، و غیره) | +| `trait::method(...)` | مشخص کردن فراخوانی متد با نام‌گذاری ویژگی‌ای که آن را تعریف کرده است | +| `type::method(...)` | مشخص کردن فراخوانی متد با نام‌گذاری نوعی که برای آن تعریف شده است | +| `::method(...)` | مشخص کردن فراخوانی متد با نام‌گذاری ویژگی و نوع | + +جدول B-4 نمادهایی را نشان می‌دهد که در زمینه استفاده از پارامترهای نوع جنریک ظاهر می‌شوند. + +جدول B-4: جنریک‌ها + +| نماد | توضیح | +| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `path<...>` | مشخص کردن پارامترها برای نوع جنریک در یک نوع (مثلاً `Vec`) | +| `path::<...>`, `method::<...>`| مشخص کردن پارامترها برای نوع جنریک، تابع، یا متد در یک عبارت؛ که معمولاً به آن turbofish می‌گویند (مثلاً `"42".parse::()`) | +| `fn ident<...> ...` | تعریف تابع جنریک | +| `struct ident<...> ...` | تعریف ساختار جنریک | +| `enum ident<...> ...` | تعریف شمارش جنریک | +| `impl<...> ...` | تعریف پیاده‌سازی جنریک | +| `for<...> type` | محدودیت طول عمر با رتبه بالاتر | +| `type` | نوع جنریک که یک یا چند نوع مرتبط با آن دارای مقادیر مشخصی هستند (مثلاً `Iterator`) | + +جدول B-5 نمادهایی را نشان می‌دهد که در زمینه محدود کردن پارامترهای نوع جنریک با محدودیت‌های ویژگی ظاهر می‌شوند. + +جدول B-5: محدودیت‌های ویژگی + + +| نماد | توضیح | +| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `T: U` | پارامتر جنریک `T` محدود به انواع که `U` را پیاده‌سازی می‌کنند | +| `T: 'a` | نوع جنریک `T` باید طول عمر بیشتری از طول عمر `'a` داشته باشد (یعنی نوع نمی‌تواند به صورت گذرا شامل ارجاعاتی با طول عمر کوتاه‌تر از `'a` باشد) | +| `T: 'static` | نوع جنریک `T` شامل ارجاعات قرض‌گرفته‌شده‌ای به جز ارجاعات `'static` نیست | +| `'b: 'a` | طول عمر جنریک `'b` باید طول عمر بیشتری از طول عمر `'a` داشته باشد | +| `T: ?Sized` | اجازه دادن به پارامتر نوع جنریک برای اینکه نوعی با اندازه پویا باشد | +| `'a + trait`, `trait + trait` | محدودیت نوع ترکیبی | + +جدول B-6: ماکروها و ویژگی‌ها + +| نماد | توضیح | | ------------------------------------------- | ------------------ | -| `#[meta]` | Outer attribute | -| `#![meta]` | Inner attribute | -| `$ident` | Macro substitution | -| `$ident:kind` | Macro metavariable | -| `$(...)...` | Macro repetition | -| `ident!(...)`, `ident!{...}`, `ident![...]` | Macro invocation | - -Table B-7 shows symbols that create comments. - -Table B-7: Comments - -| Symbol | Explanation | -| ---------- | ----------------------- | -| `//` | Line comment | -| `//!` | Inner line doc comment | -| `///` | Outer line doc comment | -| `/*...*/` | Block comment | -| `/*!...*/` | Inner block doc comment | -| `/**...*/` | Outer block doc comment | - -Table B-8 shows the contexts in which parentheses are used. - -Table B-8: Parentheses - -| Symbol | Explanation | -| ------------------------ | ------------------------------------------------------------------------------------------- | -| `()` | Empty tuple (aka unit), both literal and type | -| `(expr)` | Parenthesized expression | -| `(expr,)` | Single-element tuple expression | -| `(type,)` | Single-element tuple type | -| `(expr, ...)` | Tuple expression | -| `(type, ...)` | Tuple type | -| `expr(expr, ...)` | Function call expression; also used to initialize tuple `struct`s and tuple `enum` variants | - -Table B-9 shows the contexts in which curly braces are used. - -Table B-9: Curly Brackets - -| Context | Explanation | -| ------------ | ---------------- | -| `{...}` | Block expression | -| `Type {...}` | Struct literal | - -Table B-10 shows the contexts in which square brackets are used. - -Table B-10: Square Brackets - -| Context | Explanation | -| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| `[...]` | Array literal | -| `[expr; len]` | Array literal containing `len` copies of `expr` | -| `[type; len]` | Array type containing `len` instances of `type` | -| `expr[expr]` | Collection indexing. Overloadable (`Index`, `IndexMut`) | -| `expr[..]`, `expr[a..]`, `expr[..b]`, `expr[a..b]` | Collection indexing pretending to be collection slicing, using `Range`, `RangeFrom`, `RangeTo`, or `RangeFull` as the “index” | +| `#[meta]` | attribute بیرونی | +| `#![meta]` | attribute درونی | +| `$ident` | جای‌گذاری در ماکرو | +| `$ident:kind` | metavariable ماکرو | +| `$(...)...` | تکرار در ماکرو | +| `ident!(...)`، `ident!{...}`، `ident![...]` | فراخوانی ماکرو | + +جدول B-7: نظرات + +| نماد | توضیح | +| ----------- | ----------------------- | +| `//` | نظر تک‌خطی | +| `//!` | نظر مستند داخلی تک‌خطی | +| `///` | نظر مستند خارجی تک‌خطی | +| `/*...*/` | نظر بلوکی | +| `/*!...*/` | نظر مستند داخلی بلوکی | +| `/**...*/` | نظر مستند خارجی بلوکی | + +جدول B-8: تاپل‌ها + +جدول B-8 زمینه‌هایی را نشان می‌دهد که در آن‌ها از پرانتز استفاده می‌شود. + +جدول B-8: پرانتزها + +| نماد | توضیح | +| ----------------- | ------------------------------------------------------------------------------------------- | +| `()` | tuple تهی (یا همان unit)، هم به‌صورت litteral و هم به‌صورت نوع | +| `(expr)` | عبارت داخل پرانتز | +| `(expr,)` | عبارت tuple تک‌عضوی | +| `(type,)` | نوع tuple تک‌عضوی | +| `(expr, ...)` | عبارت tuple | +| `(type, ...)` | نوع tuple | +| `expr(expr, ...)` | فراخوانی تابع؛ همچنین برای مقداردهی به tuple structها و variantهای tuple enum به‌کار می‌رود | + +جدول B-10: براکت‌ها + +| زمینه | توضیح | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| `[...]` | لیترال آرایه | +| `[expr; len]` | لیترال آرایه که شامل `len` نسخه از `expr` است | +| `[type; len]` | نوع آرایه که شامل `len` نمونه از `type` است | +| `expr[expr]` | اندیس‌گذاری مجموعه. قابل اضافه‌بارگذاری (`Index`, `IndexMut`) | +| `expr[..]`, `expr[a..]`, `expr[..b]`, `expr[a..b]` | اندیس‌گذاری مجموعه که شبیه به برش مجموعه عمل می‌کند، با استفاده از `Range`، `RangeFrom`، `RangeTo`، یا `RangeFull` به عنوان "اندیس" | + +| زمینه | توضیح | +| ------------ | -------------- | +| `{...}` | عبارت block | +| `Type {...}` | literal ساختار | + +جدول B-10 زمینه‌هایی را نشان می‌دهد که در آن‌ها از کروشه (براکت مربعی) استفاده می‌شود. + +جدول B-10: کروشه‌ها (براکت‌های مربعی) + +| زمینه | توضیح | +| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| `[...]` | literal آرایه | +| `[expr; len]` | literal آرایه شامل `len` نسخه از `expr` | +| `[type; len]` | نوع آرایه شامل `len` نمونه از `type` | +| `expr[expr]` | ایندکس‌گذاری روی مجموعه‌ها. قابل بارگذاری مجدد (`Index`، `IndexMut`) | +| `expr[..]`، `expr[a..]`، `expr[..b]`، `expr[a..b]` | ایندکس‌گذاری روی مجموعه‌ها به‌شکل شبیه‌سازی‌شده‌ی برش (slicing)، با استفاده از `Range`، `RangeFrom`، `RangeTo`، یا `RangeFull` | diff --git a/src/appendix-03-derivable-traits.md b/src/appendix-03-derivable-traits.md index 8e74b948c2..478630bbec 100644 --- a/src/appendix-03-derivable-traits.md +++ b/src/appendix-03-derivable-traits.md @@ -1,183 +1,131 @@ -## Appendix C: Derivable Traits - -In various places in the book, we’ve discussed the `derive` attribute, which -you can apply to a struct or enum definition. The `derive` attribute generates -code that will implement a trait with its own default implementation on the -type you’ve annotated with the `derive` syntax. - -In this appendix, we provide a reference of all the traits in the standard -library that you can use with `derive`. Each section covers: - -- What operators and methods deriving this trait will enable -- What the implementation of the trait provided by `derive` does -- What implementing the trait signifies about the type -- The conditions in which you’re allowed or not allowed to implement the trait -- Examples of operations that require the trait - -If you want different behavior from that provided by the `derive` attribute, -consult the [standard library documentation](../std/index.html) -for each trait for details on how to manually implement them. - -The traits listed here are the only ones defined by the standard library that -can be implemented on your types using `derive`. Other traits defined in the -standard library don’t have sensible default behavior, so it’s up to you to -implement them in the way that makes sense for what you’re trying to accomplish. - -An example of a trait that can’t be derived is `Display`, which handles -formatting for end users. You should always consider the appropriate way to -display a type to an end user. What parts of the type should an end user be -allowed to see? What parts would they find relevant? What format of the data -would be most relevant to them? The Rust compiler doesn’t have this insight, so -it can’t provide appropriate default behavior for you. - -The list of derivable traits provided in this appendix is not comprehensive: -libraries can implement `derive` for their own traits, making the list of -traits you can use `derive` with truly open-ended. Implementing `derive` -involves using a procedural macro, which is covered in the -[“Macros”][macros] section of Chapter 20. - -### `Debug` for Programmer Output - -The `Debug` trait enables debug formatting in format strings, which you -indicate by adding `:?` within `{}` placeholders. - -The `Debug` trait allows you to print instances of a type for debugging -purposes, so you and other programmers using your type can inspect an instance -at a particular point in a program’s execution. - -The `Debug` trait is required, for example, in the use of the `assert_eq!` -macro. This macro prints the values of instances given as arguments if the -equality assertion fails so programmers can see why the two instances weren’t -equal. - -### `PartialEq` and `Eq` for Equality Comparisons - -The `PartialEq` trait allows you to compare instances of a type to check for -equality and enables use of the `==` and `!=` operators. - -Deriving `PartialEq` implements the `eq` method. When `PartialEq` is derived on -structs, two instances are equal only if _all_ fields are equal, and the -instances are not equal if any fields are not equal. When derived on enums, -each variant is equal to itself and not equal to the other variants. - -The `PartialEq` trait is required, for example, with the use of the -`assert_eq!` macro, which needs to be able to compare two instances of a type -for equality. - -The `Eq` trait has no methods. Its purpose is to signal that for every value of -the annotated type, the value is equal to itself. The `Eq` trait can only be -applied to types that also implement `PartialEq`, although not all types that -implement `PartialEq` can implement `Eq`. One example of this is floating point -number types: the implementation of floating point numbers states that two -instances of the not-a-number (`NaN`) value are not equal to each other. - -An example of when `Eq` is required is for keys in a `HashMap` so the -`HashMap` can tell whether two keys are the same. - -### `PartialOrd` and `Ord` for Ordering Comparisons - -The `PartialOrd` trait allows you to compare instances of a type for sorting -purposes. A type that implements `PartialOrd` can be used with the `<`, `>`, -`<=`, and `>=` operators. You can only apply the `PartialOrd` trait to types -that also implement `PartialEq`. - -Deriving `PartialOrd` implements the `partial_cmp` method, which returns an -`Option` that will be `None` when the values given don’t produce an -ordering. An example of a value that doesn’t produce an ordering, even though -most values of that type can be compared, is the not-a-number (`NaN`) floating -point value. Calling `partial_cmp` with any floating-point number and the `NaN` -floating-point value will return `None`. - -When derived on structs, `PartialOrd` compares two instances by comparing the -value in each field in the order in which the fields appear in the struct -definition. When derived on enums, variants of the enum declared earlier in the -enum definition are considered less than the variants listed later. - -The `PartialOrd` trait is required, for example, for the `gen_range` method -from the `rand` crate that generates a random value in the range specified by a -range expression. - -The `Ord` trait allows you to know that for any two values of the annotated -type, a valid ordering will exist. The `Ord` trait implements the `cmp` method, -which returns an `Ordering` rather than an `Option` because a valid -ordering will always be possible. You can only apply the `Ord` trait to types -that also implement `PartialOrd` and `Eq` (and `Eq` requires `PartialEq`). When -derived on structs and enums, `cmp` behaves the same way as the derived -implementation for `partial_cmp` does with `PartialOrd`. - -An example of when `Ord` is required is when storing values in a `BTreeSet`, -a data structure that stores data based on the sort order of the values. - -### `Clone` and `Copy` for Duplicating Values - -The `Clone` trait allows you to explicitly create a deep copy of a value, and -the duplication process might involve running arbitrary code and copying heap -data. See [Variables and Data Interacting with -Clone”][variables-and-data-interacting-with-clone] in Chapter 4 -for more information on `Clone`. - -Deriving `Clone` implements the `clone` method, which when implemented for the -whole type, calls `clone` on each of the parts of the type. This means all the -fields or values in the type must also implement `Clone` to derive `Clone`. - -An example of when `Clone` is required is when calling the `to_vec` method on a -slice. The slice doesn’t own the type instances it contains, but the vector -returned from `to_vec` will need to own its instances, so `to_vec` calls -`clone` on each item. Thus the type stored in the slice must implement `Clone`. - -The `Copy` trait allows you to duplicate a value by only copying bits stored on -the stack; no arbitrary code is necessary. See [“Stack-Only Data: -Copy”][stack-only-data-copy] in Chapter 4 for more information on -`Copy`. - -The `Copy` trait doesn’t define any methods to prevent programmers from -overloading those methods and violating the assumption that no arbitrary code -is being run. That way, all programmers can assume that copying a value will be -very fast. - -You can derive `Copy` on any type whose parts all implement `Copy`. A type that -implements `Copy` must also implement `Clone`, because a type that implements -`Copy` has a trivial implementation of `Clone` that performs the same task as -`Copy`. - -The `Copy` trait is rarely required; types that implement `Copy` have -optimizations available, meaning you don’t have to call `clone`, which makes -the code more concise. - -Everything possible with `Copy` you can also accomplish with `Clone`, but the -code might be slower or have to use `clone` in places. - -### `Hash` for Mapping a Value to a Value of Fixed Size - -The `Hash` trait allows you to take an instance of a type of arbitrary size and -map that instance to a value of fixed size using a hash function. Deriving -`Hash` implements the `hash` method. The derived implementation of the `hash` -method combines the result of calling `hash` on each of the parts of the type, -meaning all fields or values must also implement `Hash` to derive `Hash`. - -An example of when `Hash` is required is in storing keys in a `HashMap` -to store data efficiently. - -### `Default` for Default Values - -The `Default` trait allows you to create a default value for a type. Deriving -`Default` implements the `default` function. The derived implementation of the -`default` function calls the `default` function on each part of the type, -meaning all fields or values in the type must also implement `Default` to -derive `Default`. - -The `Default::default` function is commonly used in combination with the struct -update syntax discussed in [“Creating Instances from Other Instances with Struct -Update -Syntax”][creating-instances-from-other-instances-with-struct-update-syntax] in Chapter 5. You can customize a few fields of a struct and then set -and use a default value for the rest of the fields by using -`..Default::default()`. - -The `Default` trait is required when you use the method `unwrap_or_default` on -`Option` instances, for example. If the `Option` is `None`, the method -`unwrap_or_default` will return the result of `Default::default` for the type -`T` stored in the `Option`. +## ضمیمه ج: ویژگی‌های قابل اشتقاق + +در بخش‌های مختلف کتاب، ما درباره ویژگی `derive` صحبت کردیم که می‌توانید آن را به تعریف یک struct یا enum اعمال کنید. ویژگی `derive` کدی تولید می‌کند که یک ویژگی را با پیاده‌سازی پیش‌فرض خود روی نوعی که با سینتکس `derive` حاشیه‌نویسی کرده‌اید، پیاده‌سازی می‌کند. + +در این ضمیمه، مرجعی از تمام ویژگی‌های موجود در کتابخانه استاندارد که می‌توانید با `derive` استفاده کنید ارائه می‌شود. هر بخش شامل موارد زیر است: + +- چه عملگرها و متدهایی با مشتق‌سازی این ویژگی فعال می‌شوند +- پیاده‌سازی ویژگی که توسط `derive` ارائه می‌شود چه می‌کند +- پیاده‌سازی ویژگی چه مفهومی درباره نوع دارد +- شرایطی که در آن اجازه یا عدم اجازه پیاده‌سازی ویژگی داده می‌شود +- مثال‌هایی از عملیات‌هایی که به این ویژگی نیاز دارند + +اگر رفتاری متفاوت از آنچه ویژگی `derive` ارائه می‌دهد +می‌خواهید، به [مستندات کتابخانه استاندارد](https://doc.rust-lang.org/std/index.html) +برای هر trait مراجعه کنید تا جزئیات نحوه‌ی پیاده‌سازی +دستی آن‌ها را ببینید. + +traitهایی که در این‌جا فهرست شده‌اند، تنها traitهایی +هستند که توسط کتابخانه استاندارد تعریف شده‌اند +و می‌توان آن‌ها را با استفاده از `derive` روی نوع‌های +خود پیاده‌سازی کرد. سایر traitهای موجود در کتابخانه +استاندارد رفتاری پیش‌فرض و معنادار ندارند، بنابراین +پیاده‌سازی آن‌ها به‌صورتی که با هدف شما سازگار باشد +برعهده‌ی خودتان است. + +مثالی از یک ویژگی که نمی‌تواند مشتق شود، `Display` است که فرمت‌دهی برای کاربران نهایی را مدیریت می‌کند. شما باید همیشه راه مناسب برای نمایش یک نوع به کاربر نهایی را در نظر بگیرید. چه بخش‌هایی از نوع باید به کاربر نهایی نشان داده شود؟ چه بخش‌هایی برای او مرتبط است؟ چه فرمتی از داده برای او بیشترین اهمیت را دارد؟ کامپایلر Rust این بینش را ندارد، بنابراین نمی‌تواند رفتار پیش‌فرض مناسب را برای شما فراهم کند. + +لیست ویژگی‌های قابل اشتقاق ارائه‌شده در این ضمیمه جامع نیست: کتابخانه‌ها می‌توانند `derive` را برای ویژگی‌های خود پیاده‌سازی کنند و لیست ویژگی‌هایی که می‌توانید با `derive` استفاده کنید را به‌طور واقعی باز بگذارند. پیاده‌سازی `derive` شامل استفاده از یک ماکروی فرآیندی است که در بخش [“ماکروها”][macros] از فصل 20 پوشش داده شده است. + +### `Debug` برای خروجی برنامه‌نویسی + +ویژگی `Debug` فرمت‌دهی دیباگ را در رشته‌های فرمت فعال می‌کند که با افزودن `:?` درون نگه‌دارنده‌های `{}` مشخص می‌کنید. + +ویژگی `Debug` به شما اجازه می‌دهد نمونه‌هایی از یک نوع را برای مقاصد دیباگ چاپ کنید، به‌طوری‌که شما و سایر برنامه‌نویسانی که از نوع شما استفاده می‌کنند بتوانید نمونه‌ای را در یک نقطه خاص از اجرای برنامه بررسی کنید. + +برای مثال، trait `Debug` در استفاده از ماکروی +`assert_eq!` الزامی است. این ماکرو در صورتی +که بررسی تساوی با شکست مواجه شود، مقادیر +نمونه‌هایی را که به‌عنوان آرگومان دریافت کرده +است چاپ می‌کند تا برنامه‌نویس بتواند دلیل نابرابری +دو نمونه را مشاهده کند. + +### `PartialEq` و `Eq` برای مقایسه برابری + +ویژگی `PartialEq` به شما اجازه می‌دهد نمونه‌های یک نوع را برای بررسی برابری مقایسه کنید و استفاده از عملگرهای `==` و `!=` را ممکن می‌سازد. + +مشتق‌سازی `PartialEq` متد `eq` را پیاده‌سازی می‌کند. وقتی `PartialEq` روی struct‌ها مشتق می‌شود، دو نمونه فقط زمانی برابر هستند که _تمام_ فیلدها برابر باشند و نمونه‌ها برابر نیستند اگر هر یک از فیلدها برابر نباشند. وقتی روی enum‌ها مشتق می‌شود، هر واریانت با خودش برابر است و با سایر واریانت‌ها برابر نیست. + +ویژگی `PartialEq`، برای مثال، با استفاده از ماکروی `assert_eq!` مورد نیاز است که باید بتواند دو نمونه از یک نوع را برای برابری مقایسه کند. + +ویژگی `Eq` هیچ متدی ندارد. هدف آن این است که نشان دهد برای هر مقدار از نوع حاشیه‌نویسی‌شده، مقدار با خودش برابر است. ویژگی `Eq` فقط می‌تواند به نوع‌هایی اعمال شود که همچنین `PartialEq` را پیاده‌سازی کرده باشند، اگرچه همه نوع‌هایی که `PartialEq` را پیاده‌سازی کرده‌اند نمی‌توانند `Eq` را پیاده‌سازی کنند. مثالی از این مورد نوع‌های عدد ممیز شناور هستند: پیاده‌سازی اعداد ممیز شناور بیان می‌کند که دو نمونه از مقدار غیرعدد (`NaN`) برابر نیستند. + +مثالی از زمانی که `Eq` مورد نیاز است، برای کلیدها در `HashMap` است تا `HashMap` بتواند تعیین کند که آیا دو کلید یکسان هستند یا نه. + + +### `PartialOrd` و `Ord` برای مقایسه مرتب‌سازی + +مشتق‌گیری از `PartialOrd` باعث پیاده‌سازی +متد `partial_cmp` می‌شود، که یک `Option` +بازمی‌گرداند؛ این مقدار در صورتی `None` خواهد بود +که مقادیر داده‌شده نتوانند ترتیب مشخصی تولید کنند. +مثالی از مقداری که ترتیب‌پذیر نیست، هرچند بیشتر +مقادیر آن نوع قابل مقایسه‌اند، مقدار `NaN` در +اعداد اعشاری (floating point) است. فراخوانی +`partial_cmp` با هر عدد اعشاری و مقدار `NaN` +منجر به بازگشت `None` می‌شود. + +مشتق‌سازی `PartialOrd` متد `partial_cmp` را پیاده‌سازی می‌کند، که یک `Option` را برمی‌گرداند که در صورتی که مقادیر داده‌شده ترتیب‌بندی تولید نکنند، `None` خواهد بود. مثالی از مقداری که ترتیب‌بندی تولید نمی‌کند، حتی اگر بیشتر مقادیر آن نوع قابل مقایسه باشند، مقدار نقطه شناور غیرعدد (`NaN`) است. فراخوانی `partial_cmp` با هر عدد شناور و مقدار `NaN` نقطه شناور `None` را برمی‌گرداند. + +وقتی روی struct‌ها مشتق می‌شود، `PartialOrd` دو نمونه را با مقایسه مقدار هر فیلد به ترتیب ظاهر شدن فیلدها در تعریف struct مقایسه می‌کند. وقتی روی enum‌ها مشتق می‌شود، واریانت‌های enum که زودتر در تعریف enum اعلام شده‌اند، کمتر از واریانت‌هایی در نظر گرفته می‌شوند که بعداً فهرست شده‌اند. + +ویژگی `PartialOrd`، برای مثال، برای متد `gen_range` از crate `rand` مورد نیاز است که یک مقدار تصادفی در محدوده مشخص‌شده توسط یک عبارت محدوده تولید می‌کند. + +ویژگی `Ord` به شما امکان می‌دهد بدانید که برای هر دو مقدار از نوع حاشیه‌نویسی‌شده، یک ترتیب‌بندی معتبر وجود خواهد داشت. ویژگی `Ord` متد `cmp` را پیاده‌سازی می‌کند، که به جای `Option`، یک `Ordering` را برمی‌گرداند زیرا یک ترتیب‌بندی معتبر همیشه ممکن خواهد بود. شما فقط می‌توانید ویژگی `Ord` را به نوع‌هایی اعمال کنید که همچنین `PartialOrd` و `Eq` را پیاده‌سازی کرده باشند (و `Eq` نیازمند `PartialEq` است). وقتی روی struct‌ها و enum‌ها مشتق می‌شود، `cmp` به همان شکلی عمل می‌کند که پیاده‌سازی مشتق‌شده برای `partial_cmp` در `PartialOrd` عمل می‌کند. + +مثالی از زمانی که `Ord` مورد نیاز است، هنگام ذخیره مقادیر در `BTreeSet` است، یک ساختار داده که داده‌ها را بر اساس ترتیب مرتب‌سازی مقادیر ذخیره می‌کند. + +trait `Clone` به شما امکان می‌دهد که به‌صورت +صریح یک کپی عمیق از یک مقدار ایجاد کنید، +و این فرایند تکثیر ممکن است شامل اجرای کد دلخواه +و کپی‌کردن داده‌ها از حافظه heap باشد. برای اطلاعات +بیشتر درباره‌ی `Clone` به بخش [«متغیرها و داده‌ها در تعامل با Clone»][variables-and-data-interacting-with-clone] +در فصل ۴ مراجعه کنید. + +مثالی از جایی که `Clone` مورد نیاز است، هنگام +فراخوانی متد `to_vec` روی یک slice می‌باشد. +slice مالک نمونه‌های نوعی که در خود دارد نیست، +اما برداری که از `to_vec` بازمی‌گردد باید مالک +این نمونه‌ها باشد، بنابراین `to_vec` روی هر آیتم +تابع `clone` را فراخوانی می‌کند. از این رو، نوعی +که درون slice ذخیره شده باید trait `Clone` را +پیاده‌سازی کرده باشد. + +trait `Copy` به شما اجازه می‌دهد که یک مقدار را +تنها با کپی‌کردن بیت‌های ذخیره‌شده در stack +تکثیر کنید؛ هیچ کد دلخواهی اجرا نمی‌شود. +برای اطلاعات بیشتر درباره‌ی `Copy` به بخش +[«داده‌های فقط-روی-استک: Copy»][stack-only-data-copy] +در فصل ۴ مراجعه کنید. + +ویژگی `Copy` به شما امکان می‌دهد یک مقدار را با کپی کردن بیت‌های ذخیره‌شده روی stack تکثیر کنید؛ هیچ کد دلخواهی لازم نیست. برای اطلاعات بیشتر درباره `Copy`، به بخش [“داده‌های فقط stack: Copy”][stack-only-data-copy] در فصل 4 مراجعه کنید. + +ویژگی `Copy` هیچ متدی را تعریف نمی‌کند تا از اضافه‌بارگذاری آن متدها توسط برنامه‌نویسان و نقض فرضی که هیچ کد دلخواهی اجرا نمی‌شود جلوگیری کند. به این ترتیب، تمام برنامه‌نویسان می‌توانند فرض کنند که کپی کردن یک مقدار بسیار سریع خواهد بود. + +شما می‌توانید `Copy` را روی هر نوعی مشتق کنید که تمام اجزای آن `Copy` را پیاده‌سازی می‌کنند. نوعی که `Copy` را پیاده‌سازی می‌کند باید همچنین `Clone` را پیاده‌سازی کند، زیرا نوعی که `Copy` را پیاده‌سازی می‌کند دارای پیاده‌سازی ساده‌ای از `Clone` است که همان وظیفه را به عنوان `Copy` انجام می‌دهد. + +ویژگی `Copy` به ندرت مورد نیاز است؛ نوع‌هایی که `Copy` را پیاده‌سازی می‌کنند بهینه‌سازی‌هایی در دسترس دارند، به این معنا که شما نیازی به فراخوانی `clone` ندارید، که کد را مختصرتر می‌کند. + +هر چیزی که با `Copy` ممکن است را می‌توانید با `Clone` نیز انجام دهید، اما کد ممکن است کندتر باشد یا نیاز به استفاده از `clone` در مکان‌های مختلف داشته باشد. + + +### `Hash` برای نگاشت مقدار به مقدار با اندازه ثابت + +ویژگی `Hash` به شما امکان می‌دهد یک نمونه از نوعی با اندازه دلخواه بگیرید و آن نمونه را با استفاده از یک تابع هش به مقدار با اندازه ثابت نگاشت کنید. مشتق‌سازی `Hash` متد `hash` را پیاده‌سازی می‌کند. پیاده‌سازی مشتق‌شده متد `hash` نتیجه فراخوانی `hash` روی هر یک از بخش‌های نوع را ترکیب می‌کند، به این معنی که تمام فیلدها یا مقادیر نیز باید `Hash` را پیاده‌سازی کنند تا `Hash` مشتق شود. + +مثالی از زمانی که `Hash` مورد نیاز است، هنگام ذخیره کلیدها در `HashMap` برای ذخیره داده‌ها به صورت کارآمد است. + +تابع `Default::default` معمولاً همراه با نگارش +به‌روزرسانی ساختار (struct update syntax) که در +بخش [«ایجاد نمونه‌هایی از نمونه‌های دیگر با استفاده از نگارش به‌روزرسانی ساختار»][creating-instances-from-other-instances-with-struct-update-syntax] +در فصل ۵ توضیح داده شده، استفاده می‌شود. +می‌توانید تنها چند فیلد از یک ساختار را شخصی‌سازی کنید +و سپس برای فیلدهای باقی‌مانده از مقدار پیش‌فرض +با استفاده از `..Default::default()` بهره ببرید. + +تابع `Default::default` معمولاً به همراه سینتکس به‌روزرسانی ساختار که در بخش [“ایجاد نمونه‌ها از نمونه‌های دیگر با سینتکس به‌روزرسانی ساختار”][creating-instances-from-other-instances-with-struct-update-syntax] در فصل 5 مورد بحث قرار گرفته است، استفاده می‌شود. می‌توانید چند فیلد از یک ساختار را سفارشی کنید و سپس یک مقدار پیش‌فرض برای بقیه فیلدها با استفاده از `..Default::default()` تنظیم و استفاده کنید. + +ویژگی `Default`، برای مثال، زمانی مورد نیاز است که از متد `unwrap_or_default` روی نمونه‌های `Option` استفاده می‌کنید. اگر `Option` برابر با `None` باشد، متد `unwrap_or_default` نتیجه `Default::default` را برای نوع `T` ذخیره‌شده در `Option` برمی‌گرداند. [creating-instances-from-other-instances-with-struct-update-syntax]: ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax [stack-only-data-copy]: ch04-01-what-is-ownership.html#stack-only-data-copy diff --git a/src/appendix-04-useful-development-tools.md b/src/appendix-04-useful-development-tools.md index 02dd6490cb..b2ef577b09 100644 --- a/src/appendix-04-useful-development-tools.md +++ b/src/appendix-04-useful-development-tools.md @@ -1,35 +1,35 @@ -## Appendix D - Useful Development Tools +## ضمیمه د - ابزارهای مفید توسعه -In this appendix, we talk about some useful development tools that the Rust -project provides. We’ll look at automatic formatting, quick ways to apply -warning fixes, a linter, and integrating with IDEs. +در این ضمیمه، ما درباره برخی ابزارهای مفید توسعه که پروژه Rust ارائه می‌دهد صحبت می‌کنیم. به فرمت‌دهی خودکار، روش‌های سریع برای اعمال اصلاحات هشدارها، یک تحلیلگر کد (linter) و یکپارچه‌سازی با محیط‌های توسعه یکپارچه (IDE) خواهیم پرداخت. -### Automatic Formatting with `rustfmt` +### فرمت‌دهی خودکار با `rustfmt` -The `rustfmt` tool reformats your code according to the community code style. -Many collaborative projects use `rustfmt` to prevent arguments about which -style to use when writing Rust: everyone formats their code using the tool. - -Rust installations include `rustfmt` by default, so you should already have the -programs `rustfmt` and `cargo-fmt` on your system. These two commands are -analogous to `rustc` and `cargo` in that `rustfmt` allows finer-grained control -and `cargo-fmt` understands conventions of a project that uses Cargo. To format -any Cargo project, enter the following: +نصب‌های Rust به‌صورت پیش‌فرض شامل `rustfmt` +هستند، بنابراین احتمالاً هم‌اکنون برنامه‌های +`rustfmt` و `cargo-fmt` روی سیستم شما +نصب شده‌اند. این دو دستور همانند `rustc` و `cargo` +هستند؛ به این صورت که `rustfmt` کنترل دقیق‌تری +ارائه می‌دهد و `cargo-fmt` با ساختار و قراردادهای +پروژه‌های مبتنی بر Cargo آشنایی دارد. برای قالب‌بندی +هر پروژه‌ی Cargo، دستور زیر را وارد کنید: ```console $ cargo fmt ``` -Running this command reformats all the Rust code in the current crate. This -should only change the code style, not the code semantics. For more information -on `rustfmt`, see [its documentation][rustfmt]. +اجرای این دستور تمام کدهای Rust در crate فعلی را مجدداً فرمت می‌کند. این کار باید فقط سبک کدنویسی را تغییر دهد، نه معنای کد را. برای اطلاعات بیشتر در مورد `rustfmt`، به [مستندات آن][rustfmt] مراجعه کنید. + +[rustfmt]: https://github.com/rust-lang/rustfmt -### Fix Your Code with `rustfix` +### اصلاح کد شما با `rustfix` -The `rustfix` tool is included with Rust installations and can automatically fix -compiler warnings that have a clear way to correct the problem that’s likely -what you want. You’ve probably seen compiler warnings before. For example, -consider this code: +ابزار `rustfix` همراه با نصب Rust ارائه می‌شود و +می‌تواند هشدارهای کامپایلر را به‌صورت خودکار +برطرف کند، آن هم در مواردی که راه‌حل مشخصی +برای رفع مشکل وجود دارد و احتمالاً همان چیزی است +که شما انتظار دارید. احتمالاً پیش از این با +هشدارهای کامپایلر روبه‌رو شده‌اید. برای مثال، +به کد زیر توجه کنید: Filename: src/main.rs @@ -40,8 +40,9 @@ fn main() { } ``` -Here, we’re defining the variable `x` as mutable, but we never actually mutate -it. Rust warns us about that: +در اینجا، متغیر `x` را به‌صورت قابل‌تغییر (mutable) +تعریف کرده‌ایم، اما در عمل هیچ‌گاه آن را تغییر نمی‌دهیم. +Rust در این مورد به ما هشدار می‌دهد: ```console $ cargo build @@ -57,9 +58,10 @@ warning: variable does not need to be mutable = note: `#[warn(unused_mut)]` on by default ``` -The warning suggests that we remove the `mut` keyword. We can automatically -apply that suggestion using the `rustfix` tool by running the command `cargo -fix`: +هشدار پیشنهاد می‌دهد که کلمه‌ی کلیدی `mut` +را حذف کنیم. می‌توانیم این پیشنهاد را به‌صورت +خودکار با استفاده از ابزار `rustfix` و اجرای دستور +`cargo fix` اعمال کنیم: ```console $ cargo fix @@ -68,8 +70,7 @@ $ cargo fix Finished dev [unoptimized + debuginfo] target(s) in 0.59s ``` -When we look at _src/main.rs_ again, we’ll see that `cargo fix` has changed the -code: +وقتی دوباره فایل _src/main.rs_ را بررسی کنیم، خواهیم دید که `cargo fix` کد را تغییر داده است: Filename: src/main.rs @@ -80,25 +81,25 @@ fn main() { } ``` -The variable `x` is now immutable, and the warning no longer appears. +متغیر `x` اکنون غیرقابل‌تغییر (immutable) شده است +و هشدار نیز دیگر نمایش داده نمی‌شود. -You can also use the `cargo fix` command to transition your code between -different Rust editions. Editions are covered in [Appendix E][editions]. +همچنین می‌توانید از دستور `cargo fix` برای انتقال کد خود بین نسخه‌های مختلف Rust استفاده کنید. نسخه‌ها در [ضمیمه ه][editions] پوشش داده شده‌اند. -### More Lints with Clippy +### لینت‌های بیشتر با Clippy -The Clippy tool is a collection of lints to analyze your code so you can catch -common mistakes and improve your Rust code. Clippy is included with standard -Rust installations. +ابزار Clippy مجموعه‌ای از lintها برای تحلیل کد شماست +تا بتوانید خطاهای رایج را شناسایی کرده و کد Rust خود +را بهبود دهید. Clippy همراه با نصب استاندارد Rust +در دسترس است. -To run Clippy’s lints on any Cargo project, enter the following: +برای اجرای تحلیلگرهای Clippy روی هر پروژه Cargo، دستور زیر را وارد کنید: ```console $ cargo clippy ``` -For example, say you write a program that uses an approximation of a -mathematical constant, such as pi, as this program does: +به عنوان مثال، فرض کنید برنامه‌ای می‌نویسید که از یک مقدار تقریبی برای یک ثابت ریاضی، مانند pi، استفاده می‌کند، همانطور که این برنامه انجام می‌دهد: @@ -112,7 +113,7 @@ fn main() { -Running `cargo clippy` on this project results in this error: +اجرای `cargo clippy` روی این پروژه منجر به این خطا می‌شود: ```text error: approximate value of `f{32, 64}::consts::PI` found @@ -126,11 +127,13 @@ error: approximate value of `f{32, 64}::consts::PI` found = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant ``` -This error lets you know that Rust already has a more precise `PI` constant -defined, and that your program would be more correct if you used the constant -instead. You would then change your code to use the `PI` constant. +این خطا به شما اطلاع می‌دهد که Rust از پیش +ثابت `PI` دقیق‌تری را تعریف کرده است، و استفاده از +این ثابت در برنامه‌تان باعث درستی بیشتر کد می‌شود. +بنابراین باید کد خود را طوری تغییر دهید که از +ثابت `PI` استفاده کند. -The following code doesn’t result in any errors or warnings from Clippy: +کد زیر هیچ خطا یا هشداری از Clippy ایجاد نمی‌کند: @@ -144,21 +147,25 @@ fn main() { -For more information on Clippy, see [its documentation][clippy]. - -### IDE Integration Using `rust-analyzer` - -To help with IDE integration, the Rust community recommends using -[`rust-analyzer`][rust-analyzer]. This tool is a set of -compiler-centric utilities that speak [Language Server Protocol][lsp], which is a specification for IDEs and programming languages to -communicate with each other. Different clients can use `rust-analyzer`, such as -[the Rust analyzer plug-in for Visual Studio Code][vscode]. - -Visit the `rust-analyzer` project’s [home page][rust-analyzer] -for installation instructions, then install the language server support in your -particular IDE. Your IDE will gain capabilities such as autocompletion, jump to -definition, and inline errors. +برای اطلاعات بیشتر درباره Clippy، به [مستندات آن][clippy] مراجعه کنید. + +### یکپارچه‌سازی IDE با استفاده از `rust-analyzer` + +برای یکپارچه‌سازی بهتر با محیط‌های توسعه (IDE)، +جامعه‌ی Rust استفاده از [`rust-analyzer`][rust-analyzer] +را توصیه می‌کند. این ابزار مجموعه‌ای از ابزارهای +وابسته به کامپایلر است که با [پروتکل زبان سرور (LSP)][lsp] +ارتباط برقرار می‌کند؛ این پروتکل مشخصاتی است برای +ارتباط میان IDEها و زبان‌های برنامه‌نویسی. +کلاینت‌های مختلفی می‌توانند از `rust-analyzer` +استفاده کنند، مانند [افزونه‌ی Rust Analyzer برای +Visual Studio Code][vscode]. + +برای دریافت دستورالعمل نصب، به [صفحه‌ی اصلی +پروژه‌ی `rust-analyzer`][rust-analyzer] مراجعه کنید، +سپس پشتیبانی از language server را در IDE خود نصب نمایید. +پس از آن، امکاناتی مانند تکمیل خودکار، پرش به تعریف، +و نمایش خطاها به‌صورت درون‌خطی به IDE شما اضافه خواهد شد. [rustfmt]: https://github.com/rust-lang/rustfmt [editions]: appendix-05-editions.md diff --git a/src/appendix-05-editions.md b/src/appendix-05-editions.md index 0b8c4a3a12..91e3cc09bf 100644 --- a/src/appendix-05-editions.md +++ b/src/appendix-05-editions.md @@ -1,57 +1,33 @@ -## Appendix E - Editions - -In Chapter 1, you saw that `cargo new` adds a bit of metadata to your -_Cargo.toml_ file about an edition. This appendix talks about what that means! - -The Rust language and compiler have a six-week release cycle, meaning users get -a constant stream of new features. Other programming languages release larger -changes less often; Rust releases smaller updates more frequently. After a -while, all of these tiny changes add up. But from release to release, it can be -difficult to look back and say, “Wow, between Rust 1.10 and Rust 1.31, Rust has -changed a lot!” - -Every three years or so, the Rust team produces a new Rust _edition_. Each -edition brings together the features that have landed into a clear package with -fully updated documentation and tooling. New editions ship as part of the usual -six-week release process. - -Editions serve different purposes for different people: - -- For active Rust users, a new edition brings together incremental changes into - an easy-to-understand package. -- For non-users, a new edition signals that some major advancements have - landed, which might make Rust worth another look. -- For those developing Rust, a new edition provides a rallying point for the - project as a whole. - -At the time of this writing, four Rust editions are available: Rust 2015, Rust -2018, Rust 2021, and Rust 2024. This book is written using Rust 2024 edition -idioms. - -The `edition` key in _Cargo.toml_ indicates which edition the compiler should -use for your code. If the key doesn’t exist, Rust uses `2015` as the edition -value for backward compatibility reasons. - -Each project can opt in to an edition other than the default 2015 edition. -Editions can contain incompatible changes, such as including a new keyword that -conflicts with identifiers in code. However, unless you opt in to those -changes, your code will continue to compile even as you upgrade the Rust -compiler version you use. - -All Rust compiler versions support any edition that existed prior to that -compiler’s release, and they can link crates of any supported editions -together. Edition changes only affect the way the compiler initially parses -code. Therefore, if you’re using Rust 2015 and one of your dependencies uses -Rust 2018, your project will compile and be able to use that dependency. The -opposite situation, where your project uses Rust 2018 and a dependency uses -Rust 2015, works as well. - -To be clear: most features will be available on all editions. Developers using -any Rust edition will continue to see improvements as new stable releases are -made. However, in some cases, mainly when new keywords are added, some new -features might only be available in later editions. You will need to switch -editions if you want to take advantage of such features. - -For more details, the [_Edition Guide_](https://doc.rust-lang.org/stable/edition-guide/) is a complete book -about editions that enumerates the differences between editions and explains -how to automatically upgrade your code to a new edition via `cargo fix`. +## ضمیمه ه - نسخه‌ها + +در فصل 1 دیدید که `cargo new` کمی متاداده به فایل _Cargo.toml_ شما اضافه می‌کند که درباره نسخه است. این ضمیمه توضیح می‌دهد که این به چه معناست! + +زبان Rust و کامپایلر آن یک چرخه انتشار شش‌هفته‌ای دارند، به این معنی که کاربران به طور مداوم به ویژگی‌های جدید دسترسی پیدا می‌کنند. زبان‌های برنامه‌نویسی دیگر تغییرات بزرگ را کمتر منتشر می‌کنند؛ در حالی که Rust به طور مرتب به‌روزرسانی‌های کوچک ارائه می‌دهد. پس از مدتی، همه این تغییرات کوچک جمع می‌شوند. اما از انتشار به انتشار، ممکن است سخت باشد که بگویید: «وای، بین Rust 1.10 و Rust 1.31، Rust خیلی تغییر کرده است!» + +تقریباً هر سه سال یک‌بار، تیم Rust یک *نگارش* +جدید از Rust منتشر می‌کند. هر نگارش، ویژگی‌هایی +را که در طول زمان اضافه شده‌اند، در قالبی منسجم +و همراه با مستندات و ابزارهای کاملاً به‌روزشده +ارائه می‌دهد. نگارش‌های جدید به‌عنوان بخشی +از روند معمول انتشارهای شش‌هفته‌ای عرضه می‌شوند. + +نسخه‌ها اهداف مختلفی برای افراد مختلف دارند: + +
    +
  • برای کاربران فعال Rust، یک نسخه جدید تغییرات تدریجی را در یک بسته آسان برای درک جمع می‌کند.
  • +
  • برای غیرکاربران، یک نسخه جدید نشان‌دهنده این است که پیشرفت‌های عمده‌ای انجام شده است که ممکن است ارزش داشته باشد Rust را دوباره بررسی کنند.
  • +
  • برای کسانی که Rust را توسعه می‌دهند، یک نسخه جدید نقطه‌ای برای گردهمایی پروژه به عنوان یک کل فراهم می‌کند.
  • +
+ +در زمان نگارش این متن، چهار نسخه از Rust در دسترس هستند: Rust 2015، Rust 2018، Rust 2021، و Rust 2024. این کتاب با استفاده از الگوها و قواعد نسخه Rust 2024 نوشته شده است. + +کلید `edition` در فایل _Cargo.toml_ نشان می‌دهد که کامپایلر باید از کدام نسخه برای کد شما استفاده کند. اگر این کلید وجود نداشته باشد، Rust به دلایل سازگاری با نسخه‌های قبلی از مقدار `2015` به‌عنوان نسخه پیش‌فرض استفاده می‌کند. + +هر پروژه می‌تواند به یک نسخه غیر از نسخه پیش‌فرض 2015 مهاجرت کند. نسخه‌ها می‌توانند تغییرات ناسازگار داشته باشند، مانند اضافه کردن یک کلمه کلیدی جدید که با شناسه‌ها در کد تداخل دارد. اما تا زمانی که به این تغییرات مهاجرت نکنید، کد شما همچنان کامپایل خواهد شد حتی اگر نسخه کامپایلر Rust خود را ارتقا دهید. + +تمام نسخه‌های کامپایلر Rust از هر نسخه‌ای که پیش از انتشار آن کامپایلر وجود داشته باشد پشتیبانی می‌کنند و می‌توانند کتابخانه‌هایی از هر نسخه پشتیبانی‌شده را به یکدیگر لینک کنند. تغییرات نسخه فقط بر نحوه تجزیه اولیه کد توسط کامپایلر تأثیر می‌گذارد. بنابراین، اگر از Rust 2015 استفاده می‌کنید و یکی از وابستگی‌های شما از Rust 2018 استفاده می‌کند، پروژه شما کامپایل خواهد شد و می‌تواند از آن وابستگی استفاده کند. وضعیت معکوس، جایی که پروژه شما از Rust 2018 و یک وابستگی از Rust 2015 استفاده می‌کند، نیز کار می‌کند. + +برای روشن شدن: بیشتر ویژگی‌ها در همه نسخه‌ها در دسترس خواهند بود. توسعه‌دهندگان با استفاده از هر نسخه‌ای از Rust همچنان بهبودهایی را با انتشارهای پایدار جدید مشاهده خواهند کرد. اما در برخی موارد، عمدتاً زمانی که کلمات کلیدی جدید اضافه می‌شوند، برخی ویژگی‌های جدید ممکن است فقط در نسخه‌های بعدی در دسترس باشند. اگر می‌خواهید از چنین ویژگی‌هایی استفاده کنید، باید به نسخه‌های جدیدتر مهاجرت کنید. + +برای جزئیات بیشتر، [_راهنمای نسخه‌ها_](https://doc.rust-lang.org/stable/edition-guide/) کتاب کاملی درباره نسخه‌ها است که تفاوت‌های بین نسخه‌ها را فهرست می‌کند و توضیح می‌دهد که چگونه می‌توانید کد خود را با استفاده از `cargo fix` به نسخه جدید ارتقا دهید. + diff --git a/src/appendix-06-translation.md b/src/appendix-06-translation.md index f06b1986cf..5f1d3b2e98 100644 --- a/src/appendix-06-translation.md +++ b/src/appendix-06-translation.md @@ -1,7 +1,6 @@ -## Appendix F: Translations of the Book +## ضمیمه و: ترجمه‌های کتاب -For resources in languages other than English. Most are still in progress; see -[the Translations label][label] to help or let us know about a new translation! +برای منابع به زبان‌هایی غیر از انگلیسی. بیشتر آن‌ها هنوز در حال پیشرفت هستند؛ برای کمک یا اطلاع دادن از ترجمه جدید، به [برچسب ترجمه‌ها][label] مراجعه کنید! [label]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations @@ -26,3 +25,4 @@ For resources in languages other than English. Most are still in progress; see - [हिंदी](https://github.com/venkatarun95/rust-book-hindi) - [ไทย](https://github.com/rust-lang-th/book-th) - [Danske](https://github.com/DanKHansen/book-dk) + diff --git a/src/appendix-07-nightly-rust.md b/src/appendix-07-nightly-rust.md index 5246d72825..6b4a963a2a 100644 --- a/src/appendix-07-nightly-rust.md +++ b/src/appendix-07-nightly-rust.md @@ -1,50 +1,33 @@ -## Appendix G - How Rust is Made and “Nightly Rust” +## ضمیمه ی - چگونگی توسعه Rust و "Rust Nightly" -This appendix is about how Rust is made and how that affects you as a Rust -developer. +این ضمیمه درباره چگونگی توسعه Rust و تأثیر آن بر شما به عنوان یک توسعه‌دهنده Rust است. -### Stability Without Stagnation +### ثبات بدون رکود -As a language, Rust cares a _lot_ about the stability of your code. We want -Rust to be a rock-solid foundation you can build on, and if things were -constantly changing, that would be impossible. At the same time, if we can’t -experiment with new features, we may not find out important flaws until after -their release, when we can no longer change things. +به عنوان یک زبان، Rust به _ثبات_ کد شما بسیار اهمیت می‌دهد. ما می‌خواهیم Rust یک پایه محکم و قابل اعتماد باشد که بتوانید بر روی آن بسازید، و اگر همه چیز به طور مداوم تغییر می‌کرد، این امکان‌پذیر نبود. در عین حال، اگر نتوانیم با ویژگی‌های جدید آزمایش کنیم، ممکن است مشکلات مهمی را تا بعد از انتشار آن‌ها کشف نکنیم، زمانی که دیگر نمی‌توان تغییراتی ایجاد کرد. -Our solution to this problem is what we call “stability without stagnation”, -and our guiding principle is this: you should never have to fear upgrading to a -new version of stable Rust. Each upgrade should be painless, but should also -bring you new features, fewer bugs, and faster compile times. +راه‌حل ما برای این مشکل چیزی است که ما آن را "ثبات بدون رکود" می‌نامیم، و اصل راهنمای ما این است: شما هرگز نباید از ارتقاء به یک نسخه جدید از Rust پایدار بترسید. هر ارتقاء باید بدون دردسر باشد، اما همچنین ویژگی‌های جدید، باگ‌های کمتر، و زمان‌های کامپایل سریع‌تر را برای شما به ارمغان بیاورد. -### Choo, Choo! Release Channels and Riding the Trains +### چو، چو! کانال‌های انتشار و حرکت قطارها -Rust development operates on a _train schedule_. That is, all development is -done on the `master` branch of the Rust repository. Releases follow a software -release train model, which has been used by Cisco IOS and other software -projects. There are three _release channels_ for Rust: +توسعه Rust بر اساس یک _برنامه زمانی قطار_ عمل می‌کند. یعنی تمام توسعه‌ها در شاخه `master` مخزن Rust انجام می‌شود. انتشارها از مدل قطار انتشار نرم‌افزار پیروی می‌کنند، مدلی که توسط Cisco IOS و پروژه‌های نرم‌افزاری دیگر استفاده شده است. سه _کانال انتشار_ برای Rust وجود دارد: - Nightly - Beta - Stable -Most Rust developers primarily use the stable channel, but those who want to -try out experimental new features may use nightly or beta. + -Here’s an example of how the development and release process works: let’s -assume that the Rust team is working on the release of Rust 1.5. That release -happened in December of 2015, but it will provide us with realistic version -numbers. A new feature is added to Rust: a new commit lands on the `master` -branch. Each night, a new nightly version of Rust is produced. Every day is a -release day, and these releases are created by our release infrastructure -automatically. So as time passes, our releases look like this, once a night: + +بیشتر توسعه‌دهندگان Rust عمدتاً از کانال پایدار استفاده می‌کنند، اما کسانی که می‌خواهند ویژگی‌های آزمایشی جدید را امتحان کنند ممکن است از کانال‌های nightly یا beta استفاده کنند. + +در اینجا مثالی از نحوه کار فرآیند توسعه و انتشار آورده شده است: فرض کنید تیم Rust روی انتشار نسخه Rust 1.5 کار می‌کند. آن انتشار در دسامبر 2015 اتفاق افتاد، اما اعداد نسخه‌ای واقعی به ما ارائه می‌دهد. یک ویژگی جدید به Rust اضافه می‌شود: یک commit جدید به شاخه `master` اضافه می‌شود. هر شب، یک نسخه جدید nightly از Rust تولید می‌شود. هر روز یک روز انتشار است، و این نسخه‌ها به طور خودکار توسط زیرساخت انتشار ما ایجاد می‌شوند. بنابراین با گذشت زمان، انتشارهای ما به این صورت خواهند بود، هر شب: ```text nightly: * - - * - - * ``` -Every six weeks, it’s time to prepare a new release! The `beta` branch of the -Rust repository branches off from the `master` branch used by nightly. Now, -there are two releases: +هر شش هفته، زمان آماده‌سازی یک انتشار جدید است! شاخه `beta` مخزن Rust از شاخه `master` که برای nightly استفاده می‌شود منشعب می‌شود. اکنون دو نسخه وجود دارد: ```text nightly: * - - * - - * @@ -52,9 +35,7 @@ nightly: * - - * - - * beta: * ``` -Most Rust users do not use beta releases actively, but test against beta in -their CI system to help Rust discover possible regressions. In the meantime, -there’s still a nightly release every night: +بیشتر کاربران Rust به طور فعال از نسخه‌های beta استفاده نمی‌کنند، اما در سیستم CI خود علیه beta تست می‌گیرند تا به Rust کمک کنند که مشکلات احتمالی را شناسایی کند. در همین حال، هنوز هر شب یک نسخه nightly منتشر می‌شود: ```text nightly: * - - * - - * - - * - - * @@ -62,10 +43,7 @@ nightly: * - - * - - * - - * - - * beta: * ``` -Let’s say a regression is found. Good thing we had some time to test the beta -release before the regression snuck into a stable release! The fix is applied -to `master`, so that nightly is fixed, and then the fix is backported to the -`beta` branch, and a new release of beta is produced: +فرض کنید یک مشکل (regression) پیدا شود. خوشبختانه ما زمانی برای تست نسخه beta داشتیم قبل از اینکه مشکل وارد نسخه پایدار شود! اصلاح به شاخه `master` اعمال می‌شود، بنابراین nightly اصلاح می‌شود، و سپس این اصلاح به شاخه `beta` بازگردانده می‌شود، و یک نسخه جدید از beta تولید می‌شود: ```text nightly: * - - * - - * - - * - - * - - * @@ -73,8 +51,7 @@ nightly: * - - * - - * - - * - - * - - * beta: * - - - - - - - - * ``` -Six weeks after the first beta was created, it’s time for a stable release! The -`stable` branch is produced from the `beta` branch: +شش هفته پس از ایجاد اولین نسخه beta، زمان انتشار نسخه پایدار است! شاخه `stable` از شاخه `beta` تولید می‌شود: ```text nightly: * - - * - - * - - * - - * - - * - * - * @@ -84,10 +61,7 @@ beta: * - - - - - - - - * stable: * ``` -Hooray! Rust 1.5 is done! However, we’ve forgotten one thing: because the six -weeks have gone by, we also need a new beta of the _next_ version of Rust, 1.6. -So after `stable` branches off of `beta`, the next version of `beta` branches -off of `nightly` again: +هورا! Rust 1.5 آماده است! اما یک چیز را فراموش کرده‌ایم: چون شش هفته گذشته است، ما به نسخه beta جدیدی از _نسخه بعدی_ Rust، یعنی 1.6، نیاز داریم. بنابراین پس از اینکه شاخه `stable` از `beta` جدا شد، نسخه بعدی `beta` دوباره از `nightly` منشعب می‌شود: ```text nightly: * - - * - - * - - * - - * - - * - * - * @@ -97,63 +71,33 @@ beta: * - - - - - - - - * * stable: * ``` -This is called the “train model” because every six weeks, a release “leaves the -station”, but still has to take a journey through the beta channel before it -arrives as a stable release. +این مدل "قطار" نامیده می‌شود، زیرا هر شش هفته، یک انتشار "ایستگاه را ترک می‌کند"، اما همچنان باید از کانال beta عبور کند تا به یک انتشار پایدار تبدیل شود. -Rust releases every six weeks, like clockwork. If you know the date of one Rust -release, you can know the date of the next one: it’s six weeks later. A nice -aspect of having releases scheduled every six weeks is that the next train is -coming soon. If a feature happens to miss a particular release, there’s no need -to worry: another one is happening in a short time! This helps reduce pressure -to sneak possibly unpolished features in close to the release deadline. +انتشارهای Rust هر شش هفته، مانند ساعت دقیق انجام می‌شوند. اگر تاریخ یک انتشار Rust را بدانید، می‌توانید تاریخ انتشار بعدی را بدانید: شش هفته بعد. یکی از جنبه‌های خوب داشتن انتشارهای برنامه‌ریزی‌شده هر شش هفته این است که قطار بعدی به زودی می‌آید. اگر یک ویژگی به طور اتفاقی یک انتشار خاص را از دست بدهد، نیازی به نگرانی نیست: انتشار بعدی در مدت کوتاهی اتفاق می‌افتد! این امر به کاهش فشار برای افزودن ویژگی‌های احتمالاً ناقص نزدیک به مهلت انتشار کمک می‌کند. -Thanks to this process, you can always check out the next build of Rust and -verify for yourself that it’s easy to upgrade to: if a beta release doesn’t -work as expected, you can report it to the team and get it fixed before the -next stable release happens! Breakage in a beta release is relatively rare, but -`rustc` is still a piece of software, and bugs do exist. +با تشکر از این فرآیند، شما همیشه می‌توانید نسخه بعدی Rust را بررسی کرده و برای خود تأیید کنید که ارتقاء به آن آسان است: اگر یک نسخه beta مطابق انتظار عمل نکند، می‌توانید آن را به تیم گزارش دهید و قبل از اینکه انتشار پایدار بعدی انجام شود، آن را اصلاح کنید! شکستن در یک نسخه beta نسبتاً نادر است، اما `rustc` همچنان یک نرم‌افزار است و باگ‌ها وجود دارند. -### Maintenance time +### زمان نگهداری -The Rust project supports the most recent stable version. When a new stable -version is released, the old version reaches its end of life (EOL). This means -each version is supported for six weeks. +پروژه Rust از آخرین نسخه پایدار پشتیبانی می‌کند. وقتی یک نسخه پایدار جدید منتشر می‌شود، نسخه قدیمی به پایان عمر خود (EOL) می‌رسد. این به این معنی است که هر نسخه برای شش هفته پشتیبانی می‌شود. -### Unstable Features +### ویژگی‌های ناپایدار -There’s one more catch with this release model: unstable features. Rust uses a -technique called “feature flags” to determine what features are enabled in a -given release. If a new feature is under active development, it lands on -`master`, and therefore, in nightly, but behind a _feature flag_. If you, as a -user, wish to try out the work-in-progress feature, you can, but you must be -using a nightly release of Rust and annotate your source code with the -appropriate flag to opt in. +یک نکته دیگر در این مدل انتشار وجود دارد: ویژگی‌های ناپایدار. Rust از تکنیکی به نام "پرچم‌های ویژگی" (feature flags) استفاده می‌کند تا تعیین کند چه ویژگی‌هایی در یک انتشار فعال هستند. اگر یک ویژگی جدید تحت توسعه فعال باشد، روی شاخه `master` قرار می‌گیرد و بنابراین، در nightly، اما پشت یک _پرچم ویژگی_ قرار می‌گیرد. اگر به‌عنوان کاربر، مایلید ویژگی در حال توسعه را امتحان کنید، می‌توانید این کار را انجام دهید، اما باید از نسخه nightly Rust استفاده کرده و کد منبع خود را با پرچم مناسب برای فعال‌سازی آن علامت‌گذاری کنید. -If you’re using a beta or stable release of Rust, you can’t use any feature -flags. This is the key that allows us to get practical use with new features -before we declare them stable forever. Those who wish to opt into the bleeding -edge can do so, and those who want a rock-solid experience can stick with -stable and know that their code won’t break. Stability without stagnation. +اگر از نسخه beta یا پایدار Rust استفاده می‌کنید، نمی‌توانید از پرچم‌های ویژگی استفاده کنید. این نکته‌ای است که به ما اجازه می‌دهد از ویژگی‌های جدید به صورت عملی استفاده کنیم قبل از اینکه آن‌ها را برای همیشه پایدار اعلام کنیم. کسانی که مایلند از ویژگی‌های پیشرفته استفاده کنند، می‌توانند این کار را انجام دهند، و کسانی که تجربه‌ای پایدار و قابل اعتماد می‌خواهند می‌توانند با نسخه پایدار بمانند و مطمئن باشند که کد آن‌ها خراب نخواهد شد. ثبات بدون رکود. -This book only contains information about stable features, as in-progress -features are still changing, and surely they’ll be different between when this -book was written and when they get enabled in stable builds. You can find -documentation for nightly-only features online. +این کتاب فقط شامل اطلاعات مربوط به ویژگی‌های پایدار است، زیرا ویژگی‌های در حال توسعه همچنان در حال تغییر هستند و مطمئناً بین زمانی که این کتاب نوشته شده و زمانی که در نسخه‌های پایدار فعال می‌شوند، متفاوت خواهند بود. می‌توانید مستندات مربوط به ویژگی‌هایی که فقط در nightly موجود هستند را به صورت آنلاین پیدا کنید. -### Rustup and the Role of Rust Nightly +### Rustup و نقش Rust Nightly -Rustup makes it easy to change between different release channels of Rust, on a -global or per-project basis. By default, you’ll have stable Rust installed. To -install nightly, for example: +ابزار Rustup تغییر بین کانال‌های مختلف انتشار Rust را، به صورت جهانی یا بر اساس هر پروژه، آسان می‌کند. به طور پیش‌فرض، Rust پایدار نصب خواهد بود. برای نصب نسخه nightly، به عنوان مثال: ```console $ rustup toolchain install nightly ``` -You can see all of the _toolchains_ (releases of Rust and associated -components) you have installed with `rustup` as well. Here’s an example on one -of your authors’ Windows computer: +همچنین می‌توانید تمام _ابزارهای موجود_ (نسخه‌های Rust و اجزای مرتبط) که با `rustup` نصب کرده‌اید را ببینید. در اینجا مثالی از یک کامپیوتر ویندوزی یکی از نویسندگان آورده شده است: ```powershell > rustup toolchain list @@ -162,45 +106,21 @@ beta-x86_64-pc-windows-msvc nightly-x86_64-pc-windows-msvc ``` -As you can see, the stable toolchain is the default. Most Rust users use stable -most of the time. You might want to use stable most of the time, but use -nightly on a specific project, because you care about a cutting-edge feature. -To do so, you can use `rustup override` in that project’s directory to set the -nightly toolchain as the one `rustup` should use when you’re in that directory: +همان‌طور که می‌بینید، ابزار stable به طور پیش‌فرض تنظیم شده است. بیشتر کاربران Rust بیشتر وقت خود از stable استفاده می‌کنند. ممکن است بخواهید بیشتر وقت خود از stable استفاده کنید، اما در یک پروژه خاص از nightly استفاده کنید، زیرا به یک ویژگی پیشرفته علاقه دارید. برای انجام این کار، می‌توانید از `rustup override` در دایرکتوری آن پروژه استفاده کنید تا ابزار nightly را به‌عنوان ابزار مورد استفاده `rustup` در آن دایرکتوری تنظیم کنید: ```console $ cd ~/projects/needs-nightly $ rustup override set nightly ``` -Now, every time you call `rustc` or `cargo` inside of -_~/projects/needs-nightly_, `rustup` will make sure that you are using nightly -Rust, rather than your default of stable Rust. This comes in handy when you -have a lot of Rust projects! - -### The RFC Process and Teams - -So how do you learn about these new features? Rust’s development model follows -a _Request For Comments (RFC) process_. If you’d like an improvement in Rust, -you can write up a proposal, called an RFC. - -Anyone can write RFCs to improve Rust, and the proposals are reviewed and -discussed by the Rust team, which is comprised of many topic subteams. There’s -a full list of the teams [on Rust’s website](https://www.rust-lang.org/governance), which includes teams for -each area of the project: language design, compiler implementation, -infrastructure, documentation, and more. The appropriate team reads the -proposal and the comments, writes some comments of their own, and eventually, -there’s consensus to accept or reject the feature. - -If the feature is accepted, an issue is opened on the Rust repository, and -someone can implement it. The person who implements it very well may not be the -person who proposed the feature in the first place! When the implementation is -ready, it lands on the `master` branch behind a feature gate, as we discussed -in the [“Unstable Features”](#unstable-features) section. - -After some time, once Rust developers who use nightly releases have been able -to try out the new feature, team members will discuss the feature, how it’s -worked out on nightly, and decide if it should make it into stable Rust or not. -If the decision is to move forward, the feature gate is removed, and the -feature is now considered stable! It rides the trains into a new stable release -of Rust. +اکنون، هر بار که در دایرکتوری _~/projects/needs-nightly_ دستور `rustc` یا `cargo` را فراخوانی کنید، `rustup` اطمینان حاصل می‌کند که شما از Rust nightly استفاده می‌کنید، نه نسخه پایدار پیش‌فرض. این ویژگی زمانی که پروژه‌های زیادی با Rust دارید، بسیار مفید است! + +### فرآیند RFC و تیم‌ها + +چگونه می‌توانید درباره این ویژگی‌های جدید اطلاعات کسب کنید؟ مدل توسعه Rust از یک فرآیند _درخواست نظرات (RFC)_ پیروی می‌کند. اگر بهبود خاصی در Rust می‌خواهید، می‌توانید یک پیشنهاد بنویسید که به آن RFC گفته می‌شود. + +هر کسی می‌تواند RFC بنویسد تا Rust را بهبود دهد، و این پیشنهادها توسط تیم Rust که از چندین زیرتیم موضوعی تشکیل شده است، بررسی و بحث می‌شوند. لیست کامل تیم‌ها [در وب‌سایت Rust](https://www.rust-lang.org/governance) موجود است، که شامل تیم‌هایی برای هر بخش از پروژه می‌شود: طراحی زبان، پیاده‌سازی کامپایلر، زیرساخت، مستندات و موارد دیگر. تیم مربوطه پیشنهاد و نظرات را می‌خواند، نظرات خود را می‌نویسد، و در نهایت، توافقی برای پذیرش یا رد ویژگی حاصل می‌شود. + +اگر ویژگی پذیرفته شود، یک issue در مخزن Rust باز می‌شود و کسی می‌تواند آن را پیاده‌سازی کند. فردی که آن را پیاده‌سازی می‌کند، ممکن است همان فردی نباشد که ویژگی را ابتدا پیشنهاد داده است! وقتی پیاده‌سازی آماده شد، روی شاخه `master` پشت یک پرچم ویژگی قرار می‌گیرد، همان‌طور که در بخش [“ویژگی‌های ناپایدار”](#unstable-features) بحث شد. + +پس از مدتی، زمانی که توسعه‌دهندگان Rust که از نسخه‌های nightly استفاده می‌کنند توانسته‌اند ویژگی جدید را امتحان کنند، اعضای تیم درباره این ویژگی، نحوه عملکرد آن در nightly و تصمیم‌گیری می‌کنند که آیا باید وارد Rust پایدار شود یا نه. اگر تصمیم بر ادامه باشد، پرچم ویژگی حذف می‌شود و ویژگی اکنون پایدار تلقی می‌شود! سپس این ویژگی وارد نسخه پایدار جدید Rust می‌شود. diff --git a/src/ch00-00-introduction.md b/src/ch00-00-introduction.md index f3916dceda..d5dce15aef 100644 --- a/src/ch00-00-introduction.md +++ b/src/ch00-00-introduction.md @@ -1,196 +1,129 @@ -# Introduction +# مقدمه -> Note: This edition of the book is the same as [The Rust Programming -> Language][nsprust] available in print and ebook format from [No Starch -> Press][nsp]. +> توجه: این نسخه از کتاب همان [The Rust Programming Language][nsprust] است که به صورت چاپی و الکترونیکی از [No Starch Press][nsp] در دسترس است. [nsprust]: https://nostarch.com/rust-programming-language-2nd-edition [nsp]: https://nostarch.com/ -Welcome to _The Rust Programming Language_, an introductory book about Rust. -The Rust programming language helps you write faster, more reliable software. -High-level ergonomics and low-level control are often at odds in programming -language design; Rust challenges that conflict. Through balancing powerful -technical capacity and a great developer experience, Rust gives you the option -to control low-level details (such as memory usage) without all the hassle -traditionally associated with such control. - -## Who Rust Is For - -Rust is ideal for many people for a variety of reasons. Let’s look at a few of -the most important groups. - -### Teams of Developers - -Rust is proving to be a productive tool for collaborating among large teams of -developers with varying levels of systems programming knowledge. Low-level code -is prone to various subtle bugs, which in most other languages can be caught -only through extensive testing and careful code review by experienced -developers. In Rust, the compiler plays a gatekeeper role by refusing to -compile code with these elusive bugs, including concurrency bugs. By working -alongside the compiler, the team can spend their time focusing on the program’s -logic rather than chasing down bugs. - -Rust also brings contemporary developer tools to the systems programming world: - -- Cargo, the included dependency manager and build tool, makes adding, - compiling, and managing dependencies painless and consistent across the Rust - ecosystem. -- The Rustfmt formatting tool ensures a consistent coding style across - developers. -- The rust-analyzer powers Integrated Development Environment (IDE) - integration for code completion and inline error messages. - -By using these and other tools in the Rust ecosystem, developers can be -productive while writing systems-level code. - -### Students - -Rust is for students and those who are interested in learning about systems -concepts. Using Rust, many people have learned about topics like operating -systems development. The community is very welcoming and happy to answer -student questions. Through efforts such as this book, the Rust teams want to -make systems concepts more accessible to more people, especially those new to -programming. - -### Companies - -Hundreds of companies, large and small, use Rust in production for a variety of -tasks, including command line tools, web services, DevOps tooling, embedded -devices, audio and video analysis and transcoding, cryptocurrencies, -bioinformatics, search engines, Internet of Things applications, machine -learning, and even major parts of the Firefox web browser. - -### Open Source Developers - -Rust is for people who want to build the Rust programming language, community, -developer tools, and libraries. We’d love to have you contribute to the Rust -language. - -### People Who Value Speed and Stability - -Rust is for people who crave speed and stability in a language. By speed, we -mean both how quickly Rust code can run and the speed at which Rust lets you -write programs. The Rust compiler’s checks ensure stability through feature -additions and refactoring. This is in contrast to the brittle legacy code in -languages without these checks, which developers are often afraid to modify. By -striving for zero-cost abstractions—higher-level features that compile to -lower-level code as fast as code written manually—Rust endeavors to make safe -code be fast code as well. - -The Rust language hopes to support many other users as well; those mentioned -here are merely some of the biggest stakeholders. Overall, Rust’s greatest -ambition is to eliminate the trade-offs that programmers have accepted for -decades by providing safety _and_ productivity, speed _and_ ergonomics. Give -Rust a try and see if its choices work for you. - -## Who This Book Is For - -This book assumes that you’ve written code in another programming language but -doesn’t make any assumptions about which one. We’ve tried to make the material -broadly accessible to those from a wide variety of programming backgrounds. We -don’t spend a lot of time talking about what programming _is_ or how to think -about it. If you’re entirely new to programming, you would be better served by -reading a book that specifically provides an introduction to programming. - -## How to Use This Book - -In general, this book assumes that you’re reading it in sequence from front to -back. Later chapters build on concepts in earlier chapters, and earlier -chapters might not delve into details on a particular topic but will revisit -the topic in a later chapter. - -You’ll find two kinds of chapters in this book: concept chapters and project -chapters. In concept chapters, you’ll learn about an aspect of Rust. In project -chapters, we’ll build small programs together, applying what you’ve learned so -far. Chapters 2, 12, and 21 are project chapters; the rest are concept chapters. - -Chapter 1 explains how to install Rust, how to write a “Hello, world!” program, -and how to use Cargo, Rust’s package manager and build tool. Chapter 2 is a -hands-on introduction to writing a program in Rust, having you build up a -number guessing game. Here we cover concepts at a high level, and later -chapters will provide additional detail. If you want to get your hands dirty -right away, Chapter 2 is the place for that. Chapter 3 covers Rust features -that are similar to those of other programming languages, and in Chapter 4 -you’ll learn about Rust’s ownership system. If you’re a particularly meticulous -learner who prefers to learn every detail before moving on to the next, you -might want to skip Chapter 2 and go straight to Chapter 3, returning to Chapter -2 when you’d like to work on a project applying the details you’ve learned. - -Chapter 5 discusses structs and methods, and Chapter 6 covers enums, `match` -expressions, and the `if let` control flow construct. You’ll use structs and -enums to make custom types in Rust. - -In Chapter 7, you’ll learn about Rust’s module system and about privacy rules -for organizing your code and its public Application Programming Interface -(API). Chapter 8 discusses some common collection data structures that the -standard library provides, such as vectors, strings, and hash maps. Chapter 9 -explores Rust’s error-handling philosophy and techniques. - -Chapter 10 digs into generics, traits, and lifetimes, which give you the power -to define code that applies to multiple types. Chapter 11 is all about testing, -which even with Rust’s safety guarantees is necessary to ensure your program’s -logic is correct. In Chapter 12, we’ll build our own implementation of a subset -of functionality from the `grep` command line tool that searches for text -within files. For this, we’ll use many of the concepts we discussed in the -previous chapters. - -Chapter 13 explores closures and iterators: features of Rust that come from -functional programming languages. In Chapter 14, we’ll examine Cargo in more -depth and talk about best practices for sharing your libraries with others. -Chapter 15 discusses smart pointers that the standard library provides and the -traits that enable their functionality. - -In Chapter 16, we’ll walk through different models of concurrent programming and -talk about how Rust helps you to program in multiple threads fearlessly. In -Chapter 17, we build on that by exploring Rust’s async and await syntax, along -with tasks, futures, and streams, and the lightweight concurrency model they -enable. - -Chapter 18 looks at how Rust idioms compare to object-oriented programming -principles you might be familiar with. Chapter 19 is a reference on patterns and -pattern matching, which are powerful ways of expressing ideas throughout Rust -programs. Chapter 20 contains a smorgasbord of advanced topics of interest, -including unsafe Rust, macros, and more about lifetimes, traits, types, -functions, and closures. - -In Chapter 21, we’ll complete a project in which we’ll implement a low-level -multithreaded web server! - -Finally, some appendixes contain useful information about the language in a more -reference-like format. **Appendix A** covers Rust’s keywords, **Appendix B** -covers Rust’s operators and symbols, **Appendix C** covers derivable traits -provided by the standard library, **Appendix D** covers some useful development -tools, and **Appendix E** explains Rust editions. In **Appendix F**, you can -find translations of the book, and in **Appendix G** we’ll cover how Rust is -made and what nightly Rust is. - -There is no wrong way to read this book: if you want to skip ahead, go for it! -You might have to jump back to earlier chapters if you experience any -confusion. But do whatever works for you. +به _زبان برنامه‌نویسی راست_ خوش آمدید، یک کتاب مقدماتی درباره راست. زبان برنامه‌نویسی راست به شما کمک می‌کند نرم‌افزاری سریع‌تر و قابل‌اعتمادتر بنویسید. در طراحی زبان‌های برنامه‌نویسی، راحتی در سطح بالا و کنترل در سطح پایین اغلب در تضاد هستند؛ راست این تناقض را به چالش می‌کشد. با ایجاد تعادل بین توانایی‌های فنی قدرتمند و تجربه عالی برنامه‌نویسی، راست به شما این امکان را می‌دهد که جزئیات سطح پایین (مانند استفاده از حافظه) را بدون دردسرهای سنتی مرتبط با چنین کنترلی مدیریت کنید. + +## راست برای چه کسانی است + +راست برای افراد مختلف با دلایل متنوع ایده‌آل است. بیایید به برخی از مهم‌ترین گروه‌ها نگاهی بیندازیم. + +### تیم‌های برنامه‌نویسی + +راست ابزاری اثبات شده برای همکاری میان تیم‌های بزرگ برنامه‌نویسان با سطوح مختلف دانش برنامه‌نویسی سیستم است. کد سطح پایین مستعد اشکالات ظریف متعددی است که در بیشتر زبان‌های دیگر تنها از طریق تست گسترده و بازبینی دقیق کد توسط برنامه‌نویسان با تجربه قابل شناسایی هستند. در راست، کامپایلر نقش نگهبان را ایفا می‌کند و از کامپایل کردن کدهایی با این اشکالات گریزان، از جمله اشکالات همروندی، جلوگیری می‌کند. با کار کردن در کنار کامپایلر، تیم می‌تواند زمان خود را بر روی منطق برنامه به جای رفع اشکالات صرف کند. + +راست همچنین ابزارهای مدرن برنامه‌نویسی را به دنیای برنامه‌نویسی سیستم‌ها می‌آورد: + +- Cargo، مدیر وابستگی و ابزار ساخت، اضافه کردن، کامپایل کردن، و مدیریت وابستگی‌ها را در سراسر اکوسیستم راست ساده و یکپارچه می‌کند. +- ابزار قالب‌بندی Rustfmt، یک سبک کدنویسی ثابت را در بین برنامه‌نویسان تضمین می‌کند. +- rust-analyzer یکپارچگی محیط توسعه یکپارچه (IDE) را برای تکمیل کد و پیام‌های خطای درون‌خطی فراهم می‌کند. + +با استفاده از این ابزارها و دیگر ابزارهای اکوسیستم راست، برنامه‌نویسان می‌توانند در هنگام نوشتن کد سطح سیستم‌ها بهره‌ور باشند. + +### دانشجویان + +راست برای دانشجویان و کسانی است که به یادگیری مفاهیم سیستم‌ها علاقه‌مند هستند. بسیاری از افراد با استفاده از راست موضوعاتی مانند توسعه سیستم‌عامل را آموخته‌اند. جامعه راست بسیار پذیرنده است و با خوشحالی به سوالات دانشجویان پاسخ می‌دهد. از طریق تلاش‌هایی مانند این کتاب، تیم‌های راست می‌خواهند مفاهیم سیستم‌ها را برای افراد بیشتری، به ویژه کسانی که تازه وارد برنامه‌نویسی هستند، قابل دسترس‌تر کنند. + +### شرکت‌ها + +صدها شرکت، بزرگ و کوچک، از راست در تولید برای وظایف متنوعی استفاده می‌کنند، از جمله ابزارهای خط فرمان، خدمات وب، ابزارهای DevOps، دستگاه‌های تعبیه‌شده، تحلیل و رمزگذاری صدا و تصویر، ارزهای دیجیتال، زیست‌اطلاعات، موتورهای جستجو، برنامه‌های اینترنت اشیاء، یادگیری ماشین و حتی بخش‌های اصلی مرورگر وب فایرفاکس. + +### توسعه‌دهندگان متن‌باز + +راست برای کسانی است که می‌خواهند زبان برنامه‌نویسی راست، جامعه، ابزارهای توسعه‌دهنده و کتابخانه‌ها را بسازند. ما دوست داریم شما در توسعه زبان راست مشارکت کنید. + +### افرادی که سرعت و پایداری را ارزشمند می‌دانند + +Rust برای کسانی است که به‌دنبال سرعت و پایداری +در یک زبان برنامه‌نویسی هستند. منظور از سرعت، +هم سرعت اجرای کدهای Rust و هم سرعت توسعه +برنامه با استفاده از Rust است. بررسی‌های +کامپایلر Rust پایداری را حتی هنگام افزودن ویژگی‌های +جدید یا بازسازی کد (refactoring) تضمین می‌کنند. +این در تضاد با کدهای قدیمی و شکننده در زبان‌هایی +است که چنین بررسی‌هایی ندارند و توسعه‌دهندگان +اغلب از تغییر آن‌ها واهمه دارند. Rust با تمرکز بر +مفهوم انتزاع‌های بدون‌هزینه (zero-cost abstractions)— +یعنی ویژگی‌های سطح بالا که پس از کامپایل به +کدی در سطح پایین و سریع مانند کد دستی تبدیل +می‌شوند—تلاش می‌کند تا کد امن، کدی سریع نیز باشد. + +زبان راست امیدوار است از بسیاری از کاربران دیگر نیز پشتیبانی کند؛ افرادی که در اینجا ذکر شدند تنها برخی از بزرگ‌ترین ذینفعان هستند. در کل، بزرگ‌ترین جاه‌طلبی راست این است که با ارائه ایمنی _و_ بهره‌وری، سرعت _و_ راحتی، مصالحه‌هایی که برنامه‌نویسان دهه‌ها پذیرفته‌اند را حذف کند. راست را امتحان کنید و ببینید آیا انتخاب‌های آن برای شما مناسب است یا خیر. + +## این کتاب برای چه کسانی است + +این کتاب فرض می‌کند که شما قبلاً در یک زبان برنامه‌نویسی دیگر کدنویسی کرده‌اید اما هیچ فرضی در مورد اینکه کدام زبان است، ندارد. ما سعی کرده‌ایم مطالب را به گونه‌ای ارائه دهیم که برای افراد با زمینه‌های برنامه‌نویسی متنوع قابل دسترسی باشد. ما زمان زیادی را صرف صحبت درباره اینکه برنامه‌نویسی _چیست_ یا چگونه باید به آن فکر کنید، نمی‌کنیم. اگر کاملاً تازه‌وارد برنامه‌نویسی هستید، بهتر است کتابی را بخوانید که به طور خاص مقدمه‌ای بر برنامه‌نویسی ارائه می‌دهد. + +## نحوه استفاده از این کتاب + +به طور کلی، این کتاب فرض می‌کند که شما آن را به ترتیب از ابتدا تا انتها می‌خوانید. فصل‌های بعدی بر مفاهیم فصل‌های قبلی بنا شده‌اند و فصل‌های اولیه ممکن است به جزئیات خاصی وارد نشوند اما در فصول بعدی به آن موضوعات بازمی‌گردند. + +در این کتاب، دو نوع فصل وجود دارد: فصل‌های مفهومی و فصل‌های پروژه‌ای. در فصل‌های مفهومی، درباره یک جنبه از راست یاد خواهید گرفت. در فصل‌های پروژه‌ای، برنامه‌های کوچکی را با هم می‌سازیم و آنچه را که تاکنون آموخته‌اید به کار می‌گیریم. فصل‌های ۲، ۱۲ و ۲۱ فصل‌های پروژه‌ای هستند؛ بقیه فصل‌ها مفهومی هستند. + +فصل ۱ نحوه نصب راست، نوشتن یک برنامه "سلام دنیا!" و استفاده از Cargo، مدیر بسته و ابزار ساخت راست را توضیح می‌دهد. فصل ۲ مقدمه‌ای عملی برای نوشتن برنامه‌ای در راست است و شما را به ساخت یک بازی حدس عدد می‌برد. در اینجا مفاهیم را به طور کلی پوشش می‌دهیم و جزئیات بیشتری را در فصول بعدی ارائه خواهیم کرد. اگر می‌خواهید بلافاصله کار عملی انجام دهید، فصل ۲ مناسب شماست. فصل ۳ ویژگی‌های راست را که مشابه ویژگی‌های سایر زبان‌های برنامه‌نویسی است پوشش می‌دهد و در فصل ۴ درباره سیستم مالکیت راست یاد خواهید گرفت. اگر شما یک یادگیرنده دقیق هستید که ترجیح می‌دهید قبل از ادامه، همه جزئیات را بیاموزید، ممکن است بخواهید فصل ۲ را رد کنید و مستقیماً به فصل ۳ بروید و پس از یادگیری جزئیات به فصل ۲ بازگردید تا روی پروژه‌ای کار کنید. + +فصل ۵ به ساختارها (structs) و متدها می‌پردازد و فصل ۶ شامل enumerations (enums)، عبارات `match` و سازه کنترلی `if let` است. از ساختارها و enum‌ها برای ایجاد انواع سفارشی در راست استفاده خواهید کرد. + +در فصل ۷، درباره سیستم ماژول راست و قوانین حریم خصوصی برای سازمان‌دهی کد و رابط برنامه‌نویسی عمومی (API) آن یاد خواهید گرفت. فصل ۸ به بررسی برخی از ساختارهای داده مجموعه رایج که کتابخانه استاندارد ارائه می‌دهد، مانند vectors، strings و hash maps می‌پردازد. فصل ۹ فلسفه و تکنیک‌های مدیریت خطا در راست را بررسی می‌کند. + +فصل ۱۰ به مفاهیم جنریک‌ها، traits و lifetimes می‌پردازد که به شما این قدرت را می‌دهد تا کدی بنویسید که به انواع مختلف اعمال شود. فصل ۱۱ کاملاً درباره تست است که حتی با تضمین‌های ایمنی راست، برای اطمینان از درستی منطق برنامه شما ضروری است. در فصل ۱۲، پیاده‌سازی بخشی از ابزار خط فرمان `grep` که متن را در فایل‌ها جستجو می‌کند، خواهیم ساخت. برای این کار، از بسیاری از مفاهیمی که در فصل‌های قبلی مورد بحث قرار گرفتند استفاده خواهیم کرد. + +فصل ۱۳ به بررسی closures و iterators می‌پردازد: ویژگی‌هایی از راست که از زبان‌های برنامه‌نویسی تابعی آمده‌اند. در فصل ۱۴، Cargo را به طور عمیق‌تری بررسی خواهیم کرد و درباره بهترین روش‌ها برای اشتراک‌گذاری کتابخانه‌های خود با دیگران صحبت خواهیم کرد. فصل ۱۵ اشاره‌گر (Pointer)های هوشمند (smart pointers) ارائه‌شده توسط کتابخانه استاندارد و traitsی که قابلیت‌های آن‌ها را امکان‌پذیر می‌سازد بررسی می‌کند. + +در فصل ۱۶، با مدل‌های مختلف برنامه‌نویسی همروند +(concurrent) آشنا خواهیم شد و درباره‌ی اینکه چگونه +Rust به شما کمک می‌کند تا بدون ترس در چند thread +برنامه‌نویسی کنید صحبت می‌کنیم. در فصل ۱۷، +بر پایه‌ی آن مفاهیم، نگارش async و await را در +Rust بررسی می‌کنیم و همچنین به سراغ taskها، +futureها، و streamها می‌رویم که مدل همروندی سبک‌وزن +را فراهم می‌کنند. + +فصل ۱۸ به مقایسه‌ی شیوه‌های رایج در Rust با +اصول برنامه‌نویسی شی‌گرا می‌پردازد که ممکن است +پیش‌تر با آن‌ها آشنا باشید. فصل ۱۹ مرجعی است +برای الگوها (patterns) و pattern matching، که راهکارهایی +قدرتمند برای بیان مفاهیم در سراسر برنامه‌های Rust هستند. +فصل ۲۰ مجموعه‌ای متنوع از موضوعات پیشرفته +را در بر می‌گیرد، از جمله Rust ناایمن (unsafe)، +ماکروها، و مباحث بیشتری درباره‌ی lifetimeها، +traitها، نوع‌ها، تابع‌ها و closureها. + +در فصل ۲۱، پروژه‌ای را تکمیل می‌کنیم که در آن یک سرور وب چندرشته‌ای سطح پایین پیاده‌سازی خواهیم کرد! + +در نهایت، برخی ضمیمه‌ها شامل اطلاعات مفیدی +درباره‌ی زبان Rust هستند که به‌صورت مرجع‌گونه +ارائه شده‌اند. **ضمیمه‌ی الف** به کلمات کلیدی +Rust می‌پردازد، **ضمیمه‌ی ب** عملگرها و نمادهای +Rust را پوشش می‌دهد، **ضمیمه‌ی ج** traitهای +قابل‌مشتق موجود در کتابخانه‌ی استاندارد را بررسی +می‌کند، **ضمیمه‌ی د** به برخی ابزارهای مفید توسعه +می‌پردازد، و **ضمیمه‌ی ه** نگارش‌های مختلف Rust +را توضیح می‌دهد. در **ضمیمه‌ی و** می‌توانید +ترجمه‌های این کتاب را بیابید، و در **ضمیمه‌ی ی** +با روند توسعه‌ی Rust و مفهوم Rust شبانه (nightly) +آشنا خواهید شد. + + +هیچ روش نادرستی برای خواندن این کتاب وجود ندارد: اگر می‌خواهید به جلو بروید، این کار را انجام دهید! ممکن است مجبور شوید به فصل‌های قبلی بازگردید اگر با سردرگمی روبه‌رو شدید. اما هرچه برای شما مناسب است انجام دهید. -An important part of the process of learning Rust is learning how to read the -error messages the compiler displays: these will guide you toward working code. -As such, we’ll provide many examples that don’t compile along with the error -message the compiler will show you in each situation. Know that if you enter -and run a random example, it may not compile! Make sure you read the -surrounding text to see whether the example you’re trying to run is meant to -error. Ferris will also help you distinguish code that isn’t meant to work: +بخش مهمی از فرآیند یادگیری راست، یادگیری نحوه خواندن پیام‌های خطای کامپایلر است: این پیام‌ها شما را به سمت کدی که کار می‌کند هدایت می‌کنند. به همین دلیل، مثال‌های زیادی را ارائه می‌دهیم که کامپایل نمی‌شوند، همراه با پیام خطایی که کامپایلر در هر وضعیت نمایش می‌دهد. بدانید که اگر یک مثال تصادفی را وارد کنید و اجرا کنید، ممکن است کامپایل نشود! مطمئن شوید که متن اطراف را بخوانید تا ببینید آیا مثالی که می‌خواهید اجرا کنید قرار است خطا بدهد یا خیر. Ferris همچنین به شما کمک می‌کند کدی که قرار نیست کار کند را تشخیص دهید: -| Ferris | Meaning | +| Ferris | معنی | | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | -| Ferris with a question mark | This code does not compile! | -| Ferris throwing up their hands | This code panics! | -| Ferris with one claw up, shrugging | This code does not produce the desired behavior. | +| Ferris with a question mark | این کد کامپایل نمی‌شود! | +| Ferris throwing up their hands | این کد وحشت می‌کند! | +| Ferris with one claw up, shrugging | این کد رفتار مورد انتظار را تولید نمی‌کند. | -In most situations, we’ll lead you to the correct version of any code that -doesn’t compile. +در بیشتر موارد، شما را به نسخه صحیح هر کدی که کامپایل نمی‌شود هدایت خواهیم کرد. -## Source Code +## کد منبع -The source files from which this book is generated can be found on -[GitHub][book]. +فایل‌های منبعی که این کتاب از آن‌ها تولید می‌شود را می‌توانید در [GitHub][book] پیدا کنید. -[book]: https://github.com/rust-lang/book/tree/main/src +[book]: https://github.com/persian-rust/book/tree/main/src diff --git a/src/ch01-00-getting-started.md b/src/ch01-00-getting-started.md index ccb10e884e..1f2edbe1ec 100644 --- a/src/ch01-00-getting-started.md +++ b/src/ch01-00-getting-started.md @@ -1,8 +1,8 @@ -# Getting Started +# شروع به کار -Let’s start your Rust journey! There’s a lot to learn, but every journey starts -somewhere. In this chapter, we’ll discuss: +بیایید سفر خود به دنیای راست را آغاز کنیم! چیزهای زیادی برای یادگیری وجود دارد، اما هر سفری از جایی شروع می‌شود. در این فصل، درباره موارد زیر صحبت خواهیم کرد: + +- نصب راست بر روی لینوکس، macOS، و ویندوز +- نوشتن برنامه‌ای که `سلام دنیا!` را چاپ می‌کند +- استفاده از `cargo`، مدیر بسته و سیستم ساخت راست -- Installing Rust on Linux, macOS, and Windows -- Writing a program that prints `Hello, world!` -- Using `cargo`, Rust’s package manager and build system diff --git a/src/ch01-01-installation.md b/src/ch01-01-installation.md index cf2ca86db0..93caf4d674 100644 --- a/src/ch01-01-installation.md +++ b/src/ch01-01-installation.md @@ -1,154 +1,114 @@ -## Installation +## نصب -The first step is to install Rust. We’ll download Rust through `rustup`, a -command line tool for managing Rust versions and associated tools. You’ll need -an internet connection for the download. +اولین قدم نصب راست است. ما راست را از طریق `rustup` دانلود می‌کنیم، ابزاری خط فرمان برای مدیریت نسخه‌های راست و ابزارهای مربوطه. برای دانلود به اتصال اینترنتی نیاز دارید. -> Note: If you prefer not to use `rustup` for some reason, please see the -> [Other Rust Installation Methods page][otherinstall] for more options. +> توجه: اگر به هر دلیلی ترجیح می‌دهید از `rustup` استفاده نکنید، لطفاً صفحه [روش‌های نصب دیگر راست][otherinstall] را برای گزینه‌های بیشتر مشاهده کنید. -The following steps install the latest stable version of the Rust compiler. -Rust’s stability guarantees ensure that all the examples in the book that -compile will continue to compile with newer Rust versions. The output might -differ slightly between versions because Rust often improves error messages and -warnings. In other words, any newer, stable version of Rust you install using -these steps should work as expected with the content of this book. +مراحل زیر نسخه پایدار جدیدترین کامپایلر راست را نصب می‌کنند. تضمین‌های پایداری راست اطمینان می‌دهند که تمام مثال‌های کتاب که کامپایل می‌شوند، با نسخه‌های جدیدتر راست نیز کامپایل خواهند شد. خروجی ممکن است کمی متفاوت باشد، زیرا راست به طور مرتب پیغام‌های خطا و هشدارها را بهبود می‌بخشد. به عبارت دیگر، هر نسخه پایدار جدیدی که با این مراحل نصب کنید، باید با محتوای این کتاب به درستی کار کند. -> ### Command Line Notation +> ### یادداشت دستورات خط فرمان > -> In this chapter and throughout the book, we’ll show some commands used in the -> terminal. Lines that you should enter in a terminal all start with `$`. You -> don’t need to type the `$` character; it’s the command line prompt shown to -> indicate the start of each command. Lines that don’t start with `$` typically -> show the output of the previous command. Additionally, PowerShell-specific -> examples will use `>` rather than `$`. +> در این فصل و throughout the book، ما برخی از دستورات استفاده شده در ترمینال را نمایش خواهیم داد. خطوطی که باید در ترمینال وارد کنید، همگی با `$` شروع می‌شوند. شما نیازی به وارد کردن نماد `$` ندارید؛ این نماد نشان‌دهنده شروع هر دستور است. خطوطی که با `$` شروع نمی‌شوند معمولاً خروجی دستور قبلی را نشان می‌دهند. علاوه بر این، مثال‌های خاص PowerShell از `>` به جای `$` استفاده می‌کنند. -### Installing `rustup` on Linux or macOS +### نصب `rustup` در لینوکس یا macOS -If you’re using Linux or macOS, open a terminal and enter the following command: +اگر از لینوکس یا macOS استفاده می‌کنید، یک ترمینال باز کرده و دستور زیر را وارد کنید: ```console $ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh ``` -The command downloads a script and starts the installation of the `rustup` -tool, which installs the latest stable version of Rust. You might be prompted -for your password. If the install is successful, the following line will appear: +این دستور یک اسکریپت دانلود کرده و نصب ابزار `rustup` را آغاز می‌کند که نسخه پایدار جدید راست را نصب می‌کند. ممکن است از شما خواسته شود تا رمز عبور خود را وارد کنید. اگر نصب موفقیت‌آمیز بود، خط زیر ظاهر می‌شود: ```text Rust is installed now. Great! ``` -You will also need a _linker_, which is a program that Rust uses to join its -compiled outputs into one file. It is likely you already have one. If you get -linker errors, you should install a C compiler, which will typically include a -linker. A C compiler is also useful because some common Rust packages depend on -C code and will need a C compiler. +همچنین به یک _لینکر_ نیاز خواهید داشت که برنامه‌ای است که راست از آن برای ترکیب خروجی‌های کامپایل شده خود به یک فایل استفاده می‌کند. احتمالاً شما یک لینکر دارید. اگر با ارورهای لینکر روبه‌رو شدید، باید یک کامپایلر C نصب کنید که معمولاً لینکر را نیز شامل می‌شود. یک کامپایلر C همچنین مفید است زیرا برخی از پکیج‌های رایج راست به کد C وابسته‌اند و به یک کامپایلر C نیاز دارند. -On macOS, you can get a C compiler by running: +برای نصب کامپایلر C در macOS، دستور زیر را اجرا کنید: ```console $ xcode-select --install ``` -Linux users should generally install GCC or Clang, according to their -distribution’s documentation. For example, if you use Ubuntu, you can install -the `build-essential` package. +کاربران لینوکس معمولاً باید GCC یا Clang را طبق مستندات توزیع خود نصب کنند. برای مثال، اگر از اوبونتو استفاده می‌کنید، می‌توانید پکیج `build-essential` را نصب کنید. -### Installing `rustup` on Windows +### نصب `rustup` در ویندوز -On Windows, go to [https://www.rust-lang.org/tools/install][install] and follow -the instructions for installing Rust. At some point in the installation, you’ll -be prompted to install Visual Studio. This provides a linker and the native -libraries needed to compile programs. If you need more help with this step, see -[https://rust-lang.github.io/rustup/installation/windows-msvc.html][msvc] +در ویندوز، به [https://www.rust-lang.org/tools/install][install] بروید و دستورالعمل‌های نصب راست را دنبال کنید. در یک مرحله از نصب، از شما خواسته می‌شود تا Visual Studio را نصب کنید. این ابزار یک لینکر و کتابخانه‌های بومی لازم برای کامپایل برنامه‌ها را فراهم می‌کند. اگر به کمک بیشتری نیاز دارید، این صفحه را مشاهده کنید [https://rust-lang.github.io/rustup/installation/windows-msvc.html][msvc] -The rest of this book uses commands that work in both _cmd.exe_ and PowerShell. -If there are specific differences, we’ll explain which to use. +بقیه کتاب از دستورات استفاده شده در _cmd.exe_ و PowerShell استفاده می‌کند. اگر تفاوت‌های خاصی وجود داشته باشد، توضیح خواهیم داد که کدام را باید استفاده کنید. -### Troubleshooting +### عیب‌یابی -To check whether you have Rust installed correctly, open a shell and enter this -line: +برای بررسی اینکه راست به درستی نصب شده است یا خیر، یک شل باز کرده و این دستور را وارد کنید: ```console $ rustc --version ``` -You should see the version number, commit hash, and commit date for the latest -stable version that has been released, in the following format: +باید شماره نسخه، هش کمیّت و تاریخ کمیّت برای جدیدترین نسخه پایدار منتشر شده را به صورت زیر ببینید: ```text rustc x.y.z (abcabcabc yyyy-mm-dd) ``` -If you see this information, you have installed Rust successfully! If you don’t -see this information, check that Rust is in your `%PATH%` system variable as -follows. +اگر این اطلاعات را مشاهده کردید، راست به درستی نصب شده است! اگر این اطلاعات را مشاهده نکردید، بررسی کنید که راست در متغیر سیستم `%PATH%` شما قرار دارد. -In Windows CMD, use: +در CMD ویندوز، از دستور زیر استفاده کنید: ```console > echo %PATH% ``` -In PowerShell, use: +در PowerShell، از دستور زیر استفاده کنید: ```powershell > echo $env:Path ``` -In Linux and macOS, use: +در لینوکس و macOS، از دستور زیر استفاده کنید: ```console $ echo $PATH ``` -If that’s all correct and Rust still isn’t working, there are a number of -places you can get help. Find out how to get in touch with other Rustaceans (a -silly nickname we call ourselves) on [the community page][community]. +اگر همه چیز درست باشد و راست همچنان کار نکند، منابع زیادی برای کمک وجود دارد. برای تماس با سایر راست‌نویسان (لقب خنده‌داری که خودمان به کار می‌بریم)، به صفحه [اجتماع][community] مراجعه کنید. -### Updating and Uninstalling +### بروزرسانی و حذف نصب -Once Rust is installed via `rustup`, updating to a newly released version is -easy. From your shell, run the following update script: +بعد از نصب راست از طریق `rustup`، بروزرسانی به نسخه جدید بسیار آسان است. از شل خود دستور زیر را اجرا کنید: ```console $ rustup update ``` -To uninstall Rust and `rustup`, run the following uninstall script from your -shell: +برای حذف نصب راست و `rustup`، اسکریپت حذف زیر را از شل خود اجرا کنید: ```console $ rustup self uninstall ``` -### Local Documentation +### مستندات محلی -The installation of Rust also includes a local copy of the documentation so -that you can read it offline. Run `rustup doc` to open the local documentation -in your browser. +نصب راست همچنین شامل یک نسخه محلی از مستندات است تا بتوانید آن را به صورت آفلاین مطالعه کنید. برای باز کردن مستندات محلی در مرورگر خود، دستور `rustup doc` را اجرا کنید. -Any time a type or function is provided by the standard library and you’re not -sure what it does or how to use it, use the application programming interface -(API) documentation to find out! +هر زمان که از یک نوع یا تابع ارائه‌شده توسط کتابخانه استاندارد استفاده می‌کنید و مطمئن نیستید که چه کار می‌کند یا چگونه از آن استفاده کنید، از مستندات رابط برنامه‌نویسی (API) برای یافتن آن استفاده کنید! -### Text Editors and Integrated Development Environments +### ویرایشگرهای متن و محیط‌های توسعه یکپارچه -This book makes no assumptions about what tools you use to author Rust code. -Just about any text editor will get the job done! However, many text editors and -integrated development environments (IDEs) have built-in support for Rust. You -can always find a fairly current list of many editors and IDEs on [the tools -page][tools] on the Rust website. +این کتاب هیچ فرضی درباره ابزارهایی که برای نوشتن کد راست استفاده می‌کنید، ندارد. تقریباً هر ویرایشگر متنی کار را انجام می‌دهد! با این حال، بسیاری از ویرایشگرها و محیط‌های توسعه یکپارچه (IDE) پشتیبانی داخلی برای راست دارند. همیشه می‌توانید فهرست نسبتاً جدیدی از بسیاری از ویرایشگرها و IDEها را در [صفحه ابزارها][tools] در وب‌سایت راست پیدا کنید. -### Working Offline with This Book +### کار با این کتاب به‌صورت آفلاین -In several examples, we will use Rust packages beyond the standard library. To -work through those examples, you will either need to have an internet connection -or to have downloaded those dependencies ahead of time. To download the -dependencies ahead of time, you can run the following commands. (We’ll explain -what `cargo` is and what each of these commands does in detail later.) +در چندین مثال، از پکیج‌هایی در Rust استفاده خواهیم کرد +که فراتر از کتابخانه استاندارد هستند. برای اجرای این +مثال‌ها، یا باید به اینترنت متصل باشید یا اینکه +پیشاپیش این وابستگی‌ها را دانلود کرده باشید. +برای دانلود پیشاپیش این وابستگی‌ها، می‌توانید +دستورات زیر را اجرا کنید. (در ادامه، `cargo` و +عملکرد هرکدام از این دستورات را به‌طور کامل توضیح خواهیم داد.) ```console $ cargo new get-dependencies @@ -156,11 +116,15 @@ $ cd get-dependencies $ cargo add rand@0.8.5 trpl@0.2.0 ``` -This will cache the downloads for these packages so you will not need to -download them later. Once you have run this command, you do not need to keep the -`get-dependencies` folder. If you have run this command, you can use the -`--offline` flag with all `cargo` commands in the rest of the book to use these -cached versions instead of attempting to use the network. +این کار نسخه‌های دانلودشده‌ی این پکیج‌ها را در کش +ذخیره می‌کند تا در آینده نیازی به دانلود مجدد نباشد. +پس از اجرای این دستورات، نیازی به نگه‌داشتن +پوشه‌ی `get-dependencies` ندارید. اگر این دستورات +را اجرا کرده باشید، می‌توانید در باقی قسمت‌های +این کتاب از فلگ `--offline` همراه با تمام دستورات +`cargo` استفاده کنید تا به‌جای اتصال به شبکه، +از نسخه‌های کش‌شده بهره ببرید. + [otherinstall]: https://forge.rust-lang.org/infra/other-installation-methods.html [install]: https://www.rust-lang.org/tools/install diff --git a/src/ch01-02-hello-world.md b/src/ch01-02-hello-world.md index 6c74dc1f75..aecc77e830 100644 --- a/src/ch01-02-hello-world.md +++ b/src/ch01-02-hello-world.md @@ -1,28 +1,17 @@ -## Hello, World! +## سلام، دنیا! -Now that you’ve installed Rust, it’s time to write your first Rust program. -It’s traditional when learning a new language to write a little program that -prints the text `Hello, world!` to the screen, so we’ll do the same here! +حالا که Rust را نصب کرده‌اید، وقت آن است که اولین برنامه‌ی Rust خود را بنویسید. +وقتی زبان جدیدی را یاد می‌گیرید، معمولاً یک برنامه کوچک می‌نویسید که متن `Hello, world!` را به صفحه نمایش چاپ کند، پس ما هم همین کار را خواهیم کرد! -> Note: This book assumes basic familiarity with the command line. Rust makes -> no specific demands about your editing or tooling or where your code lives, so -> if you prefer to use an integrated development environment (IDE) instead of -> the command line, feel free to use your favorite IDE. Many IDEs now have some -> degree of Rust support; check the IDE’s documentation for details. The Rust -> team has been focusing on enabling great IDE support via `rust-analyzer`. See -> [Appendix D][devtools] for more details. +> نکته: این کتاب فرض می‌کند که شما با خط فرمان آشنایی پایه‌ای دارید. Rust هیچ‌گونه الزامی در مورد ویرایش یا ابزارهای شما یا جایی که کد شما قرار دارد ندارد، بنابراین اگر ترجیح می‌دهید از یک محیط توسعه یکپارچه (IDE) به جای خط فرمان استفاده کنید، می‌توانید از IDE مورد علاقه خود استفاده کنید. بسیاری از IDE‌ها اکنون از Rust پشتیبانی می‌کنند؛ برای جزئیات، مستندات IDE خود را بررسی کنید. تیم Rust تمرکز خود را بر enabling پشتیبانی خوب از IDE از طریق `rust-analyzer` گذاشته است. برای جزئیات بیشتر، به [ضمیمه د][devtools] مراجعه کنید. -### Creating a Project Directory +### ایجاد یک دایرکتوری پروژه -You’ll start by making a directory to store your Rust code. It doesn’t matter -to Rust where your code lives, but for the exercises and projects in this book, -we suggest making a _projects_ directory in your home directory and keeping all -your projects there. +شما با ایجاد یک دایرکتوری برای ذخیره کدهای Rust خود شروع خواهید کرد. برای Rust مهم نیست که کد شما کجا قرار دارد، اما برای تمرین‌ها و پروژه‌های این کتاب، پیشنهاد می‌کنیم یک دایرکتوری _projects_ در دایرکتوری خانه‌تان بسازید و تمام پروژه‌هایتان را در آن نگهدارید. -Open a terminal and enter the following commands to make a _projects_ directory -and a directory for the “Hello, world!” project within the _projects_ directory. +یک ترمینال باز کنید و دستورات زیر را وارد کنید تا یک دایرکتوری _projects_ و یک دایرکتوری برای پروژه‌ی "Hello, world!" در داخل دایرکتوری _projects_ ایجاد کنید. -For Linux, macOS, and PowerShell on Windows, enter this: +برای لینوکس، macOS، و PowerShell در ویندوز، این دستورات را وارد کنید: ```console $ mkdir ~/projects @@ -31,7 +20,7 @@ $ mkdir hello_world $ cd hello_world ``` -For Windows CMD, enter this: +برای CMD ویندوز، این دستورات را وارد کنید: ```cmd > mkdir "%USERPROFILE%\projects" @@ -40,16 +29,13 @@ For Windows CMD, enter this: > cd hello_world ``` -### Writing and Running a Rust Program +### نوشتن و اجرای یک برنامه Rust -Next, make a new source file and call it _main.rs_. Rust files always end with -the _.rs_ extension. If you’re using more than one word in your filename, the -convention is to use an underscore to separate them. For example, use -_hello_world.rs_ rather than _helloworld.rs_. +حالا یک فایل سورس جدید بسازید و آن را _main.rs_ نام‌گذاری کنید. فایل‌های Rust همیشه با پسوند _.rs_ تمام می‌شوند. اگر از بیش از یک کلمه در نام فایل استفاده می‌کنید، سنت معمول این است که از خط تیره زیر برای جدا کردن آنها استفاده کنید. به عنوان مثال، از _hello_world.rs_ به جای _helloworld.rs_ استفاده کنید. -Now open the _main.rs_ file you just created and enter the code in Listing 1-1. +حالا فایل _main.rs_ که تازه ایجاد کرده‌اید را باز کنید و کد موجود در فهرست 1-1 را وارد کنید. -+ ```rust fn main() { @@ -59,9 +45,8 @@ fn main() { -Save the file and go back to your terminal window in the -_~/projects/hello_world_ directory. On Linux or macOS, enter the following -commands to compile and run the file: +فایل را ذخیره کنید و به پنجره ترمینال خود در دایرکتوری +_~/projects/hello_world_ برگردید. در لینوکس یا macOS، دستورات زیر را وارد کنید تا فایل را کامپایل کرده و اجرا کنید: ```console $ rustc main.rs @@ -69,7 +54,7 @@ $ ./main Hello, world! ``` -On Windows, enter the command `.\main` instead of `./main`: +در ویندوز، به جای `./main` دستور `.\main.exe` را وارد کنید: ```powershell > rustc main.rs @@ -77,18 +62,13 @@ On Windows, enter the command `.\main` instead of `./main`: Hello, world! ``` -Regardless of your operating system, the string `Hello, world!` should print to -the terminal. If you don’t see this output, refer back to the -[“Troubleshooting”][troubleshooting] part of the Installation -section for ways to get help. +صرف‌نظر از سیستم‌عامل شما، رشته `Hello, world!` باید در ترمینال چاپ شود. اگر این خروجی را مشاهده نکردید، به بخش [“رفع مشکلات”][troubleshooting] در قسمت نصب مراجعه کنید تا روش‌های دریافت کمک را بیابید. -If `Hello, world!` did print, congratulations! You’ve officially written a Rust -program. That makes you a Rust programmer—welcome! +اگر `Hello, world!` چاپ شد، تبریک می‌گوییم! شما به طور رسمی یک برنامه نویس Rust شده‌اید—خوش آمدید! -### Anatomy of a Rust Program +### آناتومی یک برنامه Rust -Let’s review this “Hello, world!” program in detail. Here’s the first piece of -the puzzle: +بیایید این برنامه "Hello, world!" را به طور دقیق بررسی کنیم. این اولین بخش معما است: ```rust fn main() { @@ -96,105 +76,77 @@ fn main() { } ``` -These lines define a function named `main`. The `main` function is special: it -is always the first code that runs in every executable Rust program. Here, the -first line declares a function named `main` that has no parameters and returns -nothing. If there were parameters, they would go inside the parentheses `()`. +این خطوط یک تابع به نام `main` تعریف می‌کنند. تابع `main` خاص است: همیشه اولین کدی است که در هر برنامه Rust اجرایی اجرا می‌شود. در اینجا، خط اول یک تابع به نام `main` اعلام می‌کند که هیچ پارامتر ندارد و هیچ چیزی را برنمی‌گرداند. اگر پارامترهایی وجود داشتند، آن‌ها داخل پرانتزهای `()` قرار می‌گرفتند. -The function body is wrapped in `{}`. Rust requires curly brackets around all -function bodies. It’s good style to place the opening curly bracket on the same -line as the function declaration, adding one space in between. +بدن تابع در `{}` قرار دارد. Rust از آکولادها برای احاطه کردن تمام بدنه‌های توابع استفاده می‌کند. این یک سبک خوب است که آکولاد باز را در همان خط اعلام تابع قرار دهید و یک فضای خالی بین آن‌ها اضافه کنید. -> Note: If you want to stick to a standard style across Rust projects, you can -> use an automatic formatter tool called `rustfmt` to format your code in a -> particular style (more on `rustfmt` in -> [Appendix D][devtools]). The Rust team has included this tool -> with the standard Rust distribution, as `rustc` is, so it should already be -> installed on your computer! +> نکته: اگر می‌خواهید در پروژه‌های Rust خود از یک سبک استاندارد پیروی کنید، می‌توانید از ابزاری به نام `rustfmt` برای فرمت کردن کد خود در یک سبک خاص استفاده کنید (بیشتر در مورد `rustfmt` در [ضمیمه د][devtools]). تیم Rust این ابزار را همراه با توزیع استاندارد Rust شامل کرده است، همانطور که `rustc` است، بنابراین باید قبلاً روی کامپیوتر شما نصب شده باشد! -The body of the `main` function holds the following code: +بدن تابع `main` شامل کد زیر است: ```rust println!("Hello, world!"); ``` -This line does all the work in this little program: it prints text to the -screen. There are three important details to notice here. +این خط، تمام کار این برنامه‌ی کوچک را انجام می‌دهد: +متنی را روی صفحه چاپ می‌کند. در اینجا سه نکته‌ی +مهم وجود دارد که باید به آن‌ها توجه کنید. -First, `println!` calls a Rust macro. If it had called a function instead, it -would be entered as `println` (without the `!`). Rust macros are a way to write -code that generates code to extend Rust syntax, and we’ll discuss them in more -detail in [Chapter 20][ch20-macros]. For now, you just need to -know that using a `!` means that you’re calling a macro instead of a normal -function and that macros don’t always follow the same rules as functions. +نخست، `println!` یک ماکرو در Rust را فراخوانی می‌کند. +اگر به‌جای ماکرو، یک تابع فراخوانی شده بود، +نگارش آن به‌صورت `println` (بدون `!`) می‌بود. +ماکروهای Rust روشی برای نوشتن کدی هستند که +کد دیگری تولید می‌کنند و به گسترش نگارش Rust +کمک می‌کنند. در فصل بیستم [Chapter 20][ch20-macros] +آن‌ها را با جزئیات بیشتری بررسی خواهیم کرد. +در حال حاضر تنها کافی‌ست بدانید که استفاده از +`!` به این معناست که در حال فراخوانی یک ماکرو هستید +و ماکروها همیشه از همان قواعدی که توابع پیروی می‌کنند، +تبعیت نمی‌کنند. -Second, you see the `"Hello, world!"` string. We pass this string as an argument -to `println!`, and the string is printed to the screen. +دوم، شما رشته `"Hello, world!"` را مشاهده می‌کنید. این رشته را به عنوان آرگومان به `println!` می‌دهیم و این رشته به صفحه نمایش چاپ می‌شود. -Third, we end the line with a semicolon (`;`), which indicates that this -expression is over and the next one is ready to begin. Most lines of Rust code -end with a semicolon. +سوم، خط را با یک نقطه‌ویرگول (`;`) تمام می‌کنیم که نشان می‌دهد این عبارت تمام شده و عبارت بعدی آماده شروع است. بیشتر خطوط کد Rust با نقطه‌ویرگول تمام می‌شوند. -### Compiling and Running Are Separate Steps +### کامپایل کردن و اجرا کردن مراحل جداگانه هستند -You’ve just run a newly created program, so let’s examine each step in the -process. +شما به تازگی یک برنامه جدید ایجاد شده را اجرا کرده‌اید، بنابراین بیایید هر مرحله از فرآیند را بررسی کنیم. -Before running a Rust program, you must compile it using the Rust compiler by -entering the `rustc` command and passing it the name of your source file, like -this: +قبل از اجرای یک برنامه Rust، باید آن را با استفاده از کامپایلر Rust کامپایل کنید. برای این کار باید دستور `rustc` را وارد کرده و نام فایل سورس خود را به آن بدهید، مانند این: ```console $ rustc main.rs ``` -If you have a C or C++ background, you’ll notice that this is similar to `gcc` -or `clang`. After compiling successfully, Rust outputs a binary executable. +اگر پیش‌زمینه‌ای از C یا C++ دارید، متوجه خواهید شد که این مشابه دستور `gcc` یا `clang` است. پس از کامپایل موفق، Rust یک فایل اجرایی باینری تولید می‌کند. -On Linux, macOS, and PowerShell on Windows, you can see the executable by -entering the `ls` command in your shell: +در لینوکس، macOS و PowerShell در ویندوز، می‌توانید فایل اجرایی را با وارد کردن دستور `ls` در شل خود مشاهده کنید: ```console $ ls main main.rs ``` -On Linux and macOS, you’ll see two files. With PowerShell on Windows, you’ll -see the same three files that you would see using CMD. With CMD on Windows, you -would enter the following: +در لینوکس و macOS، شما دو فایل خواهید دید. در PowerShell در ویندوز، همان سه فایلی را که با CMD می‌بینید مشاهده خواهید کرد. در CMD در ویندوز، باید دستور زیر را وارد کنید: ```cmd -> dir /B %= the /B option says to only show the file names =% +> dir /B %= گزینه /B می‌گوید که فقط نام فایل‌ها نمایش داده شود =% main.exe main.pdb main.rs ``` -This shows the source code file with the _.rs_ extension, the executable file -(_main.exe_ on Windows, but _main_ on all other platforms), and, when using -Windows, a file containing debugging information with the _.pdb_ extension. -From here, you run the _main_ or _main.exe_ file, like this: +این لیست فایل سورس با پسوند _.rs_، فایل اجرایی (_main.exe_ در ویندوز، اما _main_ در سایر پلتفرم‌ها)، و در صورت استفاده از ویندوز، یک فایل شامل اطلاعات دیباگ با پسوند _.pdb_ را نشان می‌دهد. از اینجا، شما فایل _main_ یا _main.exe_ را اجرا می‌کنید، مانند این: ```console -$ ./main # or .\main on Windows +$ ./main # یا .\main.exe در ویندوز ``` -If your _main.rs_ is your “Hello, world!” program, this line prints `Hello, -world!` to your terminal. - -If you’re more familiar with a dynamic language, such as Ruby, Python, or -JavaScript, you might not be used to compiling and running a program as -separate steps. Rust is an _ahead-of-time compiled_ language, meaning you can -compile a program and give the executable to someone else, and they can run it -even without having Rust installed. If you give someone a _.rb_, _.py_, or -_.js_ file, they need to have a Ruby, Python, or JavaScript implementation -installed (respectively). But in those languages, you only need one command to -compile and run your program. Everything is a trade-off in language design. - -Just compiling with `rustc` is fine for simple programs, but as your project -grows, you’ll want to manage all the options and make it easy to share your -code. Next, we’ll introduce you to the Cargo tool, which will help you write -real-world Rust programs. +اگر فایل _main.rs_ شما برنامه "Hello, world!" باشد، این خط `Hello, world!` را در ترمینال شما چاپ می‌کند. + +اگر با زبان‌های داینامیک مانند Ruby، Python یا JavaScript آشنایی بیشتری دارید، ممکن است عادت نداشته باشید که کامپایل و اجرای یک برنامه را به عنوان مراحل جداگانه انجام دهید. Rust یک زبان _کامپایل شده پیش از زمان_ است، به این معنی که شما می‌توانید یک برنامه را کامپایل کرده و فایل اجرایی را به شخص دیگری بدهید تا آن را اجرا کند، حتی بدون اینکه Rust روی سیستم آن شخص نصب شده باشد. اگر به کسی فایل _.rb_، _.py_ یا _.js_ بدهید، آن‌ها نیاز به نصب پیاده‌سازی Ruby، Python یا JavaScript (به ترتیب) دارند. اما در این زبان‌ها، شما فقط به یک دستور نیاز دارید تا برنامه خود را کامپایل و اجرا کنید. همه چیز در طراحی زبان‌ها یک تعادل است. + +فقط با کامپایل کردن با `rustc` برای برنامه‌های ساده کافی است، اما با رشد پروژه شما، می‌خواهید تمام گزینه‌ها را مدیریت کرده و اشتراک‌گذاری کد خود را آسان کنید. در ادامه، ما ابزار Cargo را معرفی خواهیم کرد که به شما کمک می‌کند برنامه‌های واقعی Rust بنویسید. [troubleshooting]: ch01-01-installation.html#troubleshooting [devtools]: appendix-04-useful-development-tools.html diff --git a/src/ch01-03-hello-cargo.md b/src/ch01-03-hello-cargo.md index 100239322c..6706648561 100644 --- a/src/ch01-03-hello-cargo.md +++ b/src/ch01-03-hello-cargo.md @@ -1,64 +1,38 @@ -## Hello, Cargo! - -Cargo is Rust’s build system and package manager. Most Rustaceans use this tool -to manage their Rust projects because Cargo handles a lot of tasks for you, -such as building your code, downloading the libraries your code depends on, and -building those libraries. (We call the libraries that your code needs -_dependencies_.) - -The simplest Rust programs, like the one we’ve written so far, don’t have any -dependencies. If we had built the “Hello, world!” project with Cargo, it would -only use the part of Cargo that handles building your code. As you write more -complex Rust programs, you’ll add dependencies, and if you start a project -using Cargo, adding dependencies will be much easier to do. - -Because the vast majority of Rust projects use Cargo, the rest of this book -assumes that you’re using Cargo too. Cargo comes installed with Rust if you -used the official installers discussed in the -[“Installation”][installation] section. If you installed Rust -through some other means, check whether Cargo is installed by entering the -following in your terminal: +## سلام، Cargo! + +Cargo سیستم ساخت و مدیر بسته‌های Rust است. بیشتر Rustacean ها از این ابزار برای مدیریت پروژه‌های Rust خود استفاده می‌کنند زیرا Cargo بسیاری از وظایف را برای شما انجام می‌دهد، مانند ساختن کد شما، دانلود کتابخانه‌هایی که کد شما به آن‌ها وابسته است، و ساختن آن کتابخانه‌ها. (ما به کتابخانه‌هایی که کد شما به آن‌ها نیاز دارد _وابستگی‌ها_ می‌گوییم.) + +ساده‌ترین برنامه‌های Rust، مانند برنامه‌ای که تا کنون نوشته‌ایم، هیچ وابستگی‌ای ندارند. اگر پروژه "Hello, world!" را با Cargo می‌ساختیم، فقط از بخشی از Cargo استفاده می‌کرد که مسئول ساختن کد شما است. هنگامی که برنامه‌های پیچیده‌تری در Rust بنویسید، وابستگی‌ها را اضافه خواهید کرد و اگر پروژه‌ای را با استفاده از Cargo شروع کنید، اضافه کردن وابستگی‌ها بسیار راحت‌تر خواهد بود. + +به دلیل اینکه اکثریت عظیم پروژه‌های Rust از Cargo استفاده می‌کنند، بقیه این کتاب فرض می‌کند که شما نیز از Cargo استفاده می‌کنید. Cargo با Rust نصب می‌شود اگر از نصب‌کننده‌های رسمی که در بخش +[“نصب”][installation] بحث شده‌اند استفاده کرده باشید. اگر Rust را از طریق روش‌های دیگری نصب کرده‌اید، بررسی کنید که آیا Cargo نصب شده است یا نه با وارد کردن دستور زیر در ترمینال خود: ```console $ cargo --version ``` -If you see a version number, you have it! If you see an error, such as `command -not found`, look at the documentation for your method of installation to -determine how to install Cargo separately. +اگر شماره نسخه‌ای مشاهده کردید، آن را دارید! اگر خطای `command not found` را دیدید، به مستندات روش نصب خود مراجعه کنید تا نحوه نصب جداگانه Cargo را پیدا کنید. -### Creating a Project with Cargo +### ایجاد یک پروژه با Cargo -Let’s create a new project using Cargo and look at how it differs from our -original “Hello, world!” project. Navigate back to your _projects_ directory -(or wherever you decided to store your code). Then, on any operating system, -run the following: +بیایید یک پروژه جدید با استفاده از Cargo بسازیم و ببینیم چگونه از پروژه اولیه "Hello, world!" ما متفاوت است. به دایرکتوری _projects_ خود بروید (یا هر جایی که تصمیم گرفته‌اید کد خود را ذخیره کنید). سپس، در هر سیستم‌عاملی، دستور زیر را وارد کنید: ```console $ cargo new hello_cargo $ cd hello_cargo ``` -The first command creates a new directory and project called _hello_cargo_. -We’ve named our project _hello_cargo_, and Cargo creates its files in a -directory of the same name. +دستور اول یک دایرکتوری جدید به نام _hello_cargo_ ایجاد می‌کند و پروژه‌ای به همین نام ایجاد می‌کند. ما پروژه خود را _hello_cargo_ نام‌گذاری کرده‌ایم و Cargo فایل‌های خود را در دایرکتوری به همین نام ایجاد می‌کند. -Go into the _hello_cargo_ directory and list the files. You’ll see that Cargo -has generated two files and one directory for us: a _Cargo.toml_ file and a -_src_ directory with a _main.rs_ file inside. +به دایرکتوری _hello_cargo_ بروید و فایل‌ها را لیست کنید. خواهید دید که Cargo دو فایل و یک دایرکتوری برای ما ایجاد کرده است: یک فایل _Cargo.toml_ و یک دایرکتوری _src_ که داخل آن یک فایل _main.rs_ است. -It has also initialized a new Git repository along with a _.gitignore_ file. -Git files won’t be generated if you run `cargo new` within an existing Git -repository; you can override this behavior by using `cargo new --vcs=git`. +همچنین یک مخزن Git جدید به همراه یک فایل _.gitignore_ ایجاد شده است. فایل‌های Git در صورتی که دستور `cargo new` را در یک مخزن Git موجود اجرا کنید، ایجاد نمی‌شوند؛ می‌توانید این رفتار را با استفاده از `cargo new --vcs=git` لغو کنید. -> Note: Git is a common version control system. You can change `cargo new` to -> use a different version control system or no version control system by using -> the `--vcs` flag. Run `cargo new --help` to see the available options. +> نکته: Git یک سیستم کنترل نسخه رایج است. شما می‌توانید دستور `cargo new` را تغییر دهید تا از سیستم کنترل نسخه‌ای متفاوت یا هیچ سیستم کنترل نسخه‌ای استفاده کند با استفاده از پرچم `--vcs`. برای دیدن گزینه‌های موجود، دستور `cargo new --help` را اجرا کنید. -Open _Cargo.toml_ in your text editor of choice. It should look similar to the -code in Listing 1-2. +فایل _Cargo.toml_ را در ویرایشگر متن دلخواه خود باز کنید. این فایل باید مشابه کدی باشد که در فهرست 1-2 آمده است. -+ ```toml [package] @@ -71,23 +45,15 @@ edition = "2024" -This file is in the [_TOML_][toml] (_Tom’s Obvious, Minimal -Language_) format, which is Cargo’s configuration format. +این فایل در فرمت [_TOML_][toml] (_زبان ساده و آشکار تام_) است که فرمت پیکربندی Cargo است. -The first line, `[package]`, is a section heading that indicates that the -following statements are configuring a package. As we add more information to -this file, we’ll add other sections. +خط اول، `[package]`، یک عنوان بخش است که نشان می‌دهد بیانیه‌های بعدی در حال پیکربندی یک بسته هستند. همانطور که اطلاعات بیشتری به این فایل اضافه می‌کنیم، بخش‌های دیگری را اضافه خواهیم کرد. -The next three lines set the configuration information Cargo needs to compile -your program: the name, the version, and the edition of Rust to use. We’ll talk -about the `edition` key in [Appendix E][appendix-e]. +سه خط بعدی اطلاعات پیکربندی‌ای را تنظیم می‌کنند که Cargo برای کامپایل برنامه شما به آن‌ها نیاز دارد: نام، نسخه و نسخه‌ای از Rust که باید استفاده شود. در مورد کلید `edition` در [ضمیمه ه][appendix-e] صحبت خواهیم کرد. -The last line, `[dependencies]`, is the start of a section for you to list any -of your project’s dependencies. In Rust, packages of code are referred to as -_crates_. We won’t need any other crates for this project, but we will in the -first project in Chapter 2, so we’ll use this dependencies section then. +آخرین خط، `[dependencies]`، شروع یک بخش است که شما باید وابستگی‌های پروژه خود را در آن ذکر کنید. در Rust، بسته‌های کد به نام _کرِیت‌ها_ شناخته می‌شوند. برای این پروژه نیازی به کرِیت‌های دیگر نداریم، اما در پروژه اول فصل 2 به آن‌ها نیاز خواهیم داشت، بنابراین در آن زمان از این بخش وابستگی‌ها استفاده خواهیم کرد. -Now open _src/main.rs_ and take a look: +حالا فایل _src/main.rs_ را باز کنید و نگاهی بیندازید: Filename: src/main.rs @@ -97,28 +63,15 @@ fn main() { } ``` -Cargo has generated a “Hello, world!” program for you, just like the one we -wrote in Listing 1-1! So far, the differences between our project and the -project Cargo generated are that Cargo placed the code in the _src_ directory -and we have a _Cargo.toml_ configuration file in the top directory. +Cargo یک برنامه "Hello, world!" برای شما ایجاد کرده است، درست مانند برنامه‌ای که در فهرست 1-1 نوشتیم! تا کنون، تفاوت‌های بین پروژه ما و پروژه‌ای که Cargo ایجاد کرده این است که Cargo کد را در دایرکتوری _src_ قرار داده و ما یک فایل پیکربندی _Cargo.toml_ در دایرکتوری بالای پروژه داریم. -Cargo expects your source files to live inside the _src_ directory. The -top-level project directory is just for README files, license information, -configuration files, and anything else not related to your code. Using Cargo -helps you organize your projects. There’s a place for everything, and -everything is in its place. +Cargo انتظار دارد که فایل‌های منبع شما داخل دایرکتوری _src_ قرار داشته باشند. دایرکتوری بالای پروژه فقط برای فایل‌های README، اطلاعات مجوز، فایل‌های پیکربندی و هر چیز دیگری که مربوط به کد شما نباشد، استفاده می‌شود. استفاده از Cargo به شما کمک می‌کند پروژه‌هایتان را سازماندهی کنید. برای هر چیز جایی وجود دارد و همه چیز در جای خود قرار دارد. -If you started a project that doesn’t use Cargo, as we did with the “Hello, -world!” project, you can convert it to a project that does use Cargo. Move the -project code into the _src_ directory and create an appropriate _Cargo.toml_ -file. One easy way to get that _Cargo.toml_ file is to run `cargo init`, which -will create it for you automatically. +اگر پروژه‌ای شروع کرده‌اید که از Cargo استفاده نمی‌کند، همانطور که در پروژه "Hello, world!" انجام دادیم، می‌توانید آن را به پروژه‌ای که از Cargo استفاده می‌کند تبدیل کنید. کد پروژه را به دایرکتوری _src_ منتقل کرده و یک فایل _Cargo.toml_ مناسب ایجاد کنید. یکی از راه‌های آسان برای به‌دست آوردن آن فایل _Cargo.toml_ این است که دستور `cargo init` را اجرا کنید که به‌طور خودکار آن را برای شما ایجاد می‌کند. -### Building and Running a Cargo Project +### ساخت و اجرای پروژه با Cargo -Now let’s look at what’s different when we build and run the “Hello, world!” -program with Cargo! From your _hello_cargo_ directory, build your project by -entering the following command: +حالا بیایید ببینیم که چه تفاوتی در زمانی که برنامه "Hello, world!" را با Cargo می‌سازیم و اجرا می‌کنیم وجود دارد! از دایرکتوری _hello_cargo_ خود، پروژه را با وارد کردن دستور زیر بسازید: ```console $ cargo build @@ -126,26 +79,16 @@ $ cargo build Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs ``` -This command creates an executable file in _target/debug/hello_cargo_ (or -_target\debug\hello_cargo.exe_ on Windows) rather than in your current -directory. Because the default build is a debug build, Cargo puts the binary in -a directory named _debug_. You can run the executable with this command: +این دستور یک فایل اجرایی در _target/debug/hello_cargo_ (یا _target\debug\hello_cargo.exe_ در ویندوز) ایجاد می‌کند به جای این که آن را در دایرکتوری فعلی شما قرار دهد. زیرا ساخت پیش‌فرض یک ساخت دیباگ است، Cargo فایل باینری را در دایرکتوری به نام _debug_ قرار می‌دهد. شما می‌توانید فایل اجرایی را با این دستور اجرا کنید: ```console -$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows +$ ./target/debug/hello_cargo # یا .\target\debug\hello_cargo.exe در ویندوز Hello, world! ``` -If all goes well, `Hello, world!` should print to the terminal. Running `cargo -build` for the first time also causes Cargo to create a new file at the top -level: _Cargo.lock_. This file keeps track of the exact versions of -dependencies in your project. This project doesn’t have dependencies, so the -file is a bit sparse. You won’t ever need to change this file manually; Cargo -manages its contents for you. +اگر همه چیز درست پیش رفته باشد، `Hello, world!` باید در ترمینال چاپ شود. اجرای `cargo build` برای اولین بار همچنین باعث می‌شود که Cargo یک فایل جدید در بالای دایرکتوری ایجاد کند: _Cargo.lock_. این فایل نسخه‌های دقیق وابستگی‌های پروژه شما را پیگیری می‌کند. چون این پروژه وابستگی ندارد، این فایل کمی خالی است. شما هیچ‌گاه نیازی به تغییر دستی این فایل نخواهید داشت؛ Cargo محتویات آن را برای شما مدیریت می‌کند. -We just built a project with `cargo build` and ran it with -`./target/debug/hello_cargo`, but we can also use `cargo run` to compile the -code and then run the resultant executable all in one command: +ما همین حالا پروژه را با دستور `cargo build` ساختیم و با `./target/debug/hello_cargo` اجرا کردیم، اما همچنین می‌توانیم از `cargo run` برای کامپایل کردن کد و سپس اجرای باینری حاصل در یک دستور استفاده کنیم: ```console $ cargo run @@ -154,15 +97,9 @@ $ cargo run Hello, world! ``` -Using `cargo run` is more convenient than having to remember to run `cargo -build` and then use the whole path to the binary, so most developers use `cargo -run`. +استفاده از `cargo run` راحت‌تر از این است که بخواهید دستور `cargo build` را اجرا کرده و سپس مسیر کامل به باینری را استفاده کنید، بنابراین بیشتر توسعه‌دهندگان از `cargo run` استفاده می‌کنند. -Notice that this time we didn’t see output indicating that Cargo was compiling -`hello_cargo`. Cargo figured out that the files hadn’t changed, so it didn’t -rebuild but just ran the binary. If you had modified your source code, Cargo -would have rebuilt the project before running it, and you would have seen this -output: +توجه کنید که این بار، خروجی‌ای که نشان دهد Cargo در حال کامپایل کردن `hello_cargo` است، مشاهده نکردیم. Cargo متوجه شد که فایل‌ها تغییر نکرده‌اند، بنابراین بازسازی نکرد و فقط باینری را اجرا کرد. اگر کد منبع خود را تغییر داده بودید، Cargo ابتدا پروژه را بازسازی می‌کرد و سپس آن را اجرا می‌کرد، و شما این خروجی را می‌دیدید: ```console $ cargo run @@ -172,8 +109,7 @@ $ cargo run Hello, world! ``` -Cargo also provides a command called `cargo check`. This command quickly checks -your code to make sure it compiles but doesn’t produce an executable: +Cargo همچنین یک دستور به نام `cargo check` را فراهم می‌کند. این دستور کد شما را به سرعت بررسی می‌کند تا مطمئن شود که کامپایل می‌شود اما هیچ اجرایی تولید نمی‌کند: ```console $ cargo check @@ -181,52 +117,27 @@ $ cargo check Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs ``` -Why would you not want an executable? Often, `cargo check` is much faster than -`cargo build` because it skips the step of producing an executable. If you’re -continually checking your work while writing the code, using `cargo check` will -speed up the process of letting you know if your project is still compiling! As -such, many Rustaceans run `cargo check` periodically as they write their -program to make sure it compiles. Then they run `cargo build` when they’re -ready to use the executable. - -Let’s recap what we’ve learned so far about Cargo: - -- We can create a project using `cargo new`. -- We can build a project using `cargo build`. -- We can build and run a project in one step using `cargo run`. -- We can build a project without producing a binary to check for errors using - `cargo check`. -- Instead of saving the result of the build in the same directory as our code, - Cargo stores it in the _target/debug_ directory. - -An additional advantage of using Cargo is that the commands are the same no -matter which operating system you’re working on. So, at this point, we’ll no -longer provide specific instructions for Linux and macOS versus Windows. - -### Building for Release - -When your project is finally ready for release, you can use `cargo build ---release` to compile it with optimizations. This command will create an -executable in _target/release_ instead of _target/debug_. The optimizations -make your Rust code run faster, but turning them on lengthens the time it takes -for your program to compile. This is why there are two different profiles: one -for development, when you want to rebuild quickly and often, and another for -building the final program you’ll give to a user that won’t be rebuilt -repeatedly and that will run as fast as possible. If you’re benchmarking your -code’s running time, be sure to run `cargo build --release` and benchmark with -the executable in _target/release_. - -### Cargo as Convention - -With simple projects, Cargo doesn’t provide a lot of value over just using -`rustc`, but it will prove its worth as your programs become more intricate. -Once programs grow to multiple files or need a dependency, it’s much easier to -let Cargo coordinate the build. - -Even though the `hello_cargo` project is simple, it now uses much of the real -tooling you’ll use in the rest of your Rust career. In fact, to work on any -existing projects, you can use the following commands to check out the code -using Git, change to that project’s directory, and build: +چرا شما به یک فایل اجرایی نیاز ندارید؟ اغلب، `cargo check` بسیار سریع‌تر از `cargo build` است زیرا مرحله تولید یک فایل اجرایی را رد می‌کند. اگر شما به طور مداوم در حال بررسی کد خود هستید، استفاده از `cargo check` سرعت فرایند اطلاع دادن به شما از این که پروژه هنوز کامپایل می‌شود را افزایش می‌دهد! به همین دلیل، بسیاری از Rustaceans به طور دوره‌ای `cargo check` را در حین نوشتن کد خود اجرا می‌کنند تا مطمئن شوند که پروژه‌شان کامپایل می‌شود. سپس زمانی که آماده استفاده از باینری شدند، از دستور `cargo build` استفاده می‌کنند. + +بیایید خلاصه‌ای از آنچه که تا به حال در مورد Cargo آموخته‌ایم مرور کنیم: + +- ما می‌توانیم یک پروژه با استفاده از `cargo new` بسازیم. +- ما می‌توانیم یک پروژه را با استفاده از `cargo build` بسازیم. +- ما می‌توانیم یک پروژه را با یک مرحله از ساخت و اجرا با استفاده از `cargo run` بسازیم و اجرا کنیم. +- ما می‌توانیم یک پروژه را بدون تولید باینری برای بررسی خطاها با استفاده از `cargo check` بسازیم. +- به جای ذخیره نتیجه ساخت در همان دایرکتوری که کد ما قرار دارد، Cargo آن را در دایرکتوری _target/debug_ ذخیره می‌کند. + +یک مزیت اضافی استفاده از Cargo این است که دستورات آن در همه سیستم‌عامل‌ها یکسان است. بنابراین، از این پس، دیگر دستورالعمل‌های خاصی برای لینوکس و macOS در مقابل ویندوز ارائه نخواهیم کرد. + +### ساخت برای انتشار + +وقتی پروژه شما آماده انتشار است، می‌توانید از دستور `cargo build --release` برای کامپایل کردن آن با بهینه‌سازی‌ها استفاده کنید. این دستور یک فایل اجرایی در دایرکتوری _target/release_ به جای _target/debug_ ایجاد می‌کند. بهینه‌سازی‌ها باعث می‌شوند که کد Rust شما سریع‌تر اجرا شود، اما فعال کردن آن‌ها زمان کامپایل برنامه را طولانی‌تر می‌کند. به همین دلیل، دو پروفایل مختلف وجود دارد: یکی برای توسعه که شما می‌خواهید سریعاً و به دفعات پروژه را بازسازی کنید، و دیگری برای ساختن برنامه نهایی که به کاربر تحویل خواهید داد، که به دفعات بازسازی نمی‌شود و باید سریع‌ترین اجرا را داشته باشد. اگر در حال اندازه‌گیری زمان اجرای کد خود هستید، حتماً از دستور `cargo build --release` استفاده کنید و با فایل اجرایی در _target/release_ اندازه‌گیری کنید. + +### Cargo به عنوان یک کنوانسیون + +در پروژه‌های ساده، Cargo نسبت به استفاده از `rustc` مزیت زیادی ندارد، اما با پیچیده‌تر شدن برنامه‌ها، ارزش خود را نشان می‌دهد. زمانی که برنامه‌ها به چندین فایل نیاز پیدا می‌کنند یا وابستگی دارند، استفاده از Cargo برای هماهنگ کردن فرایند ساخت بسیار راحت‌تر می‌شود. + +حتی اگر پروژه `hello_cargo` ساده باشد، اکنون از بسیاری از ابزارهای واقعی استفاده می‌کند که در طول مسیر Rust خود به آن‌ها نیاز خواهید داشت. در واقع، برای کار بر روی هر پروژه موجود، می‌توانید از دستورات زیر برای بررسی کد با استفاده از Git، تغییر به دایرکتوری آن پروژه و ساخت آن استفاده کنید: ```console $ git clone example.org/someproject @@ -234,25 +145,16 @@ $ cd someproject $ cargo build ``` -For more information about Cargo, check out [its documentation][cargo]. - -## Summary +برای اطلاعات بیشتر در مورد Cargo، می‌توانید به [مستندات آن](https://doc.rust-lang.org/cargo/) مراجعه کنید. -You’re already off to a great start on your Rust journey! In this chapter, -you’ve learned how to: +## خلاصه -- Install the latest stable version of Rust using `rustup` -- Update to a newer Rust version -- Open locally installed documentation -- Write and run a “Hello, world!” program using `rustc` directly -- Create and run a new project using the conventions of Cargo +شما در حال حاضر شروع بسیار خوبی برای سفر خود در Rust دارید! در این فصل، شما یاد گرفته‌اید که چگونه: -This is a great time to build a more substantial program to get used to reading -and writing Rust code. So, in Chapter 2, we’ll build a guessing game program. -If you would rather start by learning how common programming concepts work in -Rust, see Chapter 3 and then return to Chapter 2. +- آخرین نسخه پایدار Rust را با استفاده از `rustup` نصب کنید. +- به نسخه جدیدتر Rust بروزرسانی کنید. +- مستندات محلی نصب‌شده را باز کنید. +- یک برنامه “Hello, world!” را با استفاده از `rustc` مستقیماً بنویسید و اجرا کنید. +- یک پروژه جدید را با استفاده از کنوانسیون‌های Cargo بسازید و اجرا کنید. -[installation]: ch01-01-installation.html#installation -[toml]: https://toml.io -[appendix-e]: appendix-05-editions.html -[cargo]: https://doc.rust-lang.org/cargo/ +این زمان بسیار خوبی است که برنامه‌ای بزرگتر بسازید تا با خواندن و نوشتن کد Rust بیشتر آشنا شوید. بنابراین، در فصل 2، یک برنامه بازی حدس زدن خواهیم ساخت. اگر ترجیح می‌دهید ابتدا یاد بگیرید که مفاهیم برنامه‌نویسی رایج در Rust چگونه کار می‌کنند، فصل 3 را مطالعه کنید و سپس به فصل 2 بازگردید. diff --git a/src/ch02-00-guessing-game-tutorial.md b/src/ch02-00-guessing-game-tutorial.md index 015a42a5f6..0a8ffc4901 100644 --- a/src/ch02-00-guessing-game-tutorial.md +++ b/src/ch02-00-guessing-game-tutorial.md @@ -1,42 +1,21 @@ -# Programming a Guessing Game +# برنامه‌نویسی یک بازی حدس زدن -Let’s jump into Rust by working through a hands-on project together! This -chapter introduces you to a few common Rust concepts by showing you how to use -them in a real program. You’ll learn about `let`, `match`, methods, associated -functions, external crates, and more! In the following chapters, we’ll explore -these ideas in more detail. In this chapter, you’ll just practice the -fundamentals. +بیایید با کار روی یک پروژه عملی با هم به دنیای Rust وارد شویم! این فصل با نشان دادن نحوه استفاده از مفاهیم رایج Rust در یک برنامه واقعی، شما را با آن‌ها آشنا می‌کند. درباره `let`، `match`، متدها، توابع مرتبط (associated functions)، جعبه‌ها (crates)ی خارجی و موارد دیگر خواهید آموخت! در فصل‌های بعدی، این ایده‌ها را به طور مفصل بررسی خواهیم کرد. در این فصل، فقط اصول اولیه را تمرین می‌کنید. -We’ll implement a classic beginner programming problem: a guessing game. Here’s -how it works: the program will generate a random integer between 1 and 100. It -will then prompt the player to enter a guess. After a guess is entered, the -program will indicate whether the guess is too low or too high. If the guess is -correct, the game will print a congratulatory message and exit. +ما یک مسئله کلاسیک برنامه‌نویسی برای مبتدیان را پیاده‌سازی خواهیم کرد: یک بازی حدس زدن. این بازی به این صورت عمل می‌کند: برنامه یک عدد صحیح تصادفی بین 1 تا 100 تولید می‌کند. سپس از بازیکن می‌خواهد که یک حدس وارد کند. پس از وارد کردن حدس، برنامه مشخص می‌کند که آیا حدس خیلی پایین است یا خیلی بالا. اگر حدس درست باشد، برنامه یک پیام تبریک چاپ می‌کند و از بازی خارج می‌شود. -## Setting Up a New Project +## راه‌اندازی یک پروژه جدید -To set up a new project, go to the _projects_ directory that you created in -Chapter 1 and make a new project using Cargo, like so: +برای راه‌اندازی یک پروژه جدید، به دایرکتوری _projects_ که در فصل 1 ایجاد کردید بروید و یک پروژه جدید با استفاده از Cargo ایجاد کنید، به این صورت: ```console $ cargo new guessing_game $ cd guessing_game ``` -The first command, `cargo new`, takes the name of the project (`guessing_game`) -as the first argument. The second command changes to the new project’s -directory. +دستور اول، `cargo new`، نام پروژه (`guessing_game`) را به عنوان آرگومان اول می‌گیرد. دستور دوم به دایرکتوری پروژه جدید منتقل می‌شود. -Look at the generated _Cargo.toml_ file: - - +فایل _Cargo.toml_ تولیدشده را مشاهده کنید: Filename: Cargo.toml @@ -44,8 +23,7 @@ cd ../../.. {{#include ../listings/ch02-guessing-game-tutorial/no-listing-01-cargo-new/Cargo.toml}} ``` -As you saw in Chapter 1, `cargo new` generates a “Hello, world!” program for -you. Check out the _src/main.rs_ file: +همان‌طور که در فصل 1 دیدید، `cargo new` یک برنامه "Hello, world!" برای شما تولید می‌کند. فایل _src/main.rs_ را بررسی کنید: Filename: src/main.rs @@ -53,27 +31,21 @@ you. Check out the _src/main.rs_ file: {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/no-listing-01-cargo-new/src/main.rs}} ``` -Now let’s compile this “Hello, world!” program and run it in the same step -using the `cargo run` command: +حالا این برنامه "Hello, world!" را کامپایل کرده و در همان مرحله با استفاده از دستور `cargo run` اجرا کنید: ```console {{#include ../listings/ch02-guessing-game-tutorial/no-listing-01-cargo-new/output.txt}} ``` -The `run` command comes in handy when you need to rapidly iterate on a project, -as we’ll do in this game, quickly testing each iteration before moving on to -the next one. +دستور `run` زمانی که نیاز دارید به سرعت روی یک پروژه تکرار کنید مفید است، همان‌طور که در این بازی انجام خواهیم داد، و به سرعت هر مرحله را قبل از ادامه به مرحله بعدی آزمایش می‌کنیم. -Reopen the _src/main.rs_ file. You’ll be writing all the code in this file. +فایل _src/main.rs_ را دوباره باز کنید. شما تمام کد را در این فایل خواهید نوشت. -## Processing a Guess +## پردازش یک حدس -The first part of the guessing game program will ask for user input, process -that input, and check that the input is in the expected form. To start, we’ll -allow the player to input a guess. Enter the code in Listing 2-1 into -_src/main.rs_. +اولین بخش از برنامه بازی حدس زدن از کاربر درخواست ورودی می‌کند، آن ورودی را پردازش می‌کند و بررسی می‌کند که ورودی در قالب مورد انتظار باشد. برای شروع، به بازیکن اجازه می‌دهیم یک حدس وارد کند. کد موجود در لیستینگ 2-1 را در فایل _src/main.rs_ وارد کنید. -+ ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:all}} @@ -81,208 +53,124 @@ _src/main.rs_. -This code contains a lot of information, so let’s go over it line by line. To -obtain user input and then print the result as output, we need to bring the -`io` input/output library into scope. The `io` library comes from the standard -library, known as `std`: +این کد اطلاعات زیادی دارد، پس بیایید خط به خط آن را بررسی کنیم. برای گرفتن ورودی کاربر و سپس چاپ نتیجه به‌عنوان خروجی، نیاز داریم که کتابخانه ورودی/خروجی `io` را به دامنه بیاوریم. کتابخانه `io` از کتابخانه استاندارد که با نام `std` شناخته می‌شود، می‌آید: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:io}} ``` -By default, Rust has a set of items defined in the standard library that it -brings into the scope of every program. This set is called the _prelude_, and -you can see everything in it [in the standard library documentation][prelude]. +به‌طور پیش‌فرض، Rust مجموعه‌ای از آیتم‌ها را که در کتابخانه استاندارد تعریف شده‌اند به دامنه هر برنامه وارد می‌کند. این مجموعه _prelude_ نامیده می‌شود و می‌توانید همه چیز در آن را [در مستندات کتابخانه استاندارد][prelude] ببینید. -If a type you want to use isn’t in the prelude, you have to bring that type -into scope explicitly with a `use` statement. Using the `std::io` library -provides you with a number of useful features, including the ability to accept -user input. +اگر نوعی که می‌خواهید استفاده کنید در prelude نباشد، باید آن نوع را به‌طور صریح با یک دستور `use` به دامنه بیاورید. استفاده از کتابخانه `std::io` به شما ویژگی‌های مفیدی مانند امکان پذیرش ورودی کاربر می‌دهد. -As you saw in Chapter 1, the `main` function is the entry point into the -program: +همان‌طور که در فصل 1 دیدید، تابع `main` نقطه ورود به برنامه است: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:main}} ``` -The `fn` syntax declares a new function; the parentheses, `()`, indicate there -are no parameters; and the curly bracket, `{`, starts the body of the function. +نحو `fn` یک تابع جدید را اعلام می‌کند؛ پرانتزها `()` نشان می‌دهند که هیچ پارامتری وجود ندارد و کروشه باز `{` بدنه تابع را شروع می‌کند. -As you also learned in Chapter 1, `println!` is a macro that prints a string to -the screen: +همچنین در فصل 1 آموختید که `println!` یک ماکرو است که یک رشته را به صفحه چاپ می‌کند: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:print}} ``` -This code is printing a prompt stating what the game is and requesting input -from the user. +این کد یک پیغام اعلام می‌کند که بازی چیست و از کاربر درخواست ورودی می‌کند. -### Storing Values with Variables +### ذخیره مقادیر با متغیرها -Next, we’ll create a _variable_ to store the user input, like this: +سپس، یک _متغیر_ ایجاد می‌کنیم تا ورودی کاربر را ذخیره کند، مانند این: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:string}} ``` -Now the program is getting interesting! There’s a lot going on in this little -line. We use the `let` statement to create the variable. Here’s another example: +حالا برنامه جالب‌تر می‌شود! در این خط کوچک چیزهای زیادی در حال اتفاق است. ما از دستور `let` برای ایجاد متغیر استفاده می‌کنیم. در اینجا یک مثال دیگر آورده شده است: ```rust,ignore let apples = 5; ``` -This line creates a new variable named `apples` and binds it to the value 5. In -Rust, variables are immutable by default, meaning once we give the variable a -value, the value won’t change. We’ll be discussing this concept in detail in -the [“Variables and Mutability”][variables-and-mutability] -section in Chapter 3. To make a variable mutable, we add `mut` before the -variable name: +این خط یک متغیر جدید به نام `apples` ایجاد می‌کند و آن را به مقدار 5 متصل می‌کند. در Rust، متغیرها به‌طور پیش‌فرض غیرقابل‌تغییر هستند، به این معنا که پس از اختصاص مقدار به متغیر، مقدار تغییر نخواهد کرد. این مفهوم را به‌طور مفصل در بخش [“متغیرها و تغییرپذیری”][variables-and-mutability] در فصل 3 بررسی خواهیم کرد. برای متغیری که تغییرپذیر باشد، `mut` را قبل از نام متغیر اضافه می‌کنیم: ```rust,ignore let apples = 5; // immutable let mut bananas = 5; // mutable ``` -> Note: The `//` syntax starts a comment that continues until the end of the -> line. Rust ignores everything in comments. We’ll discuss comments in more -> detail in [Chapter 3][comments]. +> نکته: نحو `//` یک نظر (comment) را آغاز می‌کند که تا انتهای خط ادامه دارد. Rust همه چیز در نظرات را نادیده می‌گیرد. نظرات را در [فصل 3][comments] با جزئیات بیشتری بررسی خواهیم کرد. -Returning to the guessing game program, you now know that `let mut guess` will -introduce a mutable variable named `guess`. The equal sign (`=`) tells Rust we -want to bind something to the variable now. On the right of the equal sign is -the value that `guess` is bound to, which is the result of calling -`String::new`, a function that returns a new instance of a `String`. -[`String`][string] is a string type provided by the standard -library that is a growable, UTF-8 encoded bit of text. +بازگشت به برنامه بازی حدس زدن: اکنون می‌دانید که `let mut guess` یک متغیر تغییرپذیر به نام `guess` معرفی می‌کند. علامت مساوی (`=`) به Rust می‌گوید که می‌خواهیم چیزی را به این متغیر متصل کنیم. در سمت راست علامت مساوی، مقداری قرار دارد که `guess` به آن متصل می‌شود، که نتیجه فراخوانی `String::new` است، یک تابع که یک نمونه جدید از نوع `String` بازمی‌گرداند. [`String`][string] یک نوع رشته‌ای ارائه‌شده توسط کتابخانه استاندارد است که بخشی از متن قابل رشد و با رمزگذاری UTF-8 است. -The `::` syntax in the `::new` line indicates that `new` is an associated -function of the `String` type. An _associated function_ is a function that’s -implemented on a type, in this case `String`. This `new` function creates a -new, empty string. You’ll find a `new` function on many types because it’s a -common name for a function that makes a new value of some kind. +نحو `::` در خط `::new` نشان می‌دهد که `new` یک تابع مرتبط با نوع `String` است. یک _تابع مرتبط_ تابعی است که روی یک نوع پیاده‌سازی شده است، در اینجا `String`. این تابع `new` یک رشته جدید و خالی ایجاد می‌کند. شما در بسیاری از انواع یک تابع `new` پیدا خواهید کرد، زیرا این نام معمولاً برای تابعی که یک مقدار جدید از یک نوع خاص ایجاد می‌کند استفاده می‌شود. -In full, the `let mut guess = String::new();` line has created a mutable -variable that is currently bound to a new, empty instance of a `String`. Whew! +در مجموع، خط `let mut guess = String::new();` یک متغیر تغییرپذیر ایجاد کرده است که در حال حاضر به یک نمونه جدید و خالی از `String` متصل شده است. خوب! -### Receiving User Input +### دریافت ورودی کاربر -Recall that we included the input/output functionality from the standard -library with `use std::io;` on the first line of the program. Now we’ll call -the `stdin` function from the `io` module, which will allow us to handle user -input: +به یاد آورید که با `use std::io;` در اولین خط برنامه، قابلیت ورودی/خروجی را از کتابخانه استاندارد اضافه کردیم. اکنون تابع `stdin` را از ماژول `io` فراخوانی می‌کنیم که به ما امکان مدیریت ورودی کاربر را می‌دهد: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:read}} ``` -If we hadn’t imported the `io` module with `use std::io;` at the beginning of -the program, we could still use the function by writing this function call as -`std::io::stdin`. The `stdin` function returns an instance of -[`std::io::Stdin`][iostdin], which is a type that represents a -handle to the standard input for your terminal. +اگر ماژول `io` را با دستور `use std::io;` +در ابتدای برنامه وارد نکرده بودیم، همچنان +می‌توانستیم از این تابع استفاده کنیم، به این شکل +که آن را به‌صورت `std::io::stdin` فراخوانی کنیم. +تابع `stdin` نمونه‌ای از [`std::io::Stdin`][iostdin] +بازمی‌گرداند، که نوعی است برای نمایش یک +دسته (handle) به ورودی استاندارد ترمینال شما. -Next, the line `.read_line(&mut guess)` calls the [`read_line`][read_line] method on the standard input handle to get input from the user. -We’re also passing `&mut guess` as the argument to `read_line` to tell it what -string to store the user input in. The full job of `read_line` is to take -whatever the user types into standard input and append that into a string -(without overwriting its contents), so we therefore pass that string as an -argument. The string argument needs to be mutable so the method can change the -string’s content. +در خط بعدی، متد `.read_line(&mut guess)` را روی handle ورودی استاندارد فراخوانی می‌کنیم تا ورودی کاربر را دریافت کنیم. همچنین `&mut guess` را به‌عنوان آرگومان به `read_line` ارسال می‌کنیم تا به آن بگوییم ورودی کاربر را در چه رشته‌ای ذخیره کند. وظیفه کامل `read_line` این است که هر چیزی را که کاربر در ورودی استاندارد تایپ می‌کند به رشته‌ای اضافه کند (بدون بازنویسی محتوای آن)، بنابراین این رشته را به‌عنوان آرگومان ارسال می‌کنیم. آرگومان رشته باید تغییرپذیر باشد تا متد بتواند محتوای رشته را تغییر دهد. -The `&` indicates that this argument is a _reference_, which gives you a way to -let multiple parts of your code access one piece of data without needing to -copy that data into memory multiple times. References are a complex feature, -and one of Rust’s major advantages is how safe and easy it is to use -references. You don’t need to know a lot of those details to finish this -program. For now, all you need to know is that, like variables, references are -immutable by default. Hence, you need to write `&mut guess` rather than -`&guess` to make it mutable. (Chapter 4 will explain references more -thoroughly.) +علامت `&` نشان می‌دهد که این آرگومان یک _ارجاع_ است، که به شما راهی می‌دهد تا به چندین بخش از کد اجازه دهید به یک قطعه داده دسترسی داشته باشند بدون اینکه نیاز به کپی کردن آن داده در حافظه چندین بار داشته باشید. ارجاعات یک ویژگی پیچیده هستند و یکی از مزایای اصلی Rust این است که استفاده از ارجاعات ایمن و آسان است. نیازی نیست جزئیات زیادی درباره آن بدانید تا این برنامه را کامل کنید. فعلاً، تنها چیزی که باید بدانید این است که، مانند متغیرها، ارجاعات به‌طور پیش‌فرض غیرقابل تغییر هستند. بنابراین، باید `&mut guess` بنویسید به‌جای `&guess` تا آن را تغییرپذیر کنید. (فصل 4 ارجاعات را به‌طور کامل توضیح خواهد داد.) - +### مدیریت خطای احتمالی با `Result` - - -### Handling Potential Failure with `Result` - -We’re still working on this line of code. We’re now discussing a third line of -text, but note that it’s still part of a single logical line of code. The next -part is this method: +ما همچنان روی همین خط کد کار می‌کنیم. اکنون در حال بحث درباره خط سوم هستیم، اما توجه داشته باشید که این هنوز بخشی از یک خط منطقی از کد است. قسمت بعدی این متد است: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:expect}} ``` -We could have written this code as: +ما می‌توانستیم این کد را به این صورت بنویسیم: ```rust,ignore io::stdin().read_line(&mut guess).expect("Failed to read line"); ``` -However, one long line is difficult to read, so it’s best to divide it. It’s -often wise to introduce a newline and other whitespace to help break up long -lines when you call a method with the `.method_name()` syntax. Now let’s -discuss what this line does. +با این حال، یک خط طولانی خواندن آن را دشوار می‌کند، بنابراین بهتر است آن را تقسیم کنیم. اغلب توصیه می‌شود یک خط جدید و فضای سفید معرفی کنید تا خطوط طولانی را هنگام فراخوانی متدی با نحو `.method_name()` تقسیم کنید. حالا بیایید ببینیم این خط چه می‌کند. -As mentioned earlier, `read_line` puts whatever the user enters into the string -we pass to it, but it also returns a `Result` value. [`Result`][result] is an [_enumeration_][enums], often called an _enum_, -which is a type that can be in one of multiple possible states. We call each -possible state a _variant_. +همان‌طور که قبلاً ذکر شد، `read_line` هر چیزی که کاربر وارد می‌کند را در رشته‌ای که به آن ارسال می‌کنیم قرار می‌دهد، اما همچنین یک مقدار `Result` بازمی‌گرداند. [`Result`][result] یک [_enumeration_][enums] است که اغلب به عنوان _enum_ نامیده می‌شود و نوعی است که می‌تواند در یکی از چندین حالت ممکن باشد. ما هر حالت ممکن را یک _متغیر_ (variant) می‌نامیم. -[Chapter 6][enums] will cover enums in more detail. The purpose -of these `Result` types is to encode error-handling information. +[فصل 6][enums] به جزئیات بیشتری در مورد enumها خواهد پرداخت. هدف از انواع `Result` رمزگذاری اطلاعات مدیریت خطا است. -`Result`’s variants are `Ok` and `Err`. The `Ok` variant indicates the -operation was successful, and it contains the successfully generated value. -The `Err` variant means the operation failed, and it contains information -about how or why the operation failed. +متغیرهای `Result` شامل `Ok` و `Err` هستند. متغیر `Ok` نشان می‌دهد که عملیات موفقیت‌آمیز بوده و مقداری که با موفقیت تولید شده است را در خود دارد. متغیر `Err` به معنای این است که عملیات شکست خورده و اطلاعاتی درباره چگونگی یا دلیل شکست عملیات در خود دارد. -Values of the `Result` type, like values of any type, have methods defined on -them. An instance of `Result` has an [`expect` method][expect] -that you can call. If this instance of `Result` is an `Err` value, `expect` -will cause the program to crash and display the message that you passed as an -argument to `expect`. If the `read_line` method returns an `Err`, it would -likely be the result of an error coming from the underlying operating system. -If this instance of `Result` is an `Ok` value, `expect` will take the return -value that `Ok` is holding and return just that value to you so you can use it. -In this case, that value is the number of bytes in the user’s input. +مقادیر نوع `Result`، مانند مقادیر هر نوع دیگری، متدهایی تعریف‌شده بر روی خود دارند. یک نمونه از `Result` یک [متد `expect`][expect] دارد که می‌توانید آن را فراخوانی کنید. اگر این نمونه از `Result` یک مقدار `Err` باشد، `expect` باعث می‌شود برنامه متوقف شده و پیغام خطایی که به‌عنوان آرگومان به `expect` پاس داده‌اید را نمایش دهد. اگر متد `read_line` یک `Err` بازگرداند، احتمالاً به دلیل خطایی از سیستم‌عامل زیربنایی است. اگر این نمونه از `Result` یک مقدار `Ok` باشد، `expect` مقدار بازگشتی که `Ok` در خود دارد را می‌گیرد و فقط آن مقدار را بازمی‌گرداند تا بتوانید از آن استفاده کنید. در این مورد، آن مقدار تعداد بایت‌های ورودی کاربر است. -If you don’t call `expect`, the program will compile, but you’ll get a warning: +اگر `expect` را فراخوانی نکنید، برنامه کامپایل می‌شود، اما هشداری دریافت خواهید کرد: ```console {{#include ../listings/ch02-guessing-game-tutorial/no-listing-02-without-expect/output.txt}} ``` -Rust warns that you haven’t used the `Result` value returned from `read_line`, -indicating that the program hasn’t handled a possible error. +Rust هشدار می‌دهد که از مقدار `Result` بازگشتی از `read_line` استفاده نکرده‌اید، که نشان می‌دهد برنامه یک خطای ممکن را مدیریت نکرده است. -The right way to suppress the warning is to actually write error-handling code, -but in our case we just want to crash this program when a problem occurs, so we -can use `expect`. You’ll learn about recovering from errors in [Chapter -9][recover]. +روش درست برای جلوگیری از هشدار این است که واقعاً کد مدیریت خطا بنویسید، اما در مورد ما فقط می‌خواهیم وقتی مشکلی پیش آمد این برنامه متوقف شود، بنابراین می‌توانیم از `expect` استفاده کنیم. درباره بازیابی از خطاها در [فصل 9][recover] خواهید آموخت. -### Printing Values with `println!` Placeholders +### چاپ مقادیر با جای‌نگهدارهای `println!` -Aside from the closing curly bracket, there’s only one more line to discuss in -the code so far: +علاوه بر کروشه بسته، فقط یک خط دیگر برای بحث در کدی که تاکنون نوشته‌ایم باقی مانده است: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-01/src/main.rs:print_guess}} ``` -This line prints the string that now contains the user’s input. The `{}` set of -curly brackets is a placeholder: think of `{}` as little crab pincers that hold -a value in place. When printing the value of a variable, the variable name can -go inside the curly brackets. When printing the result of evaluating an -expression, place empty curly brackets in the format string, then follow the -format string with a comma-separated list of expressions to print in each empty -curly bracket placeholder in the same order. Printing a variable and the result -of an expression in one call to `println!` would look like this: +این خط رشته‌ای را که اکنون ورودی کاربر را در خود دارد چاپ می‌کند. مجموعه `{}` از کروشه‌های باز و بسته یک جای‌نگهدار است: به `{}` به‌عنوان پنجه‌های کوچک خرچنگی فکر کنید که یک مقدار را در جای خود نگه می‌دارند. هنگام چاپ مقدار یک متغیر، نام متغیر می‌تواند داخل کروشه‌ها قرار گیرد. هنگام چاپ نتیجه ارزیابی یک عبارت، کروشه‌های باز و بسته خالی را در رشته فرمت قرار دهید، سپس رشته فرمت را با لیستی از عبارات جداشده با کاما دنبال کنید تا در هر جای‌نگهدار خالی به همان ترتیب چاپ شوند. چاپ یک متغیر و نتیجه یک عبارت در یک فراخوانی `println!` به این صورت خواهد بود: ```rust let x = 5; @@ -291,17 +179,11 @@ let y = 10; println!("x = {x} and y + 2 = {}", y + 2); ``` -This code would print `x = 5 and y + 2 = 12`. +این کد `x = 5 and y + 2 = 12` را چاپ می‌کند. -### Testing the First Part +### آزمایش بخش اول -Let’s test the first part of the guessing game. Run it using `cargo run`: - - +بیایید بخش اول بازی حدس زدن را آزمایش کنیم. با استفاده از دستور `cargo run` آن را اجرا کنید: ```console $ cargo run @@ -314,37 +196,17 @@ Please input your guess. You guessed: 6 ``` -At this point, the first part of the game is done: we’re getting input from the -keyboard and then printing it. +در این مرحله، بخش اول بازی تمام شده است: ما ورودی را از صفحه‌کلید می‌گیریم و سپس آن را چاپ می‌کنیم. -## Generating a Secret Number +## تولید یک عدد مخفی -Next, we need to generate a secret number that the user will try to guess. The -secret number should be different every time so the game is fun to play more -than once. We’ll use a random number between 1 and 100 so the game isn’t too -difficult. Rust doesn’t yet include random number functionality in its standard -library. However, the Rust team does provide a [`rand` crate][randcrate] with -said functionality. +در مرحله بعد، باید یک عدد مخفی تولید کنیم که کاربر سعی خواهد کرد آن را حدس بزند. عدد مخفی باید هر بار متفاوت باشد تا بازی بارها قابل بازی و لذت‌بخش باشد. از یک عدد تصادفی بین 1 تا 100 استفاده می‌کنیم تا بازی خیلی سخت نباشد. Rust هنوز قابلیت تولید اعداد تصادفی را در کتابخانه استاندارد خود ندارد. با این حال، تیم Rust یک [crate `rand`][randcrate] با این قابلیت ارائه می‌دهد. -### Using a Crate to Get More Functionality +### استفاده از یک crate برای دسترسی به قابلیت‌های بیشتر -Remember that a crate is a collection of Rust source code files. The project -we’ve been building is a _binary crate_, which is an executable. The `rand` -crate is a _library crate_, which contains code that is intended to be used in -other programs and can’t be executed on its own. +به یاد داشته باشید که یک crate مجموعه‌ای از فایل‌های کد منبع Rust است. پروژه‌ای که ما در حال ساخت آن هستیم یک _crate دودویی_ است که یک فایل اجرایی است. crate `rand` یک _crate کتابخانه‌ای_ است که حاوی کدی است که قرار است در برنامه‌های دیگر استفاده شود و به تنهایی قابل اجرا نیست. -Cargo’s coordination of external crates is where Cargo really shines. Before we -can write code that uses `rand`, we need to modify the _Cargo.toml_ file to -include the `rand` crate as a dependency. Open that file now and add the -following line to the bottom, beneath the `[dependencies]` section header that -Cargo created for you. Be sure to specify `rand` exactly as we have here, with -this version number, or the code examples in this tutorial may not work: - - +هماهنگی Cargo با جعبه‌ها (crates)ی خارجی یکی از نقاط قوت آن است. قبل از اینکه بتوانیم کدی بنویسیم که از `rand` استفاده کند، باید فایل _Cargo.toml_ را تغییر دهیم تا crate `rand` را به عنوان وابستگی اضافه کنیم. اکنون آن فایل را باز کنید و خط زیر را به انتهای آن، زیر بخش `[dependencies]` که Cargo برای شما ایجاد کرده است، اضافه کنید. مطمئن شوید که `rand` را دقیقاً همان‌طور که در اینجا آمده است با این شماره نسخه مشخص کنید، وگرنه مثال‌های کد در این آموزش ممکن است کار نکنند: Filename: Cargo.toml @@ -352,23 +214,11 @@ this version number, or the code examples in this tutorial may not work: {{#include ../listings/ch02-guessing-game-tutorial/listing-02-02/Cargo.toml:8:}} ``` -In the _Cargo.toml_ file, everything that follows a header is part of that -section that continues until another section starts. In `[dependencies]` you -tell Cargo which external crates your project depends on and which versions of -those crates you require. In this case, we specify the `rand` crate with the -semantic version specifier `0.8.5`. Cargo understands [Semantic -Versioning][semver] (sometimes called _SemVer_), which is a -standard for writing version numbers. The specifier `0.8.5` is actually -shorthand for `^0.8.5`, which means any version that is at least 0.8.5 but -below 0.9.0. +در فایل _Cargo.toml_، هر چیزی که بعد از یک سرآیند بیاید بخشی از آن بخش است و تا زمانی که بخش دیگری شروع نشود ادامه می‌یابد. در `[dependencies]` به Cargo می‌گویید پروژه شما به کدام جعبه‌ها (crates)ی خارجی وابسته است و کدام نسخه از آن جعبه‌ها (crates) را نیاز دارید. در این مورد، ما crate `rand` را با مشخص‌کننده نسخه `0.8.5` مشخص می‌کنیم. Cargo [نسخه‌بندی معنایی][semver] (گاهی اوقات _SemVer_ نامیده می‌شود) را درک می‌کند، که یک استاندارد برای نوشتن شماره نسخه‌ها است. مشخص‌کننده `0.8.5` در واقع مخفف `^0.8.5` است که به این معناست که هر نسخه‌ای که حداقل 0.8.5 باشد ولی کمتر از 0.9.0 باشد. -Cargo considers these versions to have public APIs compatible with version -0.8.5, and this specification ensures you’ll get the latest patch release that -will still compile with the code in this chapter. Any version 0.9.0 or greater -is not guaranteed to have the same API as what the following examples use. +Cargo این نسخه‌ها را دارای API عمومی سازگار با نسخه 0.8.5 در نظر می‌گیرد و این مشخصه تضمین می‌کند که آخرین نسخه patch را دریافت خواهید کرد که همچنان با کد موجود در این فصل کامپایل می‌شود. هیچ تضمینی وجود ندارد که نسخه 0.9.0 یا بالاتر همان API را داشته باشد که مثال‌های زیر استفاده می‌کنند. -Now, without changing any of the code, let’s build the project, as shown in -Listing 2-2. +اکنون، بدون تغییر هیچ کدی، بیایید پروژه را بسازیم، همان‌طور که در لیستینگ 2-2 نشان داده شده است. -++ ```console $ cargo build @@ -403,35 +254,15 @@ $ cargo build -You may see different version numbers (but they will all be compatible with the -code, thanks to SemVer!) and different lines (depending on the operating -system), and the lines may be in a different order. - -When we include an external dependency, Cargo fetches the latest versions of -everything that dependency needs from the _registry_, which is a copy of data -from [Crates.io][cratesio]. Crates.io is where people in the Rust ecosystem -post their open source Rust projects for others to use. +ممکن است نسخه‌های متفاوتی را ببینید (اما همه آن‌ها با کد سازگار خواهند بود، به لطف SemVer!) و خطوط متفاوتی (بسته به سیستم‌عامل) داشته باشید، و این خطوط ممکن است به ترتیب متفاوتی ظاهر شوند. -After updating the registry, Cargo checks the `[dependencies]` section and -downloads any crates listed that aren’t already downloaded. In this case, -although we only listed `rand` as a dependency, Cargo also grabbed other crates -that `rand` depends on to work. After downloading the crates, Rust compiles -them and then compiles the project with the dependencies available. +وقتی یک وابستگی خارجی اضافه می‌کنیم، Cargo جدیدترین نسخه‌های هر چیزی که آن وابستگی نیاز دارد را از _رجیستری_ دریافت می‌کند، که یک کپی از داده‌های [Crates.io][cratesio] است. Crates.io جایی است که افراد در اکوسیستم Rust پروژه‌های منبع‌باز Rust خود را برای استفاده دیگران ارسال می‌کنند. -If you immediately run `cargo build` again without making any changes, you -won’t get any output aside from the `Finished` line. Cargo knows it has already -downloaded and compiled the dependencies, and you haven’t changed anything -about them in your _Cargo.toml_ file. Cargo also knows that you haven’t changed -anything about your code, so it doesn’t recompile that either. With nothing to -do, it simply exits. +پس از به‌روزرسانی رجیستری، Cargo بخش `[dependencies]` را بررسی می‌کند و هر crateی را که در لیست نیست و هنوز دانلود نشده است دانلود می‌کند. در این مورد، اگرچه ما فقط `rand` را به‌عنوان یک وابستگی لیست کرده‌ایم، Cargo سایر جعبه‌ها (crates)یی را که `rand` برای کارکردن به آن‌ها وابسته است نیز دریافت کرده است. پس از دانلود جعبه‌ها (crates)، Rust آن‌ها را کامپایل می‌کند و سپس پروژه را با وابستگی‌های موجود کامپایل می‌کند. -If you open the _src/main.rs_ file, make a trivial change, and then save it and -build again, you’ll only see two lines of output: +اگر بلافاصله دوباره دستور `cargo build` را اجرا کنید بدون اینکه هیچ تغییری ایجاد کرده باشید، خروجی‌ای به‌جز خط `Finished` دریافت نخواهید کرد. Cargo می‌داند که قبلاً وابستگی‌ها را دانلود و کامپایل کرده است، و شما هیچ تغییری در فایل _Cargo.toml_ خود نداده‌اید. Cargo همچنین می‌داند که شما هیچ تغییری در کد خود نداده‌اید، بنابراین آن را هم دوباره کامپایل نمی‌کند. وقتی کاری برای انجام دادن وجود ندارد، فقط خارج می‌شود. - +اگر فایل _src/main.rs_ را باز کنید، یک تغییر جزئی در آن ایجاد کنید، و سپس آن را ذخیره کرده و دوباره بسازید، فقط دو خط خروجی خواهید دید: ```console $ cargo build @@ -439,46 +270,17 @@ $ cargo build Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s ``` -These lines show that Cargo only updates the build with your tiny change to the -_src/main.rs_ file. Your dependencies haven’t changed, so Cargo knows it can -reuse what it has already downloaded and compiled for those. - -#### Ensuring Reproducible Builds with the _Cargo.lock_ File - -Cargo has a mechanism that ensures you can rebuild the same artifact every time -you or anyone else builds your code: Cargo will use only the versions of the -dependencies you specified until you indicate otherwise. For example, say that -next week version 0.8.6 of the `rand` crate comes out, and that version -contains an important bug fix, but it also contains a regression that will -break your code. To handle this, Rust creates the _Cargo.lock_ file the first -time you run `cargo build`, so we now have this in the _guessing_game_ -directory. - -When you build a project for the first time, Cargo figures out all the versions -of the dependencies that fit the criteria and then writes them to the -_Cargo.lock_ file. When you build your project in the future, Cargo will see -that the _Cargo.lock_ file exists and will use the versions specified there -rather than doing all the work of figuring out versions again. This lets you -have a reproducible build automatically. In other words, your project will -remain at 0.8.5 until you explicitly upgrade, thanks to the _Cargo.lock_ file. -Because the _Cargo.lock_ file is important for reproducible builds, it’s often -checked into source control with the rest of the code in your project. - -#### Updating a Crate to Get a New Version - -When you _do_ want to update a crate, Cargo provides the command `update`, -which will ignore the _Cargo.lock_ file and figure out all the latest versions -that fit your specifications in _Cargo.toml_. Cargo will then write those -versions to the _Cargo.lock_ file. In this case, Cargo will only look for -versions greater than 0.8.5 and less than 0.9.0. If the `rand` crate has -released the two new versions 0.8.6 and 0.9.0, you would see the following if -you ran `cargo update`: +این خطوط نشان می‌دهند که Cargo فقط با تغییر کوچک شما در فایل _src/main.rs_ بیلد را به‌روزرسانی کرده است. وابستگی‌های شما تغییری نکرده‌اند، بنابراین Cargo می‌داند که می‌تواند از آنچه قبلاً دانلود و کامپایل کرده است استفاده مجدد کند. - +#### اطمینان از بیلدهای قابل بازتولید با فایل _Cargo.lock_ + +Cargo مکانیزمی دارد که اطمینان می‌دهد شما یا هر کس دیگری بتوانید هر بار که کد خود را بیلد می‌کنید، همان نتیجه را دریافت کنید: Cargo تنها از نسخه‌هایی از وابستگی‌ها که مشخص کرده‌اید استفاده می‌کند، مگر اینکه خلاف آن را اعلام کنید. برای مثال، فرض کنید هفته آینده نسخه 0.8.6 از crate `rand` منتشر می‌شود و آن نسخه شامل یک رفع باگ مهم است، اما همچنین شامل یک برگشت (regression) است که کد شما را خراب می‌کند. برای مدیریت این موضوع، Rust فایل _Cargo.lock_ را در اولین باری که `cargo build` را اجرا می‌کنید ایجاد می‌کند، بنابراین اکنون این فایل در دایرکتوری _guessing_game_ وجود دارد. + +وقتی برای اولین بار پروژه‌ای را بیلد می‌کنید، Cargo همه نسخه‌های وابستگی‌هایی که با معیارها تطابق دارند را پیدا می‌کند و سپس آن‌ها را به فایل _Cargo.lock_ می‌نویسد. وقتی در آینده پروژه خود را بیلد می‌کنید، Cargo می‌بیند که فایل _Cargo.lock_ وجود دارد و از نسخه‌های مشخص‌شده در آن استفاده می‌کند، به جای اینکه تمام کار پیدا کردن نسخه‌ها را دوباره انجام دهد. این کار به شما اجازه می‌دهد که به‌طور خودکار یک بیلد قابل بازتولید داشته باشید. به عبارت دیگر، پروژه شما در نسخه 0.8.5 باقی خواهد ماند تا زمانی که به صورت صریح آن را به‌روزرسانی کنید، به لطف فایل _Cargo.lock_. چون فایل _Cargo.lock_ برای بیلدهای قابل بازتولید مهم است، معمولاً همراه با بقیه کد پروژه در سیستم کنترل نسخه (source control) ذخیره می‌شود. + +#### به‌روزرسانی یک crate برای دریافت نسخه جدید + +وقتی _می‌خواهید_ یک crate را به‌روزرسانی کنید، Cargo دستور `update` را فراهم می‌کند که فایل _Cargo.lock_ را نادیده می‌گیرد و تمام نسخه‌های جدیدی که با مشخصات شما در فایل _Cargo.toml_ سازگار هستند را پیدا می‌کند. سپس Cargo آن نسخه‌ها را به فایل _Cargo.lock_ می‌نویسد. در این مورد، Cargo تنها به دنبال نسخه‌هایی می‌گردد که بالاتر از 0.8.5 و کمتر از 0.9.0 باشند. اگر crate `rand` دو نسخه جدید 0.8.6 و 0.9.0 را منتشر کرده باشد، با اجرای `cargo update` چنین چیزی را خواهید دید: ```console $ cargo update @@ -487,32 +289,23 @@ $ cargo update Updating rand v0.8.5 -> v0.8.6 (available: v0.9.0) ``` -Cargo ignores the 0.9.0 release. At this point, you would also notice a change -in your _Cargo.lock_ file noting that the version of the `rand` crate you are -now using is 0.8.6. To use `rand` version 0.9.0 or any version in the 0.9._x_ -series, you’d have to update the _Cargo.toml_ file to look like this instead: +Cargo نسخه 0.9.0 را نادیده می‌گیرد. در این مرحله، شما همچنین تغییری در فایل _Cargo.lock_ مشاهده می‌کنید که نشان می‌دهد نسخه crate `rand` که اکنون استفاده می‌کنید 0.8.6 است. برای استفاده از نسخه 0.9.0 `rand` یا هر نسخه‌ای در سری 0.9._x_، باید فایل _Cargo.toml_ را به این شکل تغییر دهید: ```toml [dependencies] rand = "0.9.0" ``` -The next time you run `cargo build`, Cargo will update the registry of crates -available and reevaluate your `rand` requirements according to the new version -you have specified. +دفعه بعد که `cargo build` را اجرا کنید، Cargo رجیستری جعبه‌ها (crates)ی موجود را به‌روزرسانی می‌کند و نیازمندی‌های شما برای `rand` را بر اساس نسخه جدیدی که مشخص کرده‌اید ارزیابی می‌کند. + +چیزهای بیشتری درباره [Cargo][doccargo] و [اکوسیستم آن][doccratesio] وجود دارد که در فصل 14 بحث خواهیم کرد، اما فعلاً این تمام چیزی است که باید بدانید. Cargo استفاده از کتابخانه‌ها را بسیار آسان می‌کند، بنابراین Rustaceans می‌توانند پروژه‌های کوچک‌تری بنویسند که از تعدادی بسته تشکیل شده‌اند. -There’s a lot more to say about [Cargo][doccargo] and [its -ecosystem][doccratesio], which we’ll discuss in Chapter 14, but -for now, that’s all you need to know. Cargo makes it very easy to reuse -libraries, so Rustaceans are able to write smaller projects that are assembled -from a number of packages. +### تولید یک عدد تصادفی -### Generating a Random Number +بیایید استفاده از `rand` را برای تولید یک عدد برای حدس زدن شروع کنیم. مرحله بعد به‌روزرسانی فایل _src/main.rs_ است، همان‌طور که در لیستینگ 2-3 نشان داده شده است. -Let’s start using `rand` to generate a number to guess. The next step is to -update _src/main.rs_, as shown in Listing 2-3. + - ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-03/src/main.rs:all}} @@ -520,35 +313,15 @@ update _src/main.rs_, as shown in Listing 2-3. -First we add the line `use rand::Rng;`. The `Rng` trait defines methods that -random number generators implement, and this trait must be in scope for us to -use those methods. Chapter 10 will cover traits in detail. - -Next, we’re adding two lines in the middle. In the first line, we call the -`rand::thread_rng` function that gives us the particular random number -generator we’re going to use: one that is local to the current thread of -execution and is seeded by the operating system. Then we call the `gen_range` -method on the random number generator. This method is defined by the `Rng` -trait that we brought into scope with the `use rand::Rng;` statement. The -`gen_range` method takes a range expression as an argument and generates a -random number in the range. The kind of range expression we’re using here takes -the form `start..=end` and is inclusive on the lower and upper bounds, so we -need to specify `1..=100` to request a number between 1 and 100. - -> Note: You won’t just know which traits to use and which methods and functions -> to call from a crate, so each crate has documentation with instructions for -> using it. Another neat feature of Cargo is that running the `cargo doc -> --open` command will build documentation provided by all your dependencies -> locally and open it in your browser. If you’re interested in other -> functionality in the `rand` crate, for example, run `cargo doc --open` and -> click `rand` in the sidebar on the left. - -The second new line prints the secret number. This is useful while we’re -developing the program to be able to test it, but we’ll delete it from the -final version. It’s not much of a game if the program prints the answer as soon -as it starts! - -Try running the program a few times: +ابتدا خط `use rand::Rng;` را اضافه می‌کنیم. صفت (trait) `Rng` متدهایی را تعریف می‌کند که تولیدکنندگان اعداد تصادفی پیاده‌سازی می‌کنند، و این صفت باید در دامنه باشد تا بتوانیم از آن متدها استفاده کنیم. فصل 10 به‌طور مفصل به بررسی صفت‌ها خواهد پرداخت. + +سپس دو خط در وسط اضافه می‌کنیم. در خط اول، تابع `rand::thread_rng` را فراخوانی می‌کنیم که تولیدکننده اعداد تصادفی خاصی را که می‌خواهیم استفاده کنیم به ما می‌دهد: تولیدکننده‌ای که محلی برای نخ فعلی اجرا است و توسط سیستم‌عامل seed می‌شود. سپس متد `gen_range` را روی تولیدکننده اعداد تصادفی فراخوانی می‌کنیم. این متد توسط صفت `Rng` که با دستور `use rand::Rng;` وارد دامنه کردیم، تعریف شده است. متد `gen_range` یک عبارت بازه‌ای را به‌عنوان آرگومان می‌گیرد و یک عدد تصادفی در آن بازه تولید می‌کند. نوع عبارت بازه‌ای که در اینجا استفاده می‌کنیم به صورت `start..=end` است و شامل حد پایین و بالا می‌شود، بنابراین باید `1..=100` را مشخص کنیم تا عددی بین 1 تا 100 درخواست کنیم. + +> نکته: شما نمی‌توانید به‌طور پیش‌فرض بدانید که کدام صفت‌ها را باید استفاده کنید و کدام متدها و توابع را از یک crate فراخوانی کنید، بنابراین هر crate دارای مستنداتی با دستورالعمل‌هایی برای استفاده از آن است. ویژگی جالب دیگر Cargo این است که اجرای دستور `cargo doc --open` مستندات ارائه‌شده توسط تمام وابستگی‌های شما را به‌صورت محلی می‌سازد و در مرورگر شما باز می‌کند. اگر به دیگر قابلیت‌های crate `rand` علاقه‌مند هستید، برای مثال دستور `cargo doc --open` را اجرا کنید و روی `rand` در نوار کناری سمت چپ کلیک کنید. + +خط جدید دوم عدد مخفی را چاپ می‌کند. این خط در حین توسعه برنامه برای آزمایش آن مفید است، اما در نسخه نهایی آن را حذف خواهیم کرد. اگر برنامه به محض شروع پاسخ را چاپ کند، خیلی بازی هیجان‌انگیزی نخواهد بود! + +برنامه را چند بار اجرا کنید: expression to decide what to do next based on -which variant of `Ordering` was returned from the call to `cmp` with the values -in `guess` and `secret_number`. - -A `match` expression is made up of _arms_. An arm consists of a _pattern_ to -match against, and the code that should be run if the value given to `match` -fits that arm’s pattern. Rust takes the value given to `match` and looks -through each arm’s pattern in turn. Patterns and the `match` construct are -powerful Rust features: they let you express a variety of situations your code -might encounter and they make sure you handle them all. These features will be -covered in detail in Chapter 6 and Chapter 19, respectively. - -Let’s walk through an example with the `match` expression we use here. Say that -the user has guessed 50 and the randomly generated secret number this time is -38. - -When the code compares 50 to 38, the `cmp` method will return -`Ordering::Greater` because 50 is greater than 38. The `match` expression gets -the `Ordering::Greater` value and starts checking each arm’s pattern. It looks -at the first arm’s pattern, `Ordering::Less`, and sees that the value -`Ordering::Greater` does not match `Ordering::Less`, so it ignores the code in -that arm and moves to the next arm. The next arm’s pattern is -`Ordering::Greater`, which _does_ match `Ordering::Greater`! The associated -code in that arm will execute and print `Too big!` to the screen. The `match` -expression ends after the first successful match, so it won’t look at the last -arm in this scenario. - -However, the code in Listing 2-4 won’t compile yet. Let’s try it: +ابتدا یک دستور `use` دیگر اضافه می‌کنیم تا نوعی به نام `std::cmp::Ordering` را از کتابخانه استاندارد وارد دامنه کنیم. نوع `Ordering` یک enum دیگر است و دارای متغیرهای `Less`، `Greater` و `Equal` است. این‌ها سه نتیجه ممکن هنگام مقایسه دو مقدار هستند. + +سپس پنج خط جدید در انتهای کد اضافه می‌کنیم که از نوع `Ordering` استفاده می‌کنند. متد `cmp` دو مقدار را مقایسه می‌کند و می‌تواند روی هر چیزی که قابل مقایسه باشد فراخوانی شود. این متد یک ارجاع به مقداری که می‌خواهید مقایسه کنید می‌گیرد: در اینجا مقایسه بین `guess` و `secret_number` است. سپس یکی از متغیرهای enum `Ordering` که با دستور `use` به دامنه آوردیم را بازمی‌گرداند. از یک عبارت [`match`][match] برای تصمیم‌گیری در مورد اقدام بعدی بر اساس اینکه کدام متغیر `Ordering` از فراخوانی `cmp` با مقادیر `guess` و `secret_number` بازگشته است استفاده می‌کنیم. + +یک عبارت `match` از _شاخه‌ها (arms)_ تشکیل شده است. یک شاخه شامل یک _الگو_ برای مطابقت است و کدی که باید اجرا شود اگر مقدار داده‌شده به `match` با الگوی آن شاخه تطابق داشته باشد. Rust مقدار داده‌شده به `match` را گرفته و به ترتیب هر الگوی شاخه را بررسی می‌کند. الگوها و سازه `match` از ویژگی‌های قدرتمند Rust هستند: آن‌ها به شما اجازه می‌دهند موقعیت‌های مختلفی که کد شما ممکن است با آن‌ها روبرو شود را بیان کنید و اطمینان حاصل کنید که همه آن‌ها را مدیریت می‌کنید. این ویژگی‌ها به‌طور مفصل در فصل 6 و فصل 19 پوشش داده خواهند شد. + +بیایید با یک مثال از عبارت `match` که در اینجا استفاده کرده‌ایم، آن را بررسی کنیم. فرض کنید کاربر 50 را حدس زده و عدد مخفی که این بار به‌طور تصادفی تولید شده 38 است. + +وقتی کد 50 را با 38 مقایسه می‌کند، متد `cmp` مقدار `Ordering::Greater` را بازمی‌گرداند زیرا 50 بزرگ‌تر از 38 است. عبارت `match` مقدار `Ordering::Greater` را گرفته و شروع به بررسی هر الگوی شاخه می‌کند. به الگوی شاخه اول، `Ordering::Less` نگاه می‌کند و می‌بیند که مقدار `Ordering::Greater` با `Ordering::Less` تطابق ندارد، بنابراین کد موجود در آن شاخه را نادیده می‌گیرد و به شاخه بعدی می‌رود. الگوی شاخه بعدی `Ordering::Greater` است که با `Ordering::Greater` تطابق دارد! کد مرتبط با آن شاخه اجرا شده و عبارت `Too big!` را روی صفحه چاپ می‌کند. عبارت `match` پس از اولین تطابق موفقیت‌آمیز پایان می‌یابد، بنابراین در این سناریو به شاخه آخر نگاه نمی‌کند. + +با این حال، کد موجود در لیستینگ 2-4 هنوز کامپایل نخواهد شد. بیایید آن را امتحان کنیم: , but for now, know that this feature is -often used when you want to convert a value from one type to another type. - -We bind this new variable to the expression `guess.trim().parse()`. The `guess` -in the expression refers to the original `guess` variable that contained the -input as a string. The `trim` method on a `String` instance will eliminate any -whitespace at the beginning and end, which we must do before we can convert the -string to a `u32`, which can only contain numerical data. The user must press -enter to satisfy `read_line` and input their guess, which adds a -newline character to the string. For example, if the user types 5 and -presses enter, `guess` looks like this: `5\n`. The `\n` represents -“newline.” (On Windows, pressing enter results in a carriage return -and a newline, `\r\n`.) The `trim` method eliminates `\n` or `\r\n`, resulting -in just `5`. - -The [`parse` method on strings][parse] converts a string to -another type. Here, we use it to convert from a string to a number. We need to -tell Rust the exact number type we want by using `let guess: u32`. The colon -(`:`) after `guess` tells Rust we’ll annotate the variable’s type. Rust has a -few built-in number types; the `u32` seen here is an unsigned, 32-bit integer. -It’s a good default choice for a small positive number. You’ll learn about -other number types in [Chapter 3][integers]. - -Additionally, the `u32` annotation in this example program and the comparison -with `secret_number` means Rust will infer that `secret_number` should be a -`u32` as well. So now the comparison will be between two values of the same -type! - -The `parse` method will only work on characters that can logically be converted -into numbers and so can easily cause errors. If, for example, the string -contained `A👍%`, there would be no way to convert that to a number. Because it -might fail, the `parse` method returns a `Result` type, much as the `read_line` -method does (discussed earlier in [“Handling Potential Failure with -`Result`”](#handling-potential-failure-with-result)). We’ll treat -this `Result` the same way by using the `expect` method again. If `parse` -returns an `Err` `Result` variant because it couldn’t create a number from the -string, the `expect` call will crash the game and print the message we give it. -If `parse` can successfully convert the string to a number, it will return the -`Ok` variant of `Result`, and `expect` will return the number that we want from -the `Ok` value. - -Let’s run the program now: +ما یک متغیر به نام `guess` ایجاد می‌کنیم. اما صبر کنید، آیا برنامه قبلاً یک متغیر به نام `guess` ندارد؟ دارد، اما Rust به‌طور مفیدی به ما اجازه می‌دهد مقدار قبلی `guess` را با یک مقدار جدید پوشش دهیم. _پوشش‌دهی_ به ما اجازه می‌دهد که از نام متغیر `guess` دوباره استفاده کنیم، به‌جای اینکه مجبور شویم دو متغیر منحصربه‌فرد مانند `guess_str` و `guess` ایجاد کنیم. این موضوع را در [فصل 3][shadowing] با جزئیات بیشتری بررسی خواهیم کرد، اما فعلاً بدانید که این ویژگی اغلب زمانی استفاده می‌شود که بخواهید مقدار را از یک نوع به نوع دیگری تبدیل کنید. + +ما این متغیر جدید را به عبارت `guess.trim().parse()` متصل می‌کنیم. `guess` در این عبارت به متغیر اصلی `guess` که ورودی به‌صورت رشته‌ای بود اشاره دارد. متد `trim` روی یک نمونه `String` تمام فضای سفید در ابتدا و انتهای رشته را حذف می‌کند، که قبل از تبدیل رشته به `u32` که فقط می‌تواند داده‌های عددی داشته باشد، باید این کار را انجام دهیم. کاربر باید کلید enter را فشار دهد تا `read_line` مقدار ورودی را دریافت کند، که یک کاراکتر newline به رشته اضافه می‌کند. برای مثال، اگر کاربر کلید 5 را تایپ کند و enter را فشار دهد، `guess` به این شکل خواهد بود: `5\n`. `\n` نشان‌دهنده "خط جدید" است. (در ویندوز، فشار دادن enter منجر به carriage return و newline، یعنی `\r\n` می‌شود.) متد `trim` `\n` یا `\r\n` را حذف می‌کند و نتیجه فقط `5` است. + +متد [`parse` روی رشته‌ها][parse] یک رشته را به نوع دیگری تبدیل می‌کند. اینجا از آن برای تبدیل یک رشته به عدد استفاده می‌کنیم. باید به Rust نوع عدد دقیق موردنظرمان را با استفاده از `let guess: u32` بگوییم. علامت `:` بعد از `guess` به Rust می‌گوید که نوع متغیر را مشخص خواهیم کرد. Rust چند نوع عدد داخلی دارد؛ `u32` که اینجا دیده می‌شود، یک عدد صحیح 32 بیتی بدون علامت است. این یک انتخاب پیش‌فرض خوب برای یک عدد مثبت کوچک است. درباره دیگر انواع عددی در [فصل 3][integers] خواهید آموخت. + +علاوه بر این، حاشیه‌نویسی `u32` در این برنامه نمونه و مقایسه با `secret_number` به این معناست که Rust استنباط خواهد کرد که `secret_number` نیز باید یک `u32` باشد. بنابراین اکنون مقایسه بین دو مقدار از یک نوع خواهد بود! + +متد `parse` فقط روی کاراکترهایی کار می‌کند که منطقی بتوان آن‌ها را به اعداد تبدیل کرد و بنابراین به‌راحتی می‌تواند باعث خطا شود. برای مثال، اگر رشته‌ای شامل `A👍%` باشد، هیچ راهی برای تبدیل آن به عدد وجود ندارد. چون ممکن است این عملیات شکست بخورد، متد `parse` نوع `Result` را برمی‌گرداند، دقیقاً مانند متد `read_line` (که قبلاً در [“مدیریت خطای احتمالی با `Result`”](#handling-potential-failure-with-result) بحث کردیم). ما این `Result` را همان‌طور که قبلاً انجام دادیم با استفاده مجدد از متد `expect` مدیریت خواهیم کرد. اگر `parse` متغیر `Err` از نوع `Result` را برگرداند زیرا نتوانست یک عدد از رشته ایجاد کند، فراخوانی `expect` بازی را متوقف کرده و پیام مشخص‌شده را چاپ می‌کند. اگر `parse` بتواند با موفقیت رشته را به عدد تبدیل کند، متغیر `Ok` از نوع `Result` را برمی‌گرداند و `expect` عدد مورد نظر را از مقدار `Ok` بازمی‌گرداند. + : if -the user enters a non-number answer, the program will crash. We can take -advantage of that to allow the user to quit, as shown here: +کاربر همیشه می‌تواند برنامه را با استفاده از میانبر صفحه‌کلید ctrl-c متوقف کند. اما راه دیگری برای فرار از این هیولای سیری‌ناپذیر وجود دارد، همان‌طور که در بحث `parse` در [“مقایسه حدس با عدد مخفی”](#comparing-the-guess-to-the-secret-number) ذکر شد: اگر کاربر پاسخی غیرعددی وارد کند، برنامه متوقف می‌شود. می‌توانیم از این موضوع استفاده کنیم تا به کاربر اجازه دهیم خارج شود، همان‌طور که در اینجا نشان داده شده است: . +> زبان Rust مجموعه‌ای از _کلمات کلیدی_ دارد که فقط برای استفاده توسط زبان رزرو شده‌اند، همانند سایر زبان‌ها. به خاطر داشته باشید که نمی‌توانید از این کلمات به‌عنوان نام متغیرها یا توابع استفاده کنید. اکثر کلمات کلیدی معانی خاصی دارند و شما از آن‌ها برای انجام وظایف مختلف در برنامه‌های Rust خود استفاده خواهید کرد؛ تعدادی از آن‌ها در حال حاضر هیچ عملکردی ندارند اما برای قابلیت‌هایی که ممکن است در آینده به Rust اضافه شوند رزرو شده‌اند. شما می‌توانید لیست کلمات کلیدی را در [ضمیمه الف][appendix_a] پیدا کنید. [appendix_a]: appendix-01-keywords.md diff --git a/src/ch03-01-variables-and-mutability.md b/src/ch03-01-variables-and-mutability.md index e5d599c030..8564e4287a 100644 --- a/src/ch03-01-variables-and-mutability.md +++ b/src/ch03-01-variables-and-mutability.md @@ -1,134 +1,78 @@ -## Variables and Mutability +## متغیرها و تغییرپذیری -As mentioned in the [“Storing Values with -Variables”][storing-values-with-variables] section, by default, -variables are immutable. This is one of many nudges Rust gives you to write -your code in a way that takes advantage of the safety and easy concurrency that -Rust offers. However, you still have the option to make your variables mutable. -Let’s explore how and why Rust encourages you to favor immutability and why -sometimes you might want to opt out. +همان‌طور که در بخش [“ذخیره مقادیر با استفاده از متغیرها”][storing-values-with-variables] ذکر شد، به طور پیش‌فرض متغیرها در Rust غیرقابل‌تغییر هستند. این یکی از راه‌هایی است که Rust شما را به نوشتن کدی که از ایمنی و همزمانی آسان ارائه‌شده توسط این زبان بهره می‌برد، تشویق می‌کند. با این حال، شما همچنان گزینه‌ای دارید تا متغیرهای خود را قابل‌تغییر کنید. بیایید بررسی کنیم که چگونه و چرا Rust شما را تشویق به استفاده از غیرقابل‌تغییر بودن می‌کند و چرا ممکن است گاهی بخواهید این حالت را تغییر دهید. -When a variable is immutable, once a value is bound to a name, you can’t change -that value. To illustrate this, generate a new project called _variables_ in -your _projects_ directory by using `cargo new variables`. +وقتی یک متغیر غیرقابل‌تغییر است، وقتی مقداری به یک نام متصل شد، نمی‌توانید آن مقدار را تغییر دهید. برای نشان دادن این موضوع، یک پروژه جدید به نام _variables_ در دایرکتوری _projects_ خود ایجاد کنید با استفاده از دستور `cargo new variables`. -Then, in your new _variables_ directory, open _src/main.rs_ and replace its -code with the following code, which won’t compile just yet: +سپس، در دایرکتوری جدید _variables_ خود، فایل _src/main.rs_ را باز کنید و کد آن را با کد زیر جایگزین کنید، که هنوز کامپایل نخواهد شد: -Filename: src/main.rs +تام فایل: src/main.rs ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/src/main.rs}} ``` -Save and run the program using `cargo run`. You should receive an error message -regarding an immutability error, as shown in this output: + +فایل را ذخیره کنید و برنامه را با استفاده از `cargo run` اجرا کنید. باید یک پیام خطا در مورد غیرقابل‌تغییر بودن دریافت کنید، همان‌طور که در این خروجی نشان داده شده است: + ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/output.txt}} ``` -This example shows how the compiler helps you find errors in your programs. -Compiler errors can be frustrating, but really they only mean your program -isn’t safely doing what you want it to do yet; they do _not_ mean that you’re -not a good programmer! Experienced Rustaceans still get compiler errors. - -You received the error message `` cannot assign twice to immutable variable `x` `` because you tried to assign a second value to the immutable `x` variable. - -It’s important that we get compile-time errors when we attempt to change a -value that’s designated as immutable because this very situation can lead to -bugs. If one part of our code operates on the assumption that a value will -never change and another part of our code changes that value, it’s possible -that the first part of the code won’t do what it was designed to do. The cause -of this kind of bug can be difficult to track down after the fact, especially -when the second piece of code changes the value only _sometimes_. The Rust -compiler guarantees that when you state that a value won’t change, it really -won’t change, so you don’t have to keep track of it yourself. Your code is thus -easier to reason through. - -But mutability can be very useful, and can make code more convenient to write. -Although variables are immutable by default, you can make them mutable by -adding `mut` in front of the variable name as you did in [Chapter -2][storing-values-with-variables]. Adding `mut` also conveys -intent to future readers of the code by indicating that other parts of the code -will be changing this variable’s value. - -For example, let’s change _src/main.rs_ to the following: -Filename: src/main.rs +این مثال نشان می‌دهد که چگونه کامپایلر به شما کمک می‌کند تا خطاها را در برنامه‌های خود پیدا کنید. خطاهای کامپایلر ممکن است ناامیدکننده باشند، اما در واقع به این معنا هستند که برنامه شما هنوز به طور ایمن کاری را که می‌خواهید انجام نمی‌دهد؛ این به هیچ وجه به این معنا نیست که شما برنامه‌نویس خوبی نیستید! حتی برنامه‌نویسان باتجربه Rust نیز همچنان خطاهای کامپایلر دریافت می‌کنند. + +شما پیام خطای `` cannot assign twice to immutable variable `x` `` را دریافت کردید زیرا سعی کردید مقدار دوم را به متغیر غیرقابل‌تغییر `x` تخصیص دهید. + +این بسیار مهم است که ما خطاهای زمان کامپایل را دریافت کنیم وقتی سعی می‌کنیم مقدار یک متغیر غیرقابل‌تغییر را تغییر دهیم زیرا این وضعیت می‌تواند به باگ منجر شود. اگر یک بخش از کد ما با این فرض عمل کند که یک مقدار هرگز تغییر نمی‌کند و بخش دیگری از کد آن مقدار را تغییر دهد، ممکن است بخش اول کد کاری که برای انجام آن طراحی شده بود را به درستی انجام ندهد. علت این نوع باگ می‌تواند بعد از وقوع به سختی قابل‌ردیابی باشد، به‌ویژه وقتی که بخش دوم کد فقط _گاهی اوقات_ مقدار را تغییر می‌دهد. کامپایلر Rust تضمین می‌کند که وقتی بیان می‌کنید یک مقدار تغییر نخواهد کرد، واقعاً تغییر نخواهد کرد، بنابراین نیازی نیست که خودتان این موضوع را پیگیری کنید. به این ترتیب کد شما راحت‌تر قابل‌درک خواهد بود. + +اما قابلیت تغییر می‌تواند بسیار مفید باشد و نوشتن کد را راحت‌تر کند. اگرچه متغیرها به طور پیش‌فرض غیرقابل‌تغییر هستند، می‌توانید با اضافه کردن `mut` قبل از نام متغیر آنها را قابل‌تغییر کنید، همان‌طور که در [فصل ۲][storing-values-with-variables] انجام دادید. اضافه کردن `mut` همچنین به خوانندگان آینده کد نیت شما را نشان می‌دهد که قسمت‌های دیگر کد مقدار این متغیر را تغییر خواهند داد. + +برای مثال، بیایید فایل _src/main.rs_ را به کد زیر تغییر دهیم: ```rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/src/main.rs}} ``` -When we run the program now, we get this: + +وقتی اکنون برنامه را اجرا می‌کنیم، این خروجی را دریافت می‌کنیم: + + ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/output.txt}} ``` -We’re allowed to change the value bound to `x` from `5` to `6` when `mut` is -used. Ultimately, deciding whether to use mutability or not is up to you and -depends on what you think is clearest in that particular situation. -### Constants -Like immutable variables, _constants_ are values that are bound to a name and -are not allowed to change, but there are a few differences between constants -and variables. +ما اجازه داریم مقدار مرتبط با `x` را از `5` به `6` تغییر دهیم وقتی که از `mut` استفاده شود. در نهایت، تصمیم‌گیری در مورد استفاده یا عدم استفاده از قابلیت تغییر به عهده شما است و به این بستگی دارد که در آن موقعیت خاص چه چیزی واضح‌تر به نظر می‌رسد. + +### ثابت ها -First, you aren’t allowed to use `mut` with constants. Constants aren’t just -immutable by default—they’re always immutable. You declare constants using the -`const` keyword instead of the `let` keyword, and the type of the value _must_ -be annotated. We’ll cover types and type annotations in the next section, -[“Data Types”][data-types], so don’t worry about the details -right now. Just know that you must always annotate the type. +مانند متغیرهای غیرقابل‌تغییر، _ثابت‌ها_ مقادیری هستند که به یک نام متصل می‌شوند و اجازه تغییر ندارند، اما چند تفاوت بین ثابت‌ها و متغیرها وجود دارد. -Constants can be declared in any scope, including the global scope, which makes -them useful for values that many parts of code need to know about. +اول، شما نمی‌توانید از `mut` با ثابت‌ها استفاده کنید. ثابت‌ها نه تنها به طور پیش‌فرض غیرقابل‌تغییر هستند، بلکه همیشه غیرقابل‌تغییر هستند. شما ثابت‌ها را با استفاده از کلیدواژه `const` به جای کلیدواژه `let` تعریف می‌کنید و نوع مقدار _باید_ مشخص شود. ما در بخش بعدی [“انواع داده”][data-types] درباره انواع و حاشیه‌نویسی نوع صحبت خواهیم کرد، بنابراین نگران جزئیات آن در حال حاضر نباشید. فقط بدانید که همیشه باید نوع را مشخص کنید. -The last difference is that constants may be set only to a constant expression, -not the result of a value that could only be computed at runtime. +ثابت‌ها می‌توانند در هر دامنه‌ای، از جمله دامنه‌ی جهانی، تعریف شوند، که این ویژگی آنها را برای مقادیری که بخش‌های مختلف کد باید بدانند مفید می‌سازد. -Here’s an example of a constant declaration: +آخرین تفاوت این است که ثابت‌ها فقط می‌توانند به یک عبارت ثابت تنظیم شوند، نه نتیجه‌ای که فقط می‌تواند در زمان اجرا محاسبه شود. + +در اینجا یک مثال از تعریف ثابت آورده شده است: ```rust const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3; ``` -The constant’s name is `THREE_HOURS_IN_SECONDS` and its value is set to the -result of multiplying 60 (the number of seconds in a minute) by 60 (the number -of minutes in an hour) by 3 (the number of hours we want to count in this -program). Rust’s naming convention for constants is to use all uppercase with -underscores between words. The compiler is able to evaluate a limited set of -operations at compile time, which lets us choose to write out this value in a -way that’s easier to understand and verify, rather than setting this constant -to the value 10,800. See the [Rust Reference’s section on constant -evaluation][const-eval] for more information on what operations can be used -when declaring constants. - -Constants are valid for the entire time a program runs, within the scope in -which they were declared. This property makes constants useful for values in -your application domain that multiple parts of the program might need to know -about, such as the maximum number of points any player of a game is allowed to -earn, or the speed of light. - -Naming hardcoded values used throughout your program as constants is useful in -conveying the meaning of that value to future maintainers of the code. It also -helps to have only one place in your code you would need to change if the -hardcoded value needed to be updated in the future. +نام ثابت `THREE_HOURS_IN_SECONDS` است و مقدار آن برابر با نتیجه ضرب ۶۰ (تعداد ثانیه‌ها در یک دقیقه) در ۶۰ (تعداد دقیقه‌ها در یک ساعت) در ۳ (تعداد ساعت‌هایی که می‌خواهیم در این برنامه شمارش کنیم) تنظیم شده است. قانون نام‌گذاری ثابت‌ها در Rust استفاده از حروف بزرگ با خط زیر (_) بین کلمات است. کامپایلر قادر است مجموعه محدودی از عملیات را در زمان کامپایل ارزیابی کند، که به ما این امکان را می‌دهد تا این مقدار را به صورتی بنویسیم که آسان‌تر قابل‌درک و بررسی باشد، به جای تنظیم این ثابت به مقدار ۱۰،۸۰۰. برای اطلاعات بیشتر در مورد اینکه چه عملیات‌هایی می‌توانند در زمان تعریف ثابت‌ها استفاده شوند، به [بخش ارزیابی ثابت‌ها در مرجع Rust][const-eval] مراجعه کنید. + +ثابت‌ها برای تمام مدت اجرای یک برنامه، در دامنه‌ای که در آن تعریف شده‌اند، معتبر هستند. این ویژگی، ثابت‌ها را برای مقادیر موجود در دامنه برنامه شما که ممکن است بخش‌های مختلف برنامه نیاز به دانستن آنها داشته باشند، مانند حداکثر تعداد امتیازاتی که هر بازیکن یک بازی می‌تواند کسب کند یا سرعت نور، مفید می‌سازد. + +نام‌گذاری مقادیر ثابت در سراسر برنامه شما به عنوان ثابت‌ها، در انتقال معنی آن مقدار به نگهدارندگان آینده کد شما مفید است. همچنین این کمک می‌کند که فقط یک مکان در کد وجود داشته باشد که اگر مقدار ثابت نیاز به به‌روزرسانی داشت، باید تغییر کند. ### Shadowing -As you saw in the guessing game tutorial in [Chapter -2][comparing-the-guess-to-the-secret-number], you can declare a -new variable with the same name as a previous variable. Rustaceans say that the -first variable is _shadowed_ by the second, which means that the second -variable is what the compiler will see when you use the name of the variable. -In effect, the second variable overshadows the first, taking any uses of the -variable name to itself until either it itself is shadowed or the scope ends. -We can shadow a variable by using the same variable’s name and repeating the -use of the `let` keyword as follows: +همان‌طور که در آموزش بازی حدس زدن در [فصل ۲][comparing-the-guess-to-the-secret-number] دیدید، شما می‌توانید یک متغیر جدید با همان نام متغیر قبلی تعریف کنید. Rustaceanها می‌گویند که متغیر اول توسط متغیر دوم _سایه انداخته شده است_، به این معنا که متغیر دوم چیزی است که کامپایلر وقتی از نام متغیر استفاده می‌کنید می‌بیند. در واقع، متغیر دوم متغیر اول را تحت‌الشعاع قرار می‌دهد، استفاده‌های مربوط به نام متغیر را به خود اختصاص می‌دهد تا زمانی که یا خودش تحت‌الشعاع قرار بگیرد یا دامنه تمام شود. ما می‌توانیم یک متغیر را با استفاده از همان نام متغیر و تکرار استفاده از کلیدواژه `let` به شرح زیر سایه‌اندازی کنیم: Filename: src/main.rs @@ -136,54 +80,30 @@ use of the `let` keyword as follows: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/src/main.rs}} ``` -This program first binds `x` to a value of `5`. Then it creates a new variable -`x` by repeating `let x =`, taking the original value and adding `1` so the -value of `x` is then `6`. Then, within an inner scope created with the curly -brackets, the third `let` statement also shadows `x` and creates a new -variable, multiplying the previous value by `2` to give `x` a value of `12`. -When that scope is over, the inner shadowing ends and `x` returns to being `6`. -When we run this program, it will output the following: +این برنامه ابتدا `x` را به مقدار `۵` متصل می‌کند. سپس یک متغیر جدید `x` با تکرار `let x =` ایجاد می‌کند و مقدار اصلی را می‌گیرد و `۱` اضافه می‌کند، بنابراین مقدار `x` به `۶` تغییر می‌کند. سپس، در یک دامنه داخلی که با آکولادها ایجاد شده است، عبارت سوم `let` نیز `x` را سایه‌اندازی می‌کند و یک متغیر جدید ایجاد می‌کند که مقدار قبلی را در `۲` ضرب می‌کند و به `x` مقدار `۱۲` می‌دهد. وقتی آن دامنه تمام می‌شود، سایه‌اندازی داخلی پایان می‌یابد و `x` به مقدار `۶` بازمی‌گردد. وقتی این برنامه را اجرا می‌کنیم، خروجی زیر را دریافت می‌کنیم: ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/output.txt}} ``` -Shadowing is different from marking a variable as `mut` because we’ll get a -compile-time error if we accidentally try to reassign to this variable without -using the `let` keyword. By using `let`, we can perform a few transformations -on a value but have the variable be immutable after those transformations have -been completed. +سایه‌اندازی با علامت‌گذاری متغیر به‌عنوان `mut` متفاوت است، زیرا اگر به طور تصادفی سعی کنید به این متغیر بدون استفاده از کلیدواژه `let` مقدار جدیدی تخصیص دهید، یک خطای زمان کامپایل دریافت می‌کنید. با استفاده از `let`، ما می‌توانیم چند تبدیل روی یک مقدار انجام دهیم، اما متغیر بعد از اتمام این تبدیل‌ها غیرقابل تغییر باقی می‌ماند. -The other difference between `mut` and shadowing is that because we’re -effectively creating a new variable when we use the `let` keyword again, we can -change the type of the value but reuse the same name. For example, say our -program asks a user to show how many spaces they want between some text by -inputting space characters, and then we want to store that input as a number: +تفاوت دیگر بین `mut` و سایه‌اندازی این است که به دلیل اینکه ما عملاً یک متغیر جدید ایجاد می‌کنیم وقتی دوباره از کلیدواژه `let` استفاده می‌کنیم، می‌توانیم نوع مقدار را تغییر دهیم اما همان نام را دوباره استفاده کنیم. برای مثال، فرض کنید برنامه ما از یک کاربر می‌خواهد تا نشان دهد که چند فاصله می‌خواهد بین متن‌های خاص داشته باشد با وارد کردن کاراکترهای فاصله، و سپس می‌خواهیم آن ورودی را به‌عنوان یک عدد ذخیره کنیم: ```rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-04-shadowing-can-change-types/src/main.rs:here}} ``` -The first `spaces` variable is a string type and the second `spaces` variable -is a number type. Shadowing thus spares us from having to come up with -different names, such as `spaces_str` and `spaces_num`; instead, we can reuse -the simpler `spaces` name. However, if we try to use `mut` for this, as shown -here, we’ll get a compile-time error: +اولین متغیر `spaces` یک نوع رشته است و دومین متغیر `spaces` یک نوع عدد است. سایه‌اندازی در نتیجه ما را از نیاز به یافتن نام‌های مختلف، مانند `spaces_str` و `spaces_num` نجات می‌دهد. به جای آن، می‌توانیم از نام ساده‌تر `spaces` استفاده کنیم. با این حال، اگر سعی کنیم برای این کار از `mut` استفاده کنیم، همان‌طور که در اینجا نشان داده شده است، یک خطای زمان کامپایل دریافت می‌کنیم: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/src/main.rs:here}} ``` -The error says we’re not allowed to mutate a variable’s type: +خطا می‌گوید که مجاز نیستیم نوع متغیر را تغییر دهیم: ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt}} ``` -Now that we’ve explored how variables work, let’s look at more data types they -can have. - -[comparing-the-guess-to-the-secret-number]: ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number -[data-types]: ch03-02-data-types.html#data-types -[storing-values-with-variables]: ch02-00-guessing-game-tutorial.html#storing-values-with-variables -[const-eval]: ../reference/const_eval.html +حال که بررسی کردیم متغیرها چگونه کار می‌کنند، بیایید نگاهی به انواع داده‌های بیشتری بیندازیم که متغیرها می‌توانند داشته باشند. diff --git a/src/ch03-02-data-types.md b/src/ch03-02-data-types.md index 7d0c32891c..2e43f103c2 100644 --- a/src/ch03-02-data-types.md +++ b/src/ch03-02-data-types.md @@ -1,138 +1,92 @@ -## Data Types +## انواع داده‌ها -Every value in Rust is of a certain _data type_, which tells Rust what kind of -data is being specified so it knows how to work with that data. We’ll look at -two data type subsets: scalar and compound. +هر مقدار در زبان راست نوع خاصی از _داده_ را دارد که به راست می‌گوید چه نوع داده‌ای مشخص شده است تا بداند چگونه با آن داده کار کند. ما به دو زیرمجموعه از انواع داده نگاه خواهیم کرد: انواع ساده و ترکیبی. -Keep in mind that Rust is a _statically typed_ language, which means that it -must know the types of all variables at compile time. The compiler can usually -infer what type we want to use based on the value and how we use it. In cases -when many types are possible, such as when we converted a `String` to a numeric -type using `parse` in the [“Comparing the Guess to the Secret -Number”][comparing-the-guess-to-the-secret-number] section in -Chapter 2, we must add a type annotation, like this: +به خاطر داشته باشید که راست یک زبان _ایستا-تایپ_ است، به این معنا که باید نوع تمام متغیرها در زمان کامپایل مشخص باشد. کامپایلر معمولاً می‌تواند بر اساس مقدار و نحوه استفاده از آن، نوع مورد نظر ما را حدس بزند. در مواردی که انواع متعددی ممکن است، مانند زمانی که یک `String` را به نوع عددی تبدیل کردیم در بخش [“مقایسه حدس با عدد مخفی”][comparing-the-guess-to-the-secret-number] در فصل 2، باید یک تعریف نوع اضافه کنیم، مانند این: ```rust let guess: u32 = "42".parse().expect("Not a number!"); ``` -If we don’t add the `: u32` type annotation shown in the preceding code, Rust -will display the following error, which means the compiler needs more -information from us to know which type we want to use: +اگر تعریف نوع `: u32` را که در کد بالا آمده است اضافه نکنیم، راست خطای زیر را نمایش می‌دهد، که به معنای این است که کامپایلر به اطلاعات بیشتری از ما نیاز دارد تا بداند کدام نوع را می‌خواهیم استفاده کنیم: ```console {{#include ../listings/ch03-common-programming-concepts/output-only-01-no-type-annotations/output.txt}} ``` -You’ll see different type annotations for other data types. - -### Scalar Types - -A _scalar_ type represents a single value. Rust has four primary scalar types: -integers, floating-point numbers, Booleans, and characters. You may recognize -these from other programming languages. Let’s jump into how they work in Rust. - -#### Integer Types - -An _integer_ is a number without a fractional component. We used one integer -type in Chapter 2, the `u32` type. This type declaration indicates that the -value it’s associated with should be an unsigned integer (signed integer types -start with `i` instead of `u`) that takes up 32 bits of space. Table 3-1 shows -the built-in integer types in Rust. We can use any of these variants to declare -the type of an integer value. - -Table 3-1: Integer Types in Rust - -| Length | Signed | Unsigned | -| ------- | ------- | -------- | -| 8-bit | `i8` | `u8` | -| 16-bit | `i16` | `u16` | -| 32-bit | `i32` | `u32` | -| 64-bit | `i64` | `u64` | -| 128-bit | `i128` | `u128` | -| architecture dependent | `isize` | `usize` | - -Each variant can be either signed or unsigned and has an explicit size. -_Signed_ and _unsigned_ refer to whether it’s possible for the number to be -negative—in other words, whether the number needs to have a sign with it -(signed) or whether it will only ever be positive and can therefore be -represented without a sign (unsigned). It’s like writing numbers on paper: when -the sign matters, a number is shown with a plus sign or a minus sign; however, -when it’s safe to assume the number is positive, it’s shown with no sign. -Signed numbers are stored using [two’s complement][twos-complement] representation. - -Each signed variant can store numbers from −(2n − 1) to 2n − -1 − 1 inclusive, where _n_ is the number of bits that variant uses. So an -`i8` can store numbers from −(27) to 27 − 1, which equals -−128 to 127. Unsigned variants can store numbers from 0 to 2n − 1, -so a `u8` can store numbers from 0 to 28 − 1, which equals 0 to 255. - -Additionally, the `isize` and `usize` types depend on the architecture of the -computer your program is running on: 64 bits if you’re on a 64-bit architecture -and 32 bits if you’re on a 32-bit architecture. - -You can write integer literals in any of the forms shown in Table 3-2. Note -that number literals that can be multiple numeric types allow a type suffix, -such as `57u8`, to designate the type. Number literals can also use `_` as a -visual separator to make the number easier to read, such as `1_000`, which will -have the same value as if you had specified `1000`. - -Table 3-2: Integer Literals in Rust - -| Number literals | Example | -| ---------------- | ------------- | -| Decimal | `98_222` | -| Hex | `0xff` | -| Octal | `0o77` | -| Binary | `0b1111_0000` | -| Byte (`u8` only) | `b'A'` | - -So how do you know which type of integer to use? If you’re unsure, Rust’s -defaults are generally good places to start: integer types default to `i32`. -The primary situation in which you’d use `isize` or `usize` is when indexing -some sort of collection. - -> ##### Integer Overflow +شما تعریف‌های نوع مختلفی برای انواع داده‌های دیگر خواهید دید. + +### انواع ساده + +یک نوع _ساده_ نمایانگر یک مقدار واحد است. راست چهار نوع ساده اصلی دارد: اعداد صحیح، اعداد اعشاری، بولین‌ها و کاراکترها. ممکن است این‌ها را از زبان‌های برنامه‌نویسی دیگر بشناسید. بیایید ببینیم چگونه در راست کار می‌کنند. + +#### انواع اعداد صحیح + +یک _عدد صحیح_ عددی بدون جزء اعشاری است. ما در فصل 2 از یک نوع عدد صحیح به نام `u32` استفاده کردیم. این تعریف نوع نشان می‌دهد که مقدار مرتبط باید یک عدد صحیح بدون علامت (انواع اعداد صحیح با علامت با `i` به جای `u` شروع می‌شوند) باشد که 32 بیت فضا اشغال می‌کند. جدول 3-1 انواع اعداد صحیح ساخته شده در راست را نشان می‌دهد. ما می‌توانیم از هر یک از این حالت‌ها برای تعریف نوع یک مقدار عدد صحیح استفاده کنیم. + +جدول 3-1: انواع اعداد صحیح در راست + +| طول | علامت‌دار | بدون‌علامت | +| ---------------- | --------- | ---------- | +| ۸-بیتی | `i8` | `u8` | +| ۱۶-بیتی | `i16` | `u16` | +| ۳۲-بیتی | `i32` | `u32` | +| ۶۴-بیتی | `i64` | `u64` | +| ۱۲۸-بیتی | `i128` | `u128` | +| وابسته به معماری | `isize` | `usize` | + +هر حالت می‌تواند یا با علامت یا بدون علامت باشد و اندازه صریحی دارد. _با علامت_ و _بدون علامت_ به این اشاره دارند که آیا ممکن است عدد منفی باشد یا خیر؛ به عبارت دیگر، آیا عدد نیاز به علامت دارد (با علامت) یا اینکه فقط مثبت خواهد بود و بنابراین می‌توان آن را بدون علامت نشان داد (بدون علامت). این شبیه به نوشتن اعداد روی کاغذ است: وقتی علامت مهم باشد، عدد با علامت مثبت یا منفی نشان داده می‌شود؛ اما وقتی فرض مثبت بودن عدد ایمن باشد، بدون علامت نشان داده می‌شود. اعداد با علامت با استفاده از نمایش [دو مکمل][twos-complement] ذخیره می‌شوند. + +هر نوع عدد صحیح علامت‌دار می‌تواند مقادیری از +−(2n − 1) تا 2n − 1 − 1 را +در بر بگیرد، که در آن *n* تعداد بیت‌های استفاده‌شده +توسط آن نوع است. بنابراین، یک `i8` می‌تواند +مقادیر بین −(27) تا 27 − 1 +را نگه دارد، یعنی از −۱۲۸ تا ۱۲۷. + +انواع بدون‌علامت (unsigned) می‌توانند مقادیر +بین ۰ تا 2n − 1 را نگهداری کنند؛ +مثلاً یک `u8` می‌تواند مقادیری از ۰ تا +28 − 1، یعنی از ۰ تا ۲۵۵ را ذخیره کند. + +علاوه بر این، نوع‌های `isize` و `usize` به +معماری سیستمی بستگی دارند که برنامه روی آن +اجرا می‌شود: اگر معماری ۶۴ بیتی باشد، این نوع‌ها +۶۴ بیتی هستند، و اگر ۳۲ بیتی باشد، ۳۲ بیتی خواهند بود. + +شما می‌توانید اعداد صحیح را به هر یک از اشکال نشان داده شده در جدول 3-2 بنویسید. توجه داشته باشید که عددهایی که می‌توانند به چندین نوع عددی تبدیل شوند، یک پسوند نوع دارند، مانند `57u8`، برای تعیین نوع. اعداد همچنین می‌توانند از `_` به عنوان جداکننده بصری برای خواناتر کردن استفاده کنند، مانند `1_000`، که همان مقدار `1000` را دارد. + +جدول 3-2: نمایش اعداد صحیح در راست + +| نوع اعداد | مثال | +| -------------- | ---------- | +| دهدهی | `98_222` | +| هگزادسیمال | `0xff` | +| اکتال | `0o77` | +| باینری | `0b1111_0000` | +| بایت (فقط `u8`) | `b'A'` | + +حال چگونه می‌دانید که از کدام نوع عدد صحیح استفاده کنید؟ اگر مطمئن نیستید، مقادیر پیش‌فرض راست معمولاً مکان خوبی برای شروع هستند: نوع‌های عدد صحیح پیش‌فرض به `i32` تبدیل می‌شوند. وضعیت اصلی که در آن ممکن است از `isize` یا `usize` استفاده کنید زمانی است که می‌خواهید به یک نوع مجموعه اشاره کنید. + +> ##### سرریز عدد صحیح > -> Let’s say you have a variable of type `u8` that can hold values between 0 and -> 255. If you try to change the variable to a value outside that range, such as -> 256, _integer overflow_ will occur, which can result in one of two behaviors. -> When you’re compiling in debug mode, Rust includes checks for integer overflow -> that cause your program to _panic_ at runtime if this behavior occurs. Rust -> uses the term _panicking_ when a program exits with an error; we’ll discuss -> panics in more depth in the [“Unrecoverable Errors with -> `panic!`”][unrecoverable-errors-with-panic] section in Chapter -> 9. +> فرض کنید یک متغیر از نوع `u8` دارید که می‌تواند مقادیر بین 0 و 255 را نگه دارد. اگر تلاش کنید مقدار متغیر را به عددی خارج از این بازه، مانند 256، تغییر دهید، _سرریز عدد صحیح_ رخ خواهد داد که می‌تواند منجر به یکی از دو رفتار شود. وقتی برنامه خود را در حالت دیباگ کامپایل می‌کنید، راست شامل بررسی‌هایی برای سرریز عدد صحیح است که باعث می‌شود برنامه شما در زمان اجرا _پانیک_ کند اگر این رفتار رخ دهد. راست از اصطلاح _پانیک کردن_ زمانی استفاده می‌کند که برنامه با یک خطا خارج شود؛ ما در بخش [“خطاهای غیرقابل بازیابی با `panic!`”][unrecoverable-errors-with-panic] در فصل 9 به طور عمیق‌تر درباره پانیک‌ها بحث خواهیم کرد. > -> When you’re compiling in release mode with the `--release` flag, Rust does -> _not_ include checks for integer overflow that cause panics. Instead, if -> overflow occurs, Rust performs _two’s complement wrapping_. In short, values -> greater than the maximum value the type can hold “wrap around” to the minimum -> of the values the type can hold. In the case of a `u8`, the value 256 becomes -> 0, the value 257 becomes 1, and so on. The program won’t panic, but the -> variable will have a value that probably isn’t what you were expecting it to -> have. Relying on integer overflow’s wrapping behavior is considered an error. +> وقتی برنامه خود را در حالت انتشار با پرچم `--release` کامپایل می‌کنید، راست این بررسی‌ها را برای سرریز عدد صحیح شامل نمی‌شود. در عوض، اگر سرریز رخ دهد، راست از _دو مکمل بسته‌بندی_ استفاده می‌کند. به طور خلاصه، مقادیر بزرگتر از حداکثر مقداری که نوع می‌تواند نگه دارد به "حداقل مقادیر" بازه نوع بسته‌بندی می‌شوند. در مورد یک `u8`، مقدار 256 به 0 تبدیل می‌شود، مقدار 257 به 1 و غیره. برنامه پانیک نخواهد کرد، اما متغیر مقدار متفاوتی نسبت به آنچه انتظار می‌رفت خواهد داشت. اعتماد به رفتار بسته‌بندی سرریز عدد صحیح یک خطا محسوب می‌شود. > -> To explicitly handle the possibility of overflow, you can use these families -> of methods provided by the standard library for primitive numeric types: +> برای مدیریت صریح امکان سرریز، می‌توانید از این خانواده‌های روش‌ها استفاده کنید که توسط کتابخانه استاندارد برای نوع‌های عددی اولیه ارائه شده‌اند: > -> - Wrap in all modes with the `wrapping_*` methods, such as `wrapping_add`. -> - Return the `None` value if there is overflow with the `checked_*` methods. -> - Return the value and a Boolean indicating whether there was overflow with -> the `overflowing_*` methods. -> - Saturate at the value’s minimum or maximum values with the `saturating_*` -> methods. +> - در همه حالت‌ها با استفاده از متدهای `wrapping_*` مانند `wrapping_add`، مقدار را wrap می‌کند. +> - در صورت بروز overflow، مقدار `None` را با متدهای `checked_*` بازمی‌گرداند. +> - مقدار و یک مقدار Boolean که نشان می‌دهد overflow رخ داده یا نه، با متدهای `overflowing_*` بازمی‌گردد. +> - در مقدار حداقل یا حداکثر نوع متوقف می‌شود (saturate) با استفاده از متدهای `saturating_*`. -#### Floating-Point Types -Rust also has two primitive types for _floating-point numbers_, which are -numbers with decimal points. Rust’s floating-point types are `f32` and `f64`, -which are 32 bits and 64 bits in size, respectively. The default type is `f64` -because on modern CPUs, it’s roughly the same speed as `f32` but is capable of -more precision. All floating-point types are signed. +#### انواع اعداد اعشاری -Here’s an example that shows floating-point numbers in action: +راست همچنین دو نوع اولیه برای _اعداد اعشاری_ دارد، که اعدادی با نقطه اعشار هستند. نوع‌های اعشاری راست `f32` و `f64` هستند که به ترتیب 32 بیت و 64 بیت اندازه دارند. نوع پیش‌فرض `f64` است زیرا روی CPUهای مدرن، سرعت آن تقریباً مشابه `f32` است اما دقت بیشتری دارد. همه نوع‌های اعشاری علامت‌دار هستند. + +در اینجا مثالی که اعداد اعشاری را در عمل نشان می‌دهد آورده شده است: Filename: src/main.rs @@ -140,14 +94,11 @@ Here’s an example that shows floating-point numbers in action: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-06-floating-point/src/main.rs}} ``` -Floating-point numbers are represented according to the IEEE-754 standard. +اعداد اعشاری طبق استاندارد IEEE-754 نمایش داده می‌شوند. -#### Numeric Operations +#### عملیات عددی -Rust supports the basic mathematical operations you’d expect for all the number -types: addition, subtraction, multiplication, division, and remainder. Integer -division truncates toward zero to the nearest integer. The following code shows -how you’d use each numeric operation in a `let` statement: +راست از عملیات ریاضی پایه‌ای که برای تمام انواع عددی انتظار دارید پشتیبانی می‌کند: جمع، تفریق، ضرب، تقسیم و باقی‌مانده. تقسیم اعداد صحیح به نزدیک‌ترین عدد صحیح به سمت صفر گرد می‌شود. کد زیر نشان می‌دهد چگونه می‌توانید از هر عملیات عددی در یک عبارت `let` استفاده کنید: Filename: src/main.rs @@ -155,16 +106,11 @@ how you’d use each numeric operation in a `let` statement: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-07-numeric-operations/src/main.rs}} ``` -Each expression in these statements uses a mathematical operator and evaluates -to a single value, which is then bound to a variable. [Appendix -B][appendix_b] contains a list of all operators that Rust -provides. +هر عبارت در این دستورات از یک عملگر ریاضی استفاده می‌کند و به یک مقدار واحد ارزیابی می‌شود، که سپس به یک متغیر متصل می‌شود. [ضمیمه ب][appendix_b] شامل لیستی از تمام عملگرهایی است که راست فراهم می‌کند. -#### The Boolean Type +#### نوع بولین -As in most other programming languages, a Boolean type in Rust has two possible -values: `true` and `false`. Booleans are one byte in size. The Boolean type in -Rust is specified using `bool`. For example: +مانند اکثر زبان‌های برنامه‌نویسی دیگر، نوع بولین در راست دو مقدار ممکن دارد: `true` و `false`. نوع بولین در راست یک بایت اندازه دارد. نوع بولین در راست با استفاده از `bool` مشخص می‌شود. برای مثال: Filename: src/main.rs @@ -172,14 +118,11 @@ Rust is specified using `bool`. For example: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-08-boolean/src/main.rs}} ``` -The main way to use Boolean values is through conditionals, such as an `if` -expression. We’ll cover how `if` expressions work in Rust in the [“Control -Flow”][control-flow] section. +راه اصلی استفاده از مقادیر بولین از طریق عبارات شرطی، مانند عبارت `if` است. ما در بخش [“جریان کنترل”][control-flow] توضیح می‌دهیم که چگونه عبارات `if` در راست کار می‌کنند. -#### The Character Type +#### نوع کاراکتر -Rust’s `char` type is the language’s most primitive alphabetic type. Here are -some examples of declaring `char` values: +نوع `char` در راست ابتدایی‌ترین نوع الفبایی زبان است. در اینجا برخی از مثال‌های اعلام مقادیر `char` آورده شده است: Filename: src/main.rs @@ -187,32 +130,32 @@ some examples of declaring `char` values: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-09-char/src/main.rs}} ``` -Note that we specify `char` literals with single quotes, as opposed to string -literals, which use double quotes. Rust’s `char` type is four bytes in size and -represents a Unicode scalar value, which means it can represent a lot more than -just ASCII. Accented letters; Chinese, Japanese, and Korean characters; emoji; -and zero-width spaces are all valid `char` values in Rust. Unicode scalar -values range from `U+0000` to `U+D7FF` and `U+E000` to `U+10FFFF` inclusive. -However, a “character” isn’t really a concept in Unicode, so your human -intuition for what a “character” is may not match up with what a `char` is in -Rust. We’ll discuss this topic in detail in [“Storing UTF-8 Encoded Text with -Strings”][strings] in Chapter 8. +توجه داشته باشید که literals نوع `char` با +نقل‌قول‌های تکی مشخص می‌شوند، در حالی که literals +رشته‌ای (string) از نقل‌قول‌های دوتایی استفاده می‌کنند. +نوع `char` در Rust اندازه‌ای برابر با چهار بایت +دارد و نمایانگر یک مقدار اسکالر یونیکد است، به این +معنا که می‌تواند بسیار بیشتر از فقط کاراکترهای +ASCII را نمایش دهد. حروف دارای اعراب، کاراکترهای +چینی، ژاپنی و کره‌ای، ایموجی‌ها و فضاهای بدون عرض +همگی مقادیر معتبر `char` در Rust هستند. مقدارهای +اسکالر یونیکد در بازه‌ی `U+0000` تا `U+D7FF` و +`U+E000` تا `U+10FFFF` شامل می‌شوند. با این حال، +مفهوم “کاراکتر” در یونیکد واقعاً وجود ندارد، +بنابراین تصور انسانی شما از “کاراکتر” ممکن است با +آنچه `char` در Rust است تفاوت داشته باشد. این موضوع +را در بخش [«ذخیره متن کدگذاری‌شده UTF-8 با رشته‌ها»][strings] +در فصل ۸ به‌طور مفصل بررسی خواهیم کرد. -### Compound Types +### انواع ترکیبی -_Compound types_ can group multiple values into one type. Rust has two -primitive compound types: tuples and arrays. +_انواع ترکیبی_ می‌توانند چندین مقدار را در یک نوع گروه‌بندی کنند. راست دو نوع ترکیبی اولیه دارد: تاپل‌ها و آرایه‌ها. -#### The Tuple Type +#### نوع تاپل -A _tuple_ is a general way of grouping together a number of values with a -variety of types into one compound type. Tuples have a fixed length: once -declared, they cannot grow or shrink in size. +_تاپل_ یک روش کلی برای گروه‌بندی چند مقدار با انواع مختلف در یک نوع ترکیبی است. تاپل‌ها طول ثابتی دارند: پس از اعلام، نمی‌توانند بزرگ‌تر یا کوچک‌تر شوند. -We create a tuple by writing a comma-separated list of values inside -parentheses. Each position in the tuple has a type, and the types of the -different values in the tuple don’t have to be the same. We’ve added optional -type annotations in this example: +ما یک تاپل را با نوشتن یک لیست جدا شده با کاما از مقادیر در داخل پرانتز ایجاد می‌کنیم. هر موقعیت در تاپل یک نوع دارد، و انواع مقادیر مختلف در تاپل نیازی به یکسان بودن ندارند. ما در این مثال حاشیه‌نویسی نوع اختیاری اضافه کرده‌ایم: Filename: src/main.rs @@ -220,9 +163,7 @@ type annotations in this example: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-10-tuples/src/main.rs}} ``` -The variable `tup` binds to the entire tuple because a tuple is considered a -single compound element. To get the individual values out of a tuple, we can -use pattern matching to destructure a tuple value, like this: +متغیر `tup` به کل تاپل متصل می‌شود زیرا یک تاپل به عنوان یک عنصر ترکیبی واحد در نظر گرفته می‌شود. برای استخراج مقادیر جداگانه از یک تاپل، می‌توانیم از تطابق الگو برای تجزیه مقدار تاپل استفاده کنیم، مانند این: Filename: src/main.rs @@ -230,14 +171,9 @@ use pattern matching to destructure a tuple value, like this: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-11-destructuring-tuples/src/main.rs}} ``` -This program first creates a tuple and binds it to the variable `tup`. It then -uses a pattern with `let` to take `tup` and turn it into three separate -variables, `x`, `y`, and `z`. This is called _destructuring_ because it breaks -the single tuple into three parts. Finally, the program prints the value of -`y`, which is `6.4`. +این برنامه ابتدا یک تاپل ایجاد کرده و آن را به متغیر `tup` متصل می‌کند. سپس از یک الگو با `let` برای گرفتن `tup` و تبدیل آن به سه متغیر جداگانه، `x`، `y`، و `z` استفاده می‌کند. این فرآیند _تجزیه_ نامیده می‌شود زیرا تاپل واحد را به سه قسمت تقسیم می‌کند. در نهایت، برنامه مقدار `y` را که `6.4` است، چاپ می‌کند. -We can also access a tuple element directly by using a period (`.`) followed by -the index of the value we want to access. For example: +ما همچنین می‌توانیم یک عنصر از تاپل را مستقیماً با استفاده از یک نقطه (`.`) به دنبال شماره شاخص مقدار مورد نظر دسترسی داشته باشیم. برای مثال: Filename: src/main.rs @@ -245,23 +181,15 @@ the index of the value we want to access. For example: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-12-tuple-indexing/src/main.rs}} ``` -This program creates the tuple `x` and then accesses each element of the tuple -using their respective indices. As with most programming languages, the first -index in a tuple is 0. +این برنامه تاپل `x` را ایجاد کرده و سپس به هر عنصر تاپل با استفاده از شاخص‌های مربوطه آنها دسترسی پیدا می‌کند. همانند اکثر زبان‌های برنامه‌نویسی، اولین شاخص در یک تاپل `0` است. -The tuple without any values has a special name, _unit_. This value and its -corresponding type are both written `()` and represent an empty value or an -empty return type. Expressions implicitly return the unit value if they don’t -return any other value. +تاپل بدون هیچ مقداری یک نام خاص دارد، _واحد_. این مقدار و نوع مربوط به آن هر دو با `()` نوشته می‌شوند و یک مقدار خالی یا یک نوع بازگشت خالی را نشان می‌دهند. عبارات به طور ضمنی مقدار واحد را بازمی‌گردانند اگر هیچ مقدار دیگری بازنگردانند. -#### The Array Type +#### نوع آرایه -Another way to have a collection of multiple values is with an _array_. Unlike -a tuple, every element of an array must have the same type. Unlike arrays in -some other languages, arrays in Rust have a fixed length. +روش دیگری برای داشتن مجموعه‌ای از چند مقدار، استفاده از _آرایه_ است. برخلاف تاپل، هر عنصر آرایه باید از یک نوع باشد. برخلاف آرایه‌ها در برخی زبان‌های دیگر، آرایه‌ها در راست طول ثابتی دارند. -We write the values in an array as a comma-separated list inside square -brackets: +ما مقادیر یک آرایه را به صورت یک لیست جدا شده با کاما در داخل کروشه می‌نویسیم: Filename: src/main.rs @@ -269,53 +197,45 @@ brackets: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-13-arrays/src/main.rs}} ``` -Arrays are useful when you want your data allocated on the stack, the same as -the other types we have seen so far, rather than the heap (we will discuss the -stack and the heap more in [Chapter 4][stack-and-heap]) or when -you want to ensure you always have a fixed number of elements. An array isn’t -as flexible as the vector type, though. A _vector_ is a similar collection type -provided by the standard library that _is_ allowed to grow or shrink in size -because its contents live on the heap. If you’re unsure whether to use an array -or a vector, chances are you should use a vector. [Chapter 8][vectors] discusses vectors in more detail. - -However, arrays are more useful when you know the number of elements will not -need to change. For example, if you were using the names of the month in a -program, you would probably use an array rather than a vector because you know -it will always contain 12 elements: +آرایه‌ها زمانی کاربردی هستند که بخواهید داده‌هایتان +روی stack تخصیص یابند، مشابه سایر نوع‌هایی که تاکنون +دیدیم، نه روی heap (که در [فصل ۴][stack-and-heap] بیشتر +درباره‌ی stack و heap صحبت خواهیم کرد) یا زمانی که +می‌خواهید همیشه تعداد ثابتی از عناصر داشته باشید. +با این حال، آرایه به اندازه‌ی نوع vector انعطاف‌پذیر +نیست. *وکتور* نوعی مجموعه مشابه است که توسط کتابخانه +استاندارد ارائه شده و اجازه دارد اندازه‌اش تغییر کند، +چون محتوای آن روی heap ذخیره می‌شود. اگر مطمئن نیستید +که از آرایه استفاده کنید یا وکتور، احتمالاً بهتر است +وکتور را انتخاب کنید. [فصل ۸][vectors] به‌طور دقیق‌تر +درباره‌ی وکتورها بحث می‌کند. + +با این حال، آرایه‌ها زمانی مفیدتر هستند که بدانید تعداد عناصر نیاز به تغییر ندارد. برای مثال، اگر از نام‌های ماه در یک برنامه استفاده می‌کردید، احتمالاً از یک آرایه به جای یک وکتور استفاده می‌کردید زیرا می‌دانید همیشه ۱۲ عنصر خواهد داشت: ```rust let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; ``` -You write an array’s type using square brackets with the type of each element, -a semicolon, and then the number of elements in the array, like so: +شما نوع یک آرایه را با استفاده از کروشه‌ها به همراه نوع هر عنصر، یک نقطه ویرگول، و سپس تعداد عناصر در آرایه می‌نویسید، مانند این: ```rust let a: [i32; 5] = [1, 2, 3, 4, 5]; ``` -Here, `i32` is the type of each element. After the semicolon, the number `5` -indicates the array contains five elements. +در اینجا، `i32` نوع هر عنصر است. پس از نقطه ویرگول، عدد `۵` نشان می‌دهد که آرایه شامل پنج عنصر است. -You can also initialize an array to contain the same value for each element by -specifying the initial value, followed by a semicolon, and then the length of -the array in square brackets, as shown here: +شما همچنین می‌توانید یک آرایه را طوری مقداردهی اولیه کنید که هر عنصر مقدار یکسانی داشته باشد، با مشخص کردن مقدار اولیه، یک نقطه ویرگول، و سپس طول آرایه در کروشه‌ها، مانند این: ```rust let a = [3; 5]; ``` -The array named `a` will contain `5` elements that will all be set to the value -`3` initially. This is the same as writing `let a = [3, 3, 3, 3, 3];` but in a -more concise way. +آرایه‌ای با نام `a` شامل `۵` عنصر خواهد بود که همه ابتدا مقدار `۳` دارند. این همان نوشتن `let a = [3, 3, 3, 3, 3];` است، اما به شیوه‌ای مختصرتر. -##### Accessing Array Elements +##### دسترسی به عناصر آرایه -An array is a single chunk of memory of a known, fixed size that can be -allocated on the stack. You can access elements of an array using indexing, -like this: +یک آرایه یک بخش واحد از حافظه با اندازه‌ای مشخص و ثابت است که می‌تواند روی استک تخصیص داده شود. شما می‌توانید به عناصر یک آرایه با استفاده از ایندکس دسترسی پیدا کنید، مانند این: Filename: src/main.rs @@ -323,15 +243,11 @@ like this: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-14-array-indexing/src/main.rs}} ``` -In this example, the variable named `first` will get the value `1` because that -is the value at index `[0]` in the array. The variable named `second` will get -the value `2` from index `[1]` in the array. +در این مثال، متغیری با نام `first` مقدار `1` را می‌گیرد زیرا این مقدار در ایندکس `[0]` در آرایه قرار دارد. متغیری با نام `second` مقدار `2` را از ایندکس `[1]` در آرایه می‌گیرد. -##### Invalid Array Element Access +##### دسترسی نامعتبر به عنصر آرایه -Let’s see what happens if you try to access an element of an array that is past -the end of the array. Say you run this code, similar to the guessing game in -Chapter 2, to get an array index from the user: +ببینیم چه اتفاقی می‌افتد اگر بخواهید به عنصری از آرایه دسترسی پیدا کنید که خارج از محدوده آرایه است. فرض کنید این کد را اجرا کنید که مشابه بازی حدس در فصل ۲ است، تا یک ایندکس آرایه را از کاربر دریافت کند: Filename: src/main.rs @@ -339,10 +255,7 @@ Chapter 2, to get an array index from the user: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs}} ``` -This code compiles successfully. If you run this code using `cargo run` and -enter `0`, `1`, `2`, `3`, or `4`, the program will print out the corresponding -value at that index in the array. If you instead enter a number past the end of -the array, such as `10`, you’ll see output like this: +این کد به درستی کامپایل می‌شود. اگر این کد را با استفاده از `cargo run` اجرا کنید و مقادیری مانند `0`، `1`، `2`، `3` یا `4` را وارد کنید، برنامه مقدار متناظر در آن ایندکس از آرایه را چاپ می‌کند. اما اگر به جای آن عددی خارج از محدوده آرایه، مانند `10`، وارد کنید، خروجی چیزی شبیه به این خواهد بود: -section of Chapter 14. +راست همچنین نوع دیگری از کامنت‌ها، کامنت‌های مستندات (documentation comments) دارد که آنها را در بخش [“انتشار یک کرات در Crates.io”][publishing] از فصل 14 بررسی خواهیم کرد. [publishing]: ch14-02-publishing-to-crates-io.html diff --git a/src/ch03-05-control-flow.md b/src/ch03-05-control-flow.md index ee80626d1f..15bd7707e3 100644 --- a/src/ch03-05-control-flow.md +++ b/src/ch03-05-control-flow.md @@ -1,18 +1,12 @@ -## Control Flow +## کنترل جریان -The ability to run some code depending on whether a condition is `true` and to -run some code repeatedly while a condition is `true` are basic building blocks -in most programming languages. The most common constructs that let you control -the flow of execution of Rust code are `if` expressions and loops. +توانایی اجرای کدی که وابسته به درست بودن یا نبودن یک شرط است و اجرای مکرر کدی در حالی که یک شرط درست است، از ساختارهای اساسی در بیشتر زبان‌های برنامه‌نویسی محسوب می‌شود. رایج‌ترین ساختارهایی که به شما امکان کنترل جریان اجرای کد در راست را می‌دهند، عبارتند از عبارات `if` و حلقه‌ها. -### `if` Expressions +### عبارات `if` -An `if` expression allows you to branch your code depending on conditions. You -provide a condition and then state, “If this condition is met, run this block -of code. If the condition is not met, do not run this block of code.” +یک عبارت `if` به شما امکان می‌دهد کد خود را بسته به شرایطی شاخه‌بندی کنید. شما یک شرط مشخص می‌کنید و سپس می‌گویید: «اگر این شرط برقرار بود، این بلوک کد اجرا شود. اگر شرط برقرار نبود، این بلوک کد اجرا نشود.» -Create a new project called _branches_ in your _projects_ directory to explore -the `if` expression. In the _src/main.rs_ file, input the following: +یک پروژه جدید به نام _branches_ در دایرکتوری _projects_ خود ایجاد کنید تا عبارت `if` را بررسی کنید. در فایل _src/main.rs_ کد زیر را وارد کنید: Filename: src/main.rs @@ -20,43 +14,29 @@ the `if` expression. In the _src/main.rs_ file, input the following: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-26-if-true/src/main.rs}} ``` -All `if` expressions start with the keyword `if`, followed by a condition. In -this case, the condition checks whether or not the variable `number` has a -value less than 5. We place the block of code to execute if the condition is -`true` immediately after the condition inside curly brackets. Blocks of code -associated with the conditions in `if` expressions are sometimes called _arms_, -just like the arms in `match` expressions that we discussed in the [“Comparing -the Guess to the Secret Number”][comparing-the-guess-to-the-secret-number] section of Chapter 2. +تمام عبارات `if` با کلمه کلیدی `if` شروع می‌شوند و سپس یک شرط دنبال می‌شود. در این مثال، شرط بررسی می‌کند که آیا مقدار متغیر `number` کمتر از 5 است یا خیر. بلوک کدی که در صورت درست بودن شرط باید اجرا شود، بلافاصله بعد از شرط و داخل کروشه‌ها قرار می‌گیرد. بلوک‌های کدی که با شرایط در عبارات `if` مرتبط هستند، گاهی _بازو_ (arm) نامیده می‌شوند، همانند بازوهای موجود در عبارات `match` که در بخش [“مقایسه حدس با عدد مخفی”][comparing-the-guess-to-the-secret-number] از فصل 2 مورد بحث قرار گرفت. -Optionally, we can also include an `else` expression, which we chose to do -here, to give the program an alternative block of code to execute should the -condition evaluate to `false`. If you don’t provide an `else` expression and -the condition is `false`, the program will just skip the `if` block and move on -to the next bit of code. +به‌صورت اختیاری، می‌توانیم یک عبارت `else` نیز اضافه کنیم، همان‌طور که اینجا انتخاب کردیم، تا به برنامه یک بلوک کد جایگزین برای اجرا ارائه دهیم، در صورتی که شرط به `false` ارزیابی شود. اگر عبارت `else` ارائه ندهید و شرط `false` باشد، برنامه بلوک `if` را نادیده گرفته و به بخش بعدی کد می‌رود. -Try running this code; you should see the following output: +این کد را اجرا کنید؛ باید خروجی زیر را مشاهده کنید: ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-26-if-true/output.txt}} ``` -Let’s try changing the value of `number` to a value that makes the condition -`false` to see what happens: +بیایید مقدار `number` را به مقداری تغییر دهیم که شرط `false` شود تا ببینیم چه اتفاقی می‌افتد: ```rust,ignore {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-27-if-false/src/main.rs:here}} ``` -Run the program again, and look at the output: +برنامه را دوباره اجرا کنید و خروجی را مشاهده کنید: ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-27-if-false/output.txt}} ``` -It’s also worth noting that the condition in this code _must_ be a `bool`. If -the condition isn’t a `bool`, we’ll get an error. For example, try running the -following code: +همچنین قابل توجه است که شرط در این کد _باید_ یک `bool` باشد. اگر شرط یک `bool` نباشد، خطا دریافت خواهیم کرد. به عنوان مثال، این کد را اجرا کنید: Filename: src/main.rs @@ -64,19 +44,13 @@ following code: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-28-if-condition-must-be-bool/src/main.rs}} ``` -The `if` condition evaluates to a value of `3` this time, and Rust throws an -error: +این بار شرط `if` به مقدار `3` ارزیابی می‌شود و راست خطا می‌دهد: ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-28-if-condition-must-be-bool/output.txt}} ``` -The error indicates that Rust expected a `bool` but got an integer. Unlike -languages such as Ruby and JavaScript, Rust will not automatically try to -convert non-Boolean types to a Boolean. You must be explicit and always provide -`if` with a Boolean as its condition. If we want the `if` code block to run -only when a number is not equal to `0`, for example, we can change the `if` -expression to the following: +خطا نشان می‌دهد که راست انتظار یک `bool` داشت اما یک عدد صحیح دریافت کرد. برخلاف زبان‌هایی مانند Ruby و JavaScript، راست به‌صورت خودکار تلاش نمی‌کند انواع غیر `bool` را به یک `bool` تبدیل کند. شما باید صریح باشید و همیشه یک `bool` را به‌عنوان شرط به `if` بدهید. اگر می‌خواهید بلوک کد `if` فقط زمانی اجرا شود که یک عدد برابر `0` نباشد، می‌توانید عبارت `if` را به این صورت تغییر دهید: Filename: src/main.rs @@ -84,12 +58,11 @@ expression to the following: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-29-if-not-equal-0/src/main.rs}} ``` -Running this code will print `number was something other than zero`. +اجرای این کد `number was something other than zero` را چاپ خواهد کرد. -#### Handling Multiple Conditions with `else if` +#### مدیریت شرایط متعدد با `else if` -You can use multiple conditions by combining `if` and `else` in an `else if` -expression. For example: +شما می‌توانید با ترکیب `if` و `else` در یک عبارت `else if`، شرایط متعددی را مدیریت کنید. به عنوان مثال: Filename: src/main.rs @@ -97,30 +70,21 @@ expression. For example: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-30-else-if/src/main.rs}} ``` -This program has four possible paths it can take. After running it, you should -see the following output: +این برنامه چهار مسیر ممکن برای اجرا دارد. پس از اجرای آن، باید خروجی زیر را مشاهده کنید: ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-30-else-if/output.txt}} ``` -When this program executes, it checks each `if` expression in turn and executes -the first body for which the condition evaluates to `true`. Note that even -though 6 is divisible by 2, we don’t see the output `number is divisible by 2`, -nor do we see the `number is not divisible by 4, 3, or 2` text from the `else` -block. That’s because Rust only executes the block for the first `true` -condition, and once it finds one, it doesn’t even check the rest. +هنگامی که این برنامه اجرا می‌شود، هر عبارت `if` را به ترتیب بررسی کرده و اولین بلوکی که شرط آن به `true` ارزیابی شود، اجرا می‌کند. توجه داشته باشید که حتی با وجود اینکه 6 بر 2 بخش‌پذیر است، خروجی `number is divisible by 2` را نمی‌بینیم و همچنین متن `number is not divisible by 4, 3, or 2` از بلوک `else` را نیز نمی‌بینیم. این به این دلیل است که راست فقط بلوک مربوط به اولین شرط درست را اجرا می‌کند و پس از یافتن آن، بقیه را بررسی نمی‌کند. -Using too many `else if` expressions can clutter your code, so if you have more -than one, you might want to refactor your code. Chapter 6 describes a powerful -Rust branching construct called `match` for these cases. +استفاده از تعداد زیادی عبارت `else if` می‌تواند کد شما را شلوغ کند، بنابراین اگر بیش از یک مورد دارید، ممکن است بخواهید کد خود را بازنویسی کنید. فصل 6 یک ساختار شاخه‌بندی قدرتمند در راست به نام `match` را برای این موارد توضیح می‌دهد. -#### Using `if` in a `let` Statement +#### استفاده از `if` در یک عبارت `let` -Because `if` is an expression, we can use it on the right side of a `let` -statement to assign the outcome to a variable, as in Listing 3-2. +از آنجایی که `if` یک عبارت است، می‌توانیم از آن در سمت راست یک عبارت `let` برای تخصیص نتیجه به یک متغیر استفاده کنیم، همان‌طور که در لیست 3-2 نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-02/src/main.rs}} @@ -128,20 +92,13 @@ statement to assign the outcome to a variable, as in Listing 3-2. -The `number` variable will be bound to a value based on the outcome of the `if` -expression. Run this code to see what happens: +متغیر `number` به مقداری بر اساس نتیجه عبارت `if` متصل خواهد شد. این کد را اجرا کنید تا ببینید چه اتفاقی می‌افتد: ```console {{#include ../listings/ch03-common-programming-concepts/listing-03-02/output.txt}} ``` -Remember that blocks of code evaluate to the last expression in them, and -numbers by themselves are also expressions. In this case, the value of the -whole `if` expression depends on which block of code executes. This means the -values that have the potential to be results from each arm of the `if` must be -the same type; in Listing 3-2, the results of both the `if` arm and the `else` -arm were `i32` integers. If the types are mismatched, as in the following -example, we’ll get an error: +به خاطر داشته باشید که بلوک‌های کد به آخرین عبارت در آن‌ها ارزیابی می‌شوند و اعداد به تنهایی نیز عبارات محسوب می‌شوند. در این حالت، مقدار کل عبارت `if` بستگی به این دارد که کدام بلوک کد اجرا شود. این بدان معناست که مقادیری که می‌توانند نتایج هر بازوی `if` باشند، باید از یک نوع باشند. در لیست 3-2، نتایج بازوی `if` و بازوی `else` هر دو اعداد صحیح `i32` بودند. اگر انواع ناسازگار باشند، مانند مثال زیر، خطایی دریافت خواهیم کرد: Filename: src/main.rs @@ -149,39 +106,25 @@ example, we’ll get an error: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-31-arms-must-return-same-type/src/main.rs}} ``` -When we try to compile this code, we’ll get an error. The `if` and `else` arms -have value types that are incompatible, and Rust indicates exactly where to -find the problem in the program: +هنگامی که تلاش می‌کنیم این کد را کامپایل کنیم، خطایی دریافت می‌کنیم. بازوهای `if` و `else` دارای انواع مقداری ناسازگار هستند و راست دقیقاً نشان می‌دهد که مشکل در برنامه کجاست: ```console {{#include ../listings/ch03-common-programming-concepts/no-listing-31-arms-must-return-same-type/output.txt}} ``` -The expression in the `if` block evaluates to an integer, and the expression in -the `else` block evaluates to a string. This won’t work because variables must -have a single type, and Rust needs to know at compile time what type the -`number` variable is, definitively. Knowing the type of `number` lets the -compiler verify the type is valid everywhere we use `number`. Rust wouldn’t be -able to do that if the type of `number` was only determined at runtime; the -compiler would be more complex and would make fewer guarantees about the code -if it had to keep track of multiple hypothetical types for any variable. +عبارت موجود در بلوک `if` به یک عدد صحیح ارزیابی می‌شود و عبارت موجود در بلوک `else` به یک رشته ارزیابی می‌شود. این کار نمی‌کند زیرا متغیرها باید یک نوع مشخص داشته باشند و راست باید در زمان کامپایل بداند که نوع متغیر `number` چیست. دانستن نوع `number` به کامپایلر این امکان را می‌دهد که بررسی کند نوع آن در هر جایی که از `number` استفاده می‌کنیم معتبر است. راست نمی‌توانست این کار را انجام دهد اگر نوع `number` تنها در زمان اجرا مشخص می‌شد. کامپایلر پیچیده‌تر می‌شد و تضمین‌های کمتری درباره کد ارائه می‌داد اگر مجبور بود انواع فرضی مختلفی را برای هر متغیر پیگیری کند. -### Repetition with Loops +### تکرار با حلقه‌ها -It’s often useful to execute a block of code more than once. For this task, -Rust provides several _loops_, which will run through the code inside the loop -body to the end and then start immediately back at the beginning. To experiment -with loops, let’s make a new project called _loops_. +اغلب مفید است که یک بلوک کد بیش از یک بار اجرا شود. برای این کار، Rust چندین _حلقه_ ارائه می‌دهد که کد داخل بدنه حلقه را اجرا کرده و سپس بلافاصله به ابتدای حلقه بازمی‌گردند. برای آزمایش با حلقه‌ها، یک پروژه جدید به نام _loops_ ایجاد کنید. -Rust has three kinds of loops: `loop`, `while`, and `for`. Let’s try each one. +Rust سه نوع حلقه دارد: `loop`، `while` و `for`. بیایید هر کدام را امتحان کنیم. -#### Repeating Code with `loop` +#### تکرار کد با `loop` -The `loop` keyword tells Rust to execute a block of code over and over again -forever or until you explicitly tell it to stop. +کلمه کلیدی `loop` به Rust می‌گوید که یک بلوک کد را بارها و بارها اجرا کند، تا زمانی که شما به طور صریح به آن بگویید متوقف شود. -As an example, change the _src/main.rs_ file in your _loops_ directory to look -like this: +به عنوان مثال، فایل _src/main.rs_ را در دایرکتوری _loops_ خود به شکل زیر تغییر دهید: Filename: src/main.rs @@ -189,10 +132,7 @@ like this: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-loop/src/main.rs}} ``` -When we run this program, we’ll see `again!` printed over and over continuously -until we stop the program manually. Most terminals support the keyboard shortcut -ctrl-c to interrupt a program that is stuck in a continual -loop. Give it a try: +وقتی این برنامه را اجرا کنیم، `again!` بارها و بارها به طور مداوم چاپ می‌شود تا زمانی که برنامه را به صورت دستی متوقف کنیم. اکثر ترمینال‌ها از میانبر صفحه کلید ctrl-c برای متوقف کردن برنامه‌ای که در یک حلقه بی‌پایان گیر کرده است، پشتیبانی می‌کنند. آن را امتحان کنید: section of Chapter 2 to exit the program when the user won the game by -guessing the correct number. +خوشبختانه، Rust همچنین روشی برای خروج از یک حلقه با استفاده از کد ارائه می‌دهد. شما می‌توانید کلمه کلیدی `break` را درون حلقه قرار دهید تا به برنامه بگویید که چه زمانی اجرای حلقه را متوقف کند. به یاد داشته باشید که این کار را در بازی حدس عدد در بخش [“خروج پس از یک حدس درست”][quitting-after-a-correct-guess] در فصل 2 انجام دادیم تا زمانی که کاربر با حدس درست بازی را برنده شد، برنامه خاتمه یابد. -We also used `continue` in the guessing game, which in a loop tells the program -to skip over any remaining code in this iteration of the loop and go to the -next iteration. +ما همچنین از `continue` در بازی حدس عدد استفاده کردیم که در یک حلقه به برنامه می‌گوید هر کد باقی‌مانده در این تکرار حلقه را نادیده بگیرد و به تکرار بعدی برود. -#### Returning Values from Loops +#### بازگرداندن مقادیر از حلقه‌ها -One of the uses of a `loop` is to retry an operation you know might fail, such -as checking whether a thread has completed its job. You might also need to pass -the result of that operation out of the loop to the rest of your code. To do -this, you can add the value you want returned after the `break` expression you -use to stop the loop; that value will be returned out of the loop so you can -use it, as shown here: +یکی از کاربردهای `loop` این است که یک عملیات را که ممکن است شکست بخورد دوباره امتحان کنید، مثلاً بررسی کنید که آیا یک نخ (thread) کار خود را تمام کرده است یا نه. همچنین ممکن است نیاز داشته باشید نتیجه این عملیات را از حلقه به بقیه کد خود منتقل کنید. برای انجام این کار، می‌توانید مقداری که می‌خواهید برگردانده شود را پس از عبارت `break` اضافه کنید. این مقدار از حلقه بازگردانده می‌شود تا بتوانید از آن استفاده کنید، همان‌طور که در اینجا نشان داده شده است: ```rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-33-return-value-from-loop/src/main.rs}} ``` -Before the loop, we declare a variable named `counter` and initialize it to -`0`. Then we declare a variable named `result` to hold the value returned from -the loop. On every iteration of the loop, we add `1` to the `counter` variable, -and then check whether the `counter` is equal to `10`. When it is, we use the -`break` keyword with the value `counter * 2`. After the loop, we use a -semicolon to end the statement that assigns the value to `result`. Finally, we -print the value in `result`, which in this case is `20`. +قبل از حلقه، یک متغیر به نام `counter` اعلام می‌کنیم و مقدار آن را `0` مقداردهی اولیه می‌کنیم. سپس یک متغیر به نام `result` اعلام می‌کنیم تا مقدار بازگشتی از حلقه را نگه دارد. در هر تکرار حلقه، مقدار `1` را به متغیر `counter` اضافه می‌کنیم و سپس بررسی می‌کنیم که آیا مقدار `counter` برابر با `10` است یا نه. زمانی که این شرط برقرار باشد، از کلمه کلیدی `break` با مقدار `counter * 2` استفاده می‌کنیم. پس از حلقه، با استفاده از یک سمی‌کالن، مقدار به `result` تخصیص داده می‌شود. در نهایت، مقدار `result` را چاپ می‌کنیم که در این مثال برابر با `20` است. -You can also `return` from inside a loop. While `break` only exits the current -loop, `return` always exits the current function. +شما همچنین می‌توانید از داخل یک حلقه `return` استفاده کنید. در حالی که `break` فقط از حلقه جاری خارج می‌شود، `return` همیشه از تابع جاری خارج می‌شود. -#### Loop Labels to Disambiguate Between Multiple Loops +#### برچسب حلقه‌ها برای رفع ابهام بین چندین حلقه -If you have loops within loops, `break` and `continue` apply to the innermost -loop at that point. You can optionally specify a _loop label_ on a loop that -you can then use with `break` or `continue` to specify that those keywords -apply to the labeled loop instead of the innermost loop. Loop labels must begin -with a single quote. Here’s an example with two nested loops: +اگر حلقه‌هایی تو در تو داشته باشید، `break` و `continue` به حلقه داخلی‌ترین حلقه در آن نقطه اعمال می‌شوند. به طور اختیاری می‌توانید یک _برچسب حلقه_ روی یک حلقه مشخص کنید که سپس می‌توانید از آن برچسب با `break` یا `continue` استفاده کنید تا مشخص کنید که این کلمات کلیدی به حلقه برچسب‌دار اعمال می‌شوند نه حلقه داخلی‌ترین. برچسب‌های حلقه باید با یک آپاستروف شروع شوند. در اینجا یک مثال با دو حلقه تو در تو آمده است: ```rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-5-loop-labels/src/main.rs}} ``` -The outer loop has the label `'counting_up`, and it will count up from 0 to 2. -The inner loop without a label counts down from 10 to 9. The first `break` that -doesn’t specify a label will exit the inner loop only. The `break -'counting_up;` statement will exit the outer loop. This code prints: +حلقه بیرونی دارای برچسب `'counting_up` است و از 0 تا 2 شمارش می‌کند. حلقه داخلی بدون برچسب از 10 تا 9 شمارش معکوس می‌کند. اولین `break` که برچسبی مشخص نمی‌کند فقط از حلقه داخلی خارج می‌شود. عبارت `break 'counting_up;` از حلقه بیرونی خارج می‌شود. این کد موارد زیر را چاپ می‌کند: ```console {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-5-loop-labels/output.txt}} ``` -#### Conditional Loops with `while` +#### حلقه‌های شرطی با `while` -A program will often need to evaluate a condition within a loop. While the -condition is `true`, the loop runs. When the condition ceases to be `true`, the -program calls `break`, stopping the loop. It’s possible to implement behavior -like this using a combination of `loop`, `if`, `else`, and `break`; you could -try that now in a program, if you’d like. However, this pattern is so common -that Rust has a built-in language construct for it, called a `while` loop. In -Listing 3-3, we use `while` to loop the program three times, counting down each -time, and then, after the loop, print a message and exit. +یک برنامه اغلب نیاز دارد که یک شرط را درون یک حلقه ارزیابی کند. تا زمانی که شرط `true` باشد، حلقه اجرا می‌شود. زمانی که شرط دیگر `true` نباشد، برنامه با فراخوانی `break`، حلقه را متوقف می‌کند. امکان پیاده‌سازی چنین رفتاری با استفاده از ترکیب `loop`، `if`، `else` و `break` وجود دارد. می‌توانید این را اکنون در یک برنامه امتحان کنید، اگر مایل هستید. با این حال، این الگو آن‌قدر رایج است که Rust یک سازه زبان داخلی برای آن دارد که به آن حلقه `while` گفته می‌شود. در Listing 3-3، از `while` برای اجرای برنامه سه بار، شمارش معکوس در هر بار، و سپس چاپ یک پیام و خروج از حلقه استفاده می‌کنیم. -+ ```rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-03/src/main.rs}} @@ -292,17 +201,16 @@ time, and then, after the loop, print a message and exit. -This construct eliminates a lot of nesting that would be necessary if you used -`loop`, `if`, `else`, and `break`, and it’s clearer. While a condition -evaluates to `true`, the code runs; otherwise, it exits the loop. +این سازه مقدار زیادی از تو در تویی که در صورت استفاده از `loop`، `if`، `else` و `break` لازم بود را حذف می‌کند و واضح‌تر است. تا زمانی که یک شرط به مقدار `true` ارزیابی شود، کد اجرا می‌شود؛ در غیر این صورت، حلقه متوقف می‌شود. -#### Looping Through a Collection with `for` +#### تکرار از طریق یک مجموعه با `for` -You can choose to use the `while` construct to loop over the elements of a -collection, such as an array. For example, the loop in Listing 3-4 prints each -element in the array `a`. +می‌توانید از ساختار `while` برای تکرار روی عناصر +یک مجموعه، مانند آرایه، استفاده کنید. به‌عنوان مثال، +حلقه‌ی موجود در فهرست 3-4، هر عنصر موجود در آرایه +`a` را چاپ می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-04/src/main.rs}} @@ -310,30 +218,19 @@ element in the array `a`. -Here, the code counts up through the elements in the array. It starts at index -`0`, and then loops until it reaches the final index in the array (that is, -when `index < 5` is no longer `true`). Running this code will print every -element in the array: +در اینجا، کد از طریق عناصر آرایه شمارش می‌کند. از اندیس (index)`0` شروع می‌کند و سپس تا زمانی که به آخرین اندیس (index)در آرایه برسد (یعنی وقتی که `index < 5` دیگر `true` نباشد) حلقه می‌زند. اجرای این کد هر عنصر در آرایه را چاپ می‌کند: ```console {{#include ../listings/ch03-common-programming-concepts/listing-03-04/output.txt}} ``` -All five array values appear in the terminal, as expected. Even though `index` -will reach a value of `5` at some point, the loop stops executing before trying -to fetch a sixth value from the array. +همه پنج مقدار آرایه همانطور که انتظار می‌رود در ترمینال ظاهر می‌شوند. حتی اگر `index` در نهایت به مقدار `5` برسد، حلقه قبل از تلاش برای گرفتن مقدار ششم از آرایه متوقف می‌شود. -However, this approach is error prone; we could cause the program to panic if -the index value or test condition is incorrect. For example, if you changed the -definition of the `a` array to have four elements but forgot to update the -condition to `while index < 4`, the code would panic. It’s also slow, because -the compiler adds runtime code to perform the conditional check of whether the -index is within the bounds of the array on every iteration through the loop. +با این حال، این روش مستعد خطاست؛ ما می‌توانیم باعث شویم برنامه در صورت اشتباه بودن مقدار اندیس (index)یا شرط آزمایشی متوقف شود. به عنوان مثال، اگر تعریف آرایه `a` را به چهار عنصر تغییر دهید اما فراموش کنید شرط را به `while index < 4` به‌روزرسانی کنید، کد متوقف خواهد شد. همچنین این روش کند است، زیرا کامپایلر کد زمان اجرا را برای انجام بررسی شرطی در مورد اینکه آیا اندیس (index)در محدوده آرایه است یا نه در هر تکرار حلقه اضافه می‌کند. -As a more concise alternative, you can use a `for` loop and execute some code -for each item in a collection. A `for` loop looks like the code in Listing 3-5. +به عنوان یک جایگزین مختصرتر، می‌توانید از حلقه `for` استفاده کنید و برای هر مورد در یک مجموعه، کدی اجرا کنید. یک حلقه `for` شبیه کدی در Listing 3-5 است. -+ ```rust {{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-05/src/main.rs}} @@ -341,27 +238,20 @@ for each item in a collection. A `for` loop looks like the code in Listing 3-5. -When we run this code, we’ll see the same output as in Listing 3-4. More -importantly, we’ve now increased the safety of the code and eliminated the -chance of bugs that might result from going beyond the end of the array or not -going far enough and missing some items. Machine code generated from `for` -loops can be more efficient as well, because the index doesn’t need to be -compared to the length of the array at every iteration. +وقتی این کد را اجرا کنیم، خروجی مشابه فهرست 3-4 +را خواهیم دید. مهم‌تر اینکه، اکنون ایمنی کد افزایش +یافته و احتمال بروز خطاهایی که ممکن است از دسترسی +فراتر از انتهای آرایه یا عدم پیمایش کامل عناصر +نشأت بگیرند، حذف شده است. همچنین، کد ماشینی که +از حلقه‌های `for` تولید می‌شود می‌تواند کارآمدتر +باشد، زیرا در هر تکرار نیازی به مقایسه‌ی اندیس با +طول آرایه نیست. -Using the `for` loop, you wouldn’t need to remember to change any other code if -you changed the number of values in the array, as you would with the method -used in Listing 3-4. +با استفاده از حلقه `for`، نیازی به به خاطر سپردن تغییر کد دیگری ندارید اگر تعداد مقادیر در آرایه را تغییر دهید، همانطور که با روش استفاده شده در Listing 3-4 باید انجام می‌دادید. -The safety and conciseness of `for` loops make them the most commonly used loop -construct in Rust. Even in situations in which you want to run some code a -certain number of times, as in the countdown example that used a `while` loop -in Listing 3-3, most Rustaceans would use a `for` loop. The way to do that -would be to use a `Range`, provided by the standard library, which generates -all numbers in sequence starting from one number and ending before another -number. +ایمنی و مختصر بودن حلقه‌های `for` آنها را به رایج‌ترین سازه حلقه‌ای در Rust تبدیل کرده است. حتی در موقعیت‌هایی که می‌خواهید کدی را تعداد مشخصی از دفعات اجرا کنید، مانند مثال شمارش معکوس که از حلقه `while` در Listing 3-3 استفاده می‌کرد، اکثر برنامه‌نویسان Rust از حلقه `for` استفاده می‌کنند. روش انجام این کار استفاده از `Range`، که توسط کتابخانه استاندارد ارائه می‌شود، است که تمام اعداد را به ترتیب از یک عدد شروع کرده و قبل از عدد دیگری به پایان می‌رساند. -Here’s what the countdown would look like using a `for` loop and another method -we’ve not yet talked about, `rev`, to reverse the range: +این چیزی است که شمارش معکوس با استفاده از یک حلقه `for` و روش دیگری که هنوز در مورد آن صحبت نکرده‌ایم، یعنی `rev` برای معکوس کردن محدوده، به نظر می‌رسد: Filename: src/main.rs @@ -369,22 +259,17 @@ we’ve not yet talked about, `rev`, to reverse the range: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-34-for-range/src/main.rs}} ``` -This code is a bit nicer, isn’t it? +این کد کمی بهتر نیست؟ -## Summary +## خلاصه -You made it! This was a sizable chapter: you learned about variables, scalar -and compound data types, functions, comments, `if` expressions, and loops! To -practice with the concepts discussed in this chapter, try building programs to -do the following: +شما موفق شدید! این یک فصل بزرگ بود: شما درباره متغیرها، انواع داده اسکالر و مرکب، توابع، نظرات، عبارات `if` و حلقه‌ها یاد گرفتید! برای تمرین با مفاهیم مطرح‌شده در این فصل، سعی کنید برنامه‌هایی برای انجام موارد زیر بسازید: -- Convert temperatures between Fahrenheit and Celsius. -- Generate the *n*th Fibonacci number. -- Print the lyrics to the Christmas carol “The Twelve Days of Christmas,” - taking advantage of the repetition in the song. +- تبدیل دما بین فارنهایت و سلسیوس. +- تولید عدد *n*ام دنباله فیبوناچی. +- چاپ متن سرود کریسمس "The Twelve Days of Christmas"، با استفاده از تکرار موجود در این آهنگ. -When you’re ready to move on, we’ll talk about a concept in Rust that _doesn’t_ -commonly exist in other programming languages: ownership. +وقتی آماده شدید تا به مرحله بعد بروید، ما درباره مفهومی در Rust صحبت خواهیم کرد که _معمولاً_ در زبان‌های برنامه‌نویسی دیگر وجود ندارد: مالکیت. [comparing-the-guess-to-the-secret-number]: ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number [quitting-after-a-correct-guess]: ch02-00-guessing-game-tutorial.html#quitting-after-a-correct-guess diff --git a/src/ch04-00-understanding-ownership.md b/src/ch04-00-understanding-ownership.md index 52eda6a12f..ec8149f768 100644 --- a/src/ch04-00-understanding-ownership.md +++ b/src/ch04-00-understanding-ownership.md @@ -1,7 +1,3 @@ -# Understanding Ownership +# درک مالکیت -Ownership is Rust’s most unique feature and has deep implications for the rest -of the language. It enables Rust to make memory safety guarantees without -needing a garbage collector, so it’s important to understand how ownership -works. In this chapter, we’ll talk about ownership as well as several related -features: borrowing, slices, and how Rust lays data out in memory. +مالکیت یکی از ویژگی‌های منحصر به فرد Rust است و تأثیرات عمیقی بر سایر بخش‌های زبان دارد. این ویژگی به Rust اجازه می‌دهد تا بدون نیاز به یک جمع‌آوری زباله (garbage collector)، تضمین‌های ایمنی حافظه را فراهم کند، بنابراین درک چگونگی کارکرد مالکیت بسیار مهم است. در این فصل، ما درباره مالکیت و چند ویژگی مرتبط دیگر صحبت خواهیم کرد: قرض گرفتن (borrowing)، برش‌ها (slices) و نحوه چیدمان داده‌ها در حافظه توسط Rust. diff --git a/src/ch04-01-what-is-ownership.md b/src/ch04-01-what-is-ownership.md index d3d73b2b41..eb38e14d91 100644 --- a/src/ch04-01-what-is-ownership.md +++ b/src/ch04-01-what-is-ownership.md @@ -1,119 +1,110 @@ -## What Is Ownership? - -_Ownership_ is a set of rules that govern how a Rust program manages memory. -All programs have to manage the way they use a computer’s memory while running. -Some languages have garbage collection that regularly looks for no-longer-used -memory as the program runs; in other languages, the programmer must explicitly -allocate and free the memory. Rust uses a third approach: memory is managed -through a system of ownership with a set of rules that the compiler checks. If -any of the rules are violated, the program won’t compile. None of the features -of ownership will slow down your program while it’s running. - -Because ownership is a new concept for many programmers, it does take some time -to get used to. The good news is that the more experienced you become with Rust -and the rules of the ownership system, the easier you’ll find it to naturally -develop code that is safe and efficient. Keep at it! - -When you understand ownership, you’ll have a solid foundation for understanding -the features that make Rust unique. In this chapter, you’ll learn ownership by -working through some examples that focus on a very common data structure: -strings. - -> ### The Stack and the Heap +## مالکیت چیست؟ + +_مالکیت_ مجموعه‌ای از قوانین است که نحوه مدیریت حافظه را در برنامه‌های Rust تعیین می‌کند. همه برنامه‌ها باید نحوه استفاده از حافظه کامپیوتر را در هنگام اجرا مدیریت کنند. برخی زبان‌ها از جمع‌آوری زباله استفاده می‌کنند که به طور منظم حافظه‌ای را که دیگر استفاده نمی‌شود بررسی می‌کند؛ در دیگر زبان‌ها، برنامه‌نویس باید حافظه را به صورت صریح تخصیص داده و آزاد کند. Rust از یک روش سوم استفاده می‌کند: حافظه از طریق سیستمی از مالکیت با مجموعه‌ای از قوانین مدیریت می‌شود که کامپایلر آن‌ها را بررسی می‌کند. اگر هر یک از این قوانین نقض شود، برنامه کامپایل نخواهد شد. هیچ‌یک از ویژگی‌های مالکیت برنامه شما را در هنگام اجرا کند نمی‌کند. + +از آنجا که مالکیت یک مفهوم جدید برای بسیاری از برنامه‌نویسان است، زمان می‌برد تا به آن عادت کنید. خبر خوب این است که هر چه بیشتر با Rust و قوانین سیستم مالکیت آن آشنا شوید، نوشتن کدی که امن و کارآمد باشد برایتان آسان‌تر خواهد شد. به تلاش ادامه دهید! + +وقتی مالکیت را درک کنید، پایه‌ای محکم برای درک ویژگی‌هایی که Rust را منحصر به فرد می‌کنند خواهید داشت. در این فصل، مالکیت را با کار بر روی چند مثال که بر یک ساختار داده بسیار رایج تمرکز دارند یاد خواهید گرفت: رشته‌ها. + +> ### استک (Stack) و هیپ (Heap) > -> Many programming languages don’t require you to think about the stack and the -> heap very often. But in a systems programming language like Rust, whether a -> value is on the stack or the heap affects how the language behaves and why -> you have to make certain decisions. Parts of ownership will be described in -> relation to the stack and the heap later in this chapter, so here is a brief -> explanation in preparation. +> بسیاری از زبان‌های برنامه‌نویسی نیاز ندارند که +> شما زیاد درباره‌ی استک و هیپ فکر کنید. اما در زبان‌های +> برنامه‌نویسی سیستمی مانند Rust، محل قرارگیری مقدار +> روی استک یا هیپ روی رفتار زبان و تصمیماتی که باید +> بگیرید تأثیر می‌گذارد. بخش‌هایی از مالکیت (ownership) +> در ارتباط با استک و هیپ در ادامه‌ی این فصل شرح داده +> خواهند شد، بنابراین در اینجا توضیح کوتاهی در +> آماده‌سازی برای آن ارائه می‌دهیم. > -> Both the stack and the heap are parts of memory available to your code to use -> at runtime, but they are structured in different ways. The stack stores -> values in the order it gets them and removes the values in the opposite -> order. This is referred to as _last in, first out_. Think of a stack of -> plates: when you add more plates, you put them on top of the pile, and when -> you need a plate, you take one off the top. Adding or removing plates from -> the middle or bottom wouldn’t work as well! Adding data is called _pushing -> onto the stack_, and removing data is called _popping off the stack_. All -> data stored on the stack must have a known, fixed size. Data with an unknown -> size at compile time or a size that might change must be stored on the heap -> instead. +> استک و هیپ هر دو بخش‌هایی از حافظه هستند که +> در زمان اجرا برای کد شما در دسترس‌اند، اما ساختار +> متفاوتی دارند. استک مقادیر را به ترتیبی که دریافت +> می‌کند ذخیره کرده و آن‌ها را به ترتیب معکوس حذف می‌کند. +> به این مدل *آخرین وارد شده، اولین خارج شده* (last in, first out) +> گفته می‌شود. تصور کنید یک دسته بشقاب: زمانی که +> بشقاب جدیدی اضافه می‌کنید، آن را روی بالای دسته قرار +> می‌دهید و وقتی بخواهید بشقابی بردارید، از بالای دسته +> برمی‌دارید. اضافه یا حذف کردن بشقاب از وسط یا پایین +> دسته به خوبی کار نخواهد کرد! افزودن داده به استک +> را *push کردن روی استک* و حذف داده را *pop کردن از استک* +> می‌نامند. تمامی داده‌های ذخیره‌شده روی استک باید +> اندازه‌ای مشخص و ثابت داشته باشند. داده‌هایی که اندازه +> آن‌ها هنگام کامپایل مشخص نیست یا ممکن است تغییر کند، +> باید روی هیپ ذخیره شوند. > -> The heap is less organized: when you put data on the heap, you request a -> certain amount of space. The memory allocator finds an empty spot in the heap -> that is big enough, marks it as being in use, and returns a _pointer_, which -> is the address of that location. This process is called _allocating on the -> heap_ and is sometimes abbreviated as just _allocating_ (pushing values onto -> the stack is not considered allocating). Because the pointer to the heap is a -> known, fixed size, you can store the pointer on the stack, but when you want -> the actual data, you must follow the pointer. Think of being seated at a -> restaurant. When you enter, you state the number of people in your group, and -> the host finds an empty table that fits everyone and leads you there. If -> someone in your group comes late, they can ask where you’ve been seated to -> find you. +> هیپ ساختار کمتری دارد: وقتی داده‌ای را روی هیپ می‌گذارید، +> فضایی مشخص درخواست می‌کنید. تخصیص‌دهنده‌ی حافظه +> (memory allocator) محلی خالی در هیپ پیدا می‌کند که +> به‌اندازه کافی بزرگ باشد، آن را به‌عنوان فضای استفاده‌شده +> علامت‌گذاری می‌کند و یک *اشاره‌گر* که آدرس آن مکان است +> بازمی‌گرداند. این فرایند را *تخصیص در هیپ* می‌نامند +> و گاهی به‌سادگی *تخصیص* خوانده می‌شود (push کردن روی +> استک به‌عنوان تخصیص محسوب نمی‌شود). چون اندازه +> اشاره‌گر روی هیپ ثابت و مشخص است، می‌توانید اشاره‌گر را +> روی استک ذخیره کنید، اما وقتی به داده‌ی واقعی نیاز دارید، +> باید از طریق آن اشاره‌گر مراجعه کنید. این موضوع را +> می‌توان به نشستن در رستوران تشبیه کرد: وقتی وارد +> می‌شوید، تعداد افراد گروه را می‌گویید، میزبان +> میز خالی‌ای پیدا می‌کند که همه را در خود جای دهد +> و شما را به آن‌جا هدایت می‌کند. اگر کسی دیر برسد، +> می‌تواند بپرسد شما کجا نشسته‌اید تا شما را پیدا کند. > -> Pushing to the stack is faster than allocating on the heap because the -> allocator never has to search for a place to store new data; that location is -> always at the top of the stack. Comparatively, allocating space on the heap -> requires more work because the allocator must first find a big enough space -> to hold the data and then perform bookkeeping to prepare for the next -> allocation. +> افزودن داده به استک سریع‌تر از تخصیص در هیپ است، +> زیرا تخصیص‌دهنده نیازی به جستجوی جای خالی برای داده +> جدید ندارد؛ این مکان همیشه بالای استک است. در مقایسه، +> تخصیص فضای هیپ نیازمند کار بیشتری است، چون ابتدا +> باید فضای کافی پیدا شود و سپس اقدامات لازم برای +> مدیریت تخصیص بعدی انجام شود. > -> Accessing data in the heap is generally slower than accessing data on the -> stack because you have to follow a pointer to get there. Contemporary -> processors are faster if they jump around less in memory. Continuing the -> analogy, consider a server at a restaurant taking orders from many tables. -> It’s most efficient to get all the orders at one table before moving on to -> the next table. Taking an order from table A, then an order from table B, -> then one from A again, and then one from B again would be a much slower -> process. By the same token, a processor can usually do its job better if it -> works on data that’s close to other data (as it is on the stack) rather than -> farther away (as it can be on the heap). +> دسترسی به داده‌های روی هیپ معمولاً کندتر از داده‌های +> روی استک است، چون باید اشاره‌گر را دنبال کنید. +> پردازنده‌های امروزی زمانی سریع‌تر کار می‌کنند که پرش +> کمتری در حافظه داشته باشند. با ادامه‌ی تشبیه، فرض کنید +> یک پیشخدمت در رستوران سفارش‌های میزهای مختلف را می‌گیرد. +> کارآمدترین روش این است که همه سفارش‌های یک میز را +> کامل دریافت کند و سپس به میز بعدی برود. گرفتن سفارش از +> میز A، سپس میز B، دوباره میز A و سپس میز B، روندی +> بسیار کندتر خواهد بود. به همین ترتیب، پردازنده معمولاً +> بهتر کار می‌کند اگر روی داده‌هایی کار کند که به داده‌های +> دیگر نزدیک باشند (مثل داده‌های روی استک) نه داده‌هایی +> که دورتر هستند (مثل داده‌های روی هیپ). > -> When your code calls a function, the values passed into the function -> (including, potentially, pointers to data on the heap) and the function’s -> local variables get pushed onto the stack. When the function is over, those -> values get popped off the stack. +> وقتی کد شما تابعی را فراخوانی می‌کند، مقادیری که به +> تابع داده می‌شوند (شامل اشاره‌گرهایی به داده‌های روی هیپ) +> و متغیرهای محلی تابع روی استک قرار می‌گیرند. وقتی تابع +> به پایان رسید، این داده‌ها از استک حذف می‌شوند. > -> Keeping track of what parts of code are using what data on the heap, -> minimizing the amount of duplicate data on the heap, and cleaning up unused -> data on the heap so you don’t run out of space are all problems that ownership -> addresses. Once you understand ownership, you won’t need to think about the -> stack and the heap very often, but knowing that the main purpose of ownership -> is to manage heap data can help explain why it works the way it does. +> پیگیری اینکه کدام بخش‌های کد از کدام داده‌ها روی هیپ +> استفاده می‌کنند، کمینه‌سازی داده‌های تکراری روی هیپ، +> و پاک‌سازی داده‌های استفاده‌نشده روی هیپ تا فضای کافی +> باقی بماند، همه مسائلی هستند که مالکیت (ownership) به آن‌ها +> می‌پردازد. وقتی مالکیت را درک کنید، نیاز نیست زیاد درباره‌ی +> استک و هیپ فکر کنید، اما دانستن اینکه هدف اصلی مالکیت +> مدیریت داده‌های روی هیپ است، می‌تواند توضیح دهد چرا +> مالکیت این‌گونه عمل می‌کند. -### Ownership Rules +### قوانین مالکیت -First, let’s take a look at the ownership rules. Keep these rules in mind as we -work through the examples that illustrate them: +ابتدا، بیایید نگاهی به قوانین مالکیت بیندازیم. این قوانین را در ذهن داشته باشید زیرا با مثال‌هایی که آن‌ها را نشان می‌دهند کار می‌کنیم: -- Each value in Rust has an _owner_. -- There can only be one owner at a time. -- When the owner goes out of scope, the value will be dropped. +- هر مقدار در Rust یک _مالک_ دارد. +- در یک زمان فقط می‌تواند یک مالک وجود داشته باشد. +- زمانی که مالک از دامنه خارج شود، مقدار _حذف_ خواهد شد. -### Variable Scope +### دامنه متغیر -Now that we’re past basic Rust syntax, we won’t include all the `fn main() {` -code in examples, so if you’re following along, make sure to put the following -examples inside a `main` function manually. As a result, our examples will be a -bit more concise, letting us focus on the actual details rather than -boilerplate code. +حال که از سینتکس پایه Rust گذشته‌ایم، در مثال‌ها کد کامل `fn main() {` را نخواهیم آورد. بنابراین، اگر دنبال می‌کنید، مطمئن شوید که مثال‌های زیر را به صورت دستی داخل یک تابع `main` قرار دهید. در نتیجه، مثال‌های ما کمی مختصرتر خواهند بود و می‌توانیم بر روی جزئیات واقعی به جای کد ابتدایی تمرکز کنیم. -As a first example of ownership, we’ll look at the _scope_ of some variables. A -scope is the range within a program for which an item is valid. Take the -following variable: +به عنوان اولین مثال از مالکیت، به _دامنه_ برخی متغیرها نگاه می‌کنیم. دامنه محدوده‌ای است که در آن یک آیتم در یک برنامه معتبر است. به متغیر زیر توجه کنید: ```rust let s = "hello"; ``` -The variable `s` refers to a string literal, where the value of the string is -hardcoded into the text of our program. The variable is valid from the point at -which it’s declared until the end of the current _scope_. Listing 4-1 shows a -program with comments annotating where the variable `s` would be valid. +متغیر `s` به یک رشته‌ی ثابت اشاره دارد، جایی که مقدار رشته به صورت ثابت در متن برنامه ما کدنویسی شده است. این متغیر از نقطه‌ای که اعلام شده معتبر است تا انتهای _دامنه_ جاری. لیست 4-1 برنامه‌ای را با توضیحاتی که نشان می‌دهند متغیر `s` در کجا معتبر است، نمایش می‌دهد. -+ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-01/src/main.rs:here}} @@ -121,127 +112,67 @@ program with comments annotating where the variable `s` would be valid. -In other words, there are two important points in time here: - -- When `s` comes _into_ scope, it is valid. -- It remains valid until it goes _out of_ scope. - -At this point, the relationship between scopes and when variables are valid is -similar to that in other programming languages. Now we’ll build on top of this -understanding by introducing the `String` type. - -### The `String` Type - -To illustrate the rules of ownership, we need a data type that is more complex -than those we covered in the [“Data Types”][data-types] section -of Chapter 3. The types covered previously are of a known size, can be stored -on the stack and popped off the stack when their scope is over, and can be -quickly and trivially copied to make a new, independent instance if another -part of code needs to use the same value in a different scope. But we want to -look at data that is stored on the heap and explore how Rust knows when to -clean up that data, and the `String` type is a great example. - -We’ll concentrate on the parts of `String` that relate to ownership. These -aspects also apply to other complex data types, whether they are provided by -the standard library or created by you. We’ll discuss `String` in more depth in -[Chapter 8][ch8]. - -We’ve already seen string literals, where a string value is hardcoded into our -program. String literals are convenient, but they aren’t suitable for every -situation in which we may want to use text. One reason is that they’re -immutable. Another is that not every string value can be known when we write -our code: for example, what if we want to take user input and store it? For -these situations, Rust has a second string type, `String`. This type manages -data allocated on the heap and as such is able to store an amount of text that -is unknown to us at compile time. You can create a `String` from a string -literal using the `from` function, like so: +به عبارت دیگر، در اینجا دو نقطه‌ی مهم زمانی وجود دارد: + +- وقتی `s` وارد دامنه می‌شود، معتبر است. +- تا زمانی که از دامنه خارج شود معتبر باقی می‌ماند. + +در این نقطه، رابطه بین دامنه‌ها و زمان‌هایی که متغیرها معتبر هستند مشابه با زبان‌های برنامه‌نویسی دیگر است. حالا بر اساس این درک، نوع `String` را معرفی می‌کنیم. + +### نوع `String` + +برای نشان دادن قوانین مالکیت، به نوع داده‌ای نیاز داریم که پیچیده‌تر از آن‌هایی باشد که در بخش [“انواع داده”][data-types] فصل ۳ بررسی کردیم. انواعی که قبلاً پوشش داده شد، اندازه‌ی مشخصی دارند، می‌توانند در استک ذخیره شوند و وقتی دامنه‌شان تمام شد از استک برداشته شوند و می‌توانند به سرعت و به سادگی برای ساختن یک نمونه‌ی جدید و مستقل کپی شوند اگر قسمت دیگری از کد بخواهد همان مقدار را در دامنه‌ی دیگری استفاده کند. اما ما می‌خواهیم به داده‌هایی نگاه کنیم که در هیپ ذخیره شده‌اند و بررسی کنیم چگونه Rust می‌داند چه زمانی باید این داده‌ها را پاکسازی کند، و نوع `String` یک مثال عالی است. + +ما روی بخش‌هایی از `String` تمرکز خواهیم کرد که به مالکیت مربوط می‌شوند. این جنبه‌ها همچنین به سایر انواع داده‌های پیچیده اعمال می‌شوند، چه آن‌هایی که توسط کتابخانه استاندارد ارائه شده‌اند و چه آن‌هایی که خودتان ایجاد کرده‌اید. ما `String` را در فصل [۸][ch8] با جزئیات بیشتری بررسی خواهیم کرد. + +قبلاً رشته‌های ثابت را دیده‌ایم، جایی که مقدار رشته در کد ما به صورت ثابت قرار گرفته است. رشته‌های ثابت راحت هستند، اما برای هر موقعیتی که ممکن است بخواهیم از متن استفاده کنیم مناسب نیستند. یکی از دلایل این است که آن‌ها تغییرناپذیر هستند. دلیل دیگر این است که نمی‌توان هر مقدار رشته را هنگام نوشتن کد خود دانست: به عنوان مثال، اگر بخواهیم ورودی کاربر را بگیریم و ذخیره کنیم چه؟ برای این شرایط، Rust یک نوع رشته‌ی دیگر به نام `String` دارد. این نوع داده‌های تخصیص‌یافته در هیپ را مدیریت می‌کند و به همین دلیل می‌تواند مقدار متنی را ذخیره کند که اندازه‌ی آن در زمان کامپایل برای ما ناشناخته است. شما می‌توانید یک `String` را از یک رشته‌ی ثابت با استفاده از تابع `from` ایجاد کنید، به این صورت: ```rust let s = String::from("hello"); ``` -The double colon `::` operator allows us to namespace this particular `from` -function under the `String` type rather than using some sort of name like -`string_from`. We’ll discuss this syntax more in the [“Method -Syntax”][method-syntax] section of Chapter 5, and when we talk -about namespacing with modules in [“Paths for Referring to an Item in the -Module Tree”][paths-module-tree] in Chapter 7. +عملگر `::` به ما اجازه می‌دهد این تابع `from` خاص را تحت نوع `String` نام‌گذاری کنیم به جای استفاده از نوعی نام مانند `string_from`. این سینتکس را بیشتر در بخش [“سینتکس متد”][method-syntax] فصل ۵ و هنگامی که در مورد نام‌گذاری با ماژول‌ها صحبت می‌کنیم در بخش [“مسیرها برای ارجاع به یک آیتم در درخت ماژول”][paths-module-tree] فصل ۷ بررسی خواهیم کرد. -This kind of string _can_ be mutated: +این نوع رشته _می‌تواند_ تغییر کند: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-01-can-mutate-string/src/main.rs:here}} ``` -So, what’s the difference here? Why can `String` be mutated but literals -cannot? The difference is in how these two types deal with memory. +پس، تفاوت اینجا چیست؟ چرا `String` می‌تواند تغییر کند اما رشته‌های ثابت نمی‌توانند؟ تفاوت در نحوه‌ی مدیریت حافظه توسط این دو نوع است. -### Memory and Allocation +### حافظه و تخصیص -In the case of a string literal, we know the contents at compile time, so the -text is hardcoded directly into the final executable. This is why string -literals are fast and efficient. But these properties only come from the string -literal’s immutability. Unfortunately, we can’t put a blob of memory into the -binary for each piece of text whose size is unknown at compile time and whose -size might change while running the program. +در مورد یک رشته‌ی ثابت، ما محتوا را در زمان کامپایل می‌دانیم، بنابراین متن به طور مستقیم در فایل اجرایی نهایی کدنویسی شده است. به همین دلیل رشته‌های ثابت سریع و کارآمد هستند. اما این ویژگی‌ها فقط از تغییرناپذیری رشته‌ی ثابت ناشی می‌شوند. متأسفانه، نمی‌توانیم یک تکه حافظه را برای هر قطعه متنی که اندازه‌ی آن در زمان کامپایل ناشناخته است و ممکن است در حین اجرای برنامه تغییر کند، در فایل باینری قرار دهیم. -With the `String` type, in order to support a mutable, growable piece of text, -we need to allocate an amount of memory on the heap, unknown at compile time, -to hold the contents. This means: +با نوع `String`، برای پشتیبانی از یک متن قابل تغییر و قابل رشد، ما نیاز داریم مقداری حافظه را در هیپ تخصیص دهیم که در زمان کامپایل ناشناخته است تا محتوا را نگه داریم. این به این معناست که: -- The memory must be requested from the memory allocator at runtime. -- We need a way of returning this memory to the allocator when we’re done with - our `String`. +- حافظه باید در زمان اجرا از تخصیص‌دهنده حافظه درخواست شود. +- ما نیاز داریم راهی برای بازگرداندن این حافظه به تخصیص‌دهنده زمانی که کارمان با `String` تمام شد، داشته باشیم. -That first part is done by us: when we call `String::from`, its implementation -requests the memory it needs. This is pretty much universal in programming -languages. +قسمت اول توسط ما انجام می‌شود: وقتی که `String::from` را فراخوانی می‌کنیم، پیاده‌سازی آن حافظه‌ای را که نیاز دارد درخواست می‌کند. این تقریباً در تمام زبان‌های برنامه‌نویسی رایج است. -However, the second part is different. In languages with a _garbage collector -(GC)_, the GC keeps track of and cleans up memory that isn’t being used -anymore, and we don’t need to think about it. In most languages without a GC, -it’s our responsibility to identify when memory is no longer being used and to -call code to explicitly free it, just as we did to request it. Doing this -correctly has historically been a difficult programming problem. If we forget, -we’ll waste memory. If we do it too early, we’ll have an invalid variable. If -we do it twice, that’s a bug too. We need to pair exactly one `allocate` with -exactly one `free`. +اما قسمت دوم متفاوت است. در زبان‌هایی که دارای _جمع‌کننده زباله (GC)_ هستند، GC حافظه‌ای را که دیگر استفاده نمی‌شود پیگیری و پاک‌سازی می‌کند و ما نیازی به فکر کردن در مورد آن نداریم. در بیشتر زبان‌هایی که GC ندارند، این مسئولیت بر عهده ماست که مشخص کنیم چه زمانی حافظه دیگر استفاده نمی‌شود و کدی را برای آزادسازی صریح آن فراخوانی کنیم، دقیقاً همان‌طور که آن را درخواست کرده‌ایم. انجام درست این کار در تاریخ برنامه‌نویسی یک مشکل دشوار بوده است. اگر فراموش کنیم، حافظه هدر می‌رود. اگر خیلی زود این کار را انجام دهیم، یک متغیر نامعتبر خواهیم داشت. اگر دو بار این کار را انجام دهیم، این هم یک باگ است. ما نیاز داریم دقیقاً یک `allocate` را با دقیقاً یک `free` جفت کنیم. -Rust takes a different path: the memory is automatically returned once the -variable that owns it goes out of scope. Here’s a version of our scope example -from Listing 4-1 using a `String` instead of a string literal: +Rust مسیر متفاوتی را طی می‌کند: حافظه به طور خودکار وقتی که متغیری که مالک آن است از دامنه خارج می‌شود بازگردانده می‌شود. در اینجا نسخه‌ای از مثال دامنه ما از فهرست ۴-۱ وجود دارد که از یک `String` به جای یک رشته‌ی ثابت استفاده می‌کند: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-02-string-scope/src/main.rs:here}} ``` -There is a natural point at which we can return the memory our `String` needs -to the allocator: when `s` goes out of scope. When a variable goes out of -scope, Rust calls a special function for us. This function is called -[`drop`][drop], and it’s where the author of `String` can put -the code to return the memory. Rust calls `drop` automatically at the closing -curly bracket. - -> Note: In C++, this pattern of deallocating resources at the end of an item’s -> lifetime is sometimes called _Resource Acquisition Is Initialization (RAII)_. -> The `drop` function in Rust will be familiar to you if you’ve used RAII -> patterns. +یک نقطه طبیعی وجود دارد که می‌توانیم حافظه‌ای را که `String` ما نیاز دارد به تخصیص‌دهنده بازگردانیم: وقتی `s` از دامنه خارج می‌شود. وقتی یک متغیر از دامنه خارج می‌شود، Rust یک تابع خاص را برای ما فراخوانی می‌کند. این تابع [`drop`][drop] نامیده می‌شود، و اینجا جایی است که نویسنده `String` می‌تواند کدی برای بازگرداندن حافظه قرار دهد. Rust به طور خودکار `drop` را در زمان بستن آکولاد فراخوانی می‌کند. -This pattern has a profound impact on the way Rust code is written. It may seem -simple right now, but the behavior of code can be unexpected in more -complicated situations when we want to have multiple variables use the data -we’ve allocated on the heap. Let’s explore some of those situations now. +> نکته: در C++، این الگو که منابع در انتهای دوره عمر یک آیتم آزاد می‌شوند گاهی اوقات _Resource Acquisition Is Initialization (RAII)_ نامیده می‌شود. تابع `drop` در Rust برای کسانی که از الگوهای RAII استفاده کرده‌اند آشنا خواهد بود. - +این الگو تأثیر عمیقی بر نحوه نوشتن کد در Rust دارد. ممکن است اکنون ساده به نظر برسد، اما رفتار کد می‌تواند در موقعیت‌های پیچیده‌تر که می‌خواهیم متغیرهای متعددی از داده‌هایی که در هیپ تخصیص داده‌ایم استفاده کنند، غیرمنتظره باشد. اکنون به بررسی برخی از این موقعیت‌ها می‌پردازیم. -#### Variables and Data Interacting with Move +#### تعامل متغیرها و داده‌ها با انتقال (Move) -Multiple variables can interact with the same data in different ways in Rust. -Let’s look at an example using an integer in Listing 4-2. +متغیرهای مختلف می‌توانند در Rust به روش‌های مختلفی با داده‌ها تعامل داشته باشند. بیایید به مثالی با استفاده از یک عدد صحیح در فهرست ۴-۲ نگاه کنیم. -+ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-02/src/main.rs:here}} @@ -249,222 +180,123 @@ Let’s look at an example using an integer in Listing 4-2. -We can probably guess what this is doing: “bind the value `5` to `x`; then make -a copy of the value in `x` and bind it to `y`.” We now have two variables, `x` -and `y`, and both equal `5`. This is indeed what is happening, because integers -are simple values with a known, fixed size, and these two `5` values are pushed -onto the stack. +ما احتمالاً می‌توانیم حدس بزنیم این کد چه می‌کند: "مقدار `5` را به `x` اختصاص بده؛ سپس یک کپی از مقدار `x` بگیر و آن را به `y` اختصاص بده." اکنون دو متغیر داریم، `x` و `y`، و هر دو برابر `5` هستند. این دقیقاً همان چیزی است که اتفاق می‌افتد، زیرا اعداد صحیح مقادیر ساده‌ای با اندازه‌ی مشخص هستند، و این دو مقدار `5` به استک اضافه می‌شوند. -Now let’s look at the `String` version: +اکنون بیایید به نسخه `String` نگاه کنیم: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-03-string-move/src/main.rs:here}} ``` -This looks very similar, so we might assume that the way it works would be the -same: that is, the second line would make a copy of the value in `s1` and bind -it to `s2`. But this isn’t quite what happens. - -Take a look at Figure 4-1 to see what is happening to `String` under the -covers. A `String` is made up of three parts, shown on the left: a pointer to -the memory that holds the contents of the string, a length, and a capacity. -This group of data is stored on the stack. On the right is the memory on the -heap that holds the contents. - -Two tables: the first table contains the representation of s1 on the
-stack, consisting of its length (5), capacity (5), and a pointer to the first
-value in the second table. The second table contains the representation of the
-string data on the heap, byte by byte. - -Figure 4-1: Representation in memory of a `String` -holding the value `"hello"` bound to `s1` - -The length is how much memory, in bytes, the contents of the `String` are -currently using. The capacity is the total amount of memory, in bytes, that the -`String` has received from the allocator. The difference between length and -capacity matters, but not in this context, so for now, it’s fine to ignore the -capacity. - -When we assign `s1` to `s2`, the `String` data is copied, meaning we copy the -pointer, the length, and the capacity that are on the stack. We do not copy the -data on the heap that the pointer refers to. In other words, the data -representation in memory looks like Figure 4-2. - -Three tables: tables s1 and s2 representing those strings on the
-stack, respectively, and both pointing to the same string data on the heap. - -Figure 4-2: Representation in memory of the variable `s2` -that has a copy of the pointer, length, and capacity of `s1` - -The representation does _not_ look like Figure 4-3, which is what memory would -look like if Rust instead copied the heap data as well. If Rust did this, the -operation `s2 = s1` could be very expensive in terms of runtime performance if -the data on the heap were large. - -Four tables: two tables representing the stack data for s1 and s2,
-and each points to its own copy of string data on the heap. - -Figure 4-3: Another possibility for what `s2 = s1` might -do if Rust copied the heap data as well - -Earlier, we said that when a variable goes out of scope, Rust automatically -calls the `drop` function and cleans up the heap memory for that variable. But -Figure 4-2 shows both data pointers pointing to the same location. This is a -problem: when `s2` and `s1` go out of scope, they will both try to free the -same memory. This is known as a _double free_ error and is one of the memory -safety bugs we mentioned previously. Freeing memory twice can lead to memory -corruption, which can potentially lead to security vulnerabilities. - -To ensure memory safety, after the line `let s2 = s1;`, Rust considers `s1` as -no longer valid. Therefore, Rust doesn’t need to free anything when `s1` goes -out of scope. Check out what happens when you try to use `s1` after `s2` is -created; it won’t work: +این بسیار مشابه به نظر می‌رسد، بنابراین ممکن است فرض کنیم که نحوه عملکرد آن نیز مشابه است: یعنی، خط دوم یک کپی از مقدار موجود در `s1` می‌گیرد و آن را به `s2` اختصاص می‌دهد. اما این دقیقاً چیزی نیست که اتفاق می‌افتد. + +به شکل ۴-۱ نگاه کنید تا ببینید که در پشت صحنه با `String` چه اتفاقی می‌افتد. یک `String` از سه بخش تشکیل شده است که در سمت چپ نشان داده شده‌اند: یک اشاره‌گر (Pointer) به حافظه‌ای که محتوای رشته را نگه می‌دارد، یک طول، و یک ظرفیت. این گروه داده‌ها روی استک ذخیره می‌شوند. در سمت راست، حافظه روی هیپ قرار دارد که محتوای رشته را نگه می‌دارد. + +دو جدول: جدول اول نمایش s1 روی استک را نشان می‌دهد که شامل طول (۵)، ظرفیت (۵)، و اشاره‌گر (Pointer)ی به اولین مقدار در جدول دوم است. جدول دوم نمایش داده‌های رشته روی هیپ را بایت به بایت نشان می‌دهد. + +شکل ۴-۱: نمایش در حافظه یک `String` که مقدار `"hello"` به `s1` متصل است + +طول مشخص می‌کند که محتوای `String` در حال حاضر چقدر حافظه به بایت استفاده می‌کند. ظرفیت مقدار کل حافظه‌ای است که `String` از تخصیص‌دهنده دریافت کرده است. تفاوت بین طول و ظرفیت اهمیت دارد، اما نه در این زمینه، بنابراین در حال حاضر می‌توان ظرفیت را نادیده گرفت. + +وقتی `s1` را به `s2` اختصاص می‌دهیم، داده‌های `String` کپی می‌شوند، به این معنی که اشاره‌گر (Pointer)، طول، و ظرفیت موجود روی استک را کپی می‌کنیم. ما داده‌های روی هیپ را که اشاره‌گر (Pointer) به آن اشاره می‌کند، کپی نمی‌کنیم. به عبارت دیگر، نمایش داده‌ها در حافظه به شکل ۴-۲ به نظر می‌رسد. + +سه جدول: جدول‌های s1 و s2 به ترتیب نمایش‌دهنده رشته‌ها روی استک هستند و هر دو به داده‌های رشته یکسان روی هیپ اشاره می‌کنند. + +شکل ۴-۲: نمایش در حافظه متغیر `s2` که یک کپی از اشاره‌گر (Pointer)، طول، و ظرفیت `s1` دارد + +نمایش داده‌ها _به این شکل_ نیست که در شکل ۴-۳ آمده است، که نشان می‌دهد حافظه به گونه‌ای باشد که Rust همچنین داده‌های هیپ را کپی کند. اگر Rust این کار را انجام می‌داد، عملیات `s2 = s1` می‌توانست از نظر عملکرد زمان اجرا بسیار گران باشد اگر داده‌های روی هیپ بزرگ بودند. + +چهار جدول: دو جدول نمایانگر داده‌های استک برای s1 و s2 هستند و هر کدام به نسخه خود از داده‌های رشته روی هیپ اشاره می‌کنند. + +شکل ۴-۳: یک امکان دیگر برای آنچه که `s2 = s1` ممکن است انجام دهد اگر Rust داده‌های هیپ را نیز کپی کند + +قبلاً گفتیم که وقتی یک متغیر از دامنه خارج می‌شود، Rust به طور خودکار تابع `drop` را فراخوانی می‌کند و حافظه هیپ را برای آن متغیر پاک‌سازی می‌کند. اما شکل ۴-۲ نشان می‌دهد که هر دو اشاره‌گر (Pointer) داده‌ها به یک مکان اشاره می‌کنند. این یک مشکل است: وقتی `s2` و `s1` از دامنه خارج می‌شوند، هر دو سعی می‌کنند همان حافظه را آزاد کنند. این به عنوان یک خطای _آزادسازی دوباره_ شناخته می‌شود و یکی از مشکلات ایمنی حافظه است که قبلاً ذکر کردیم. آزادسازی حافظه دو بار می‌تواند منجر به خراب شدن حافظه شود، که به طور بالقوه می‌تواند منجر به آسیب‌پذیری‌های امنیتی شود. + +برای اطمینان از ایمنی حافظه، پس از خط `let s2 = s1;`، Rust متغیر `s1` را دیگر معتبر نمی‌داند. بنابراین، Rust نیازی به آزادسازی هیچ چیزی ندارد وقتی `s1` از دامنه خارج می‌شود. بررسی کنید که وقتی سعی می‌کنید `s1` را پس از ایجاد `s2` استفاده کنید، چه اتفاقی می‌افتد؛ این کار جواب نمی‌دهد: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/src/main.rs:here}} ``` -You’ll get an error like this because Rust prevents you from using the -invalidated reference: +شما خطایی مشابه این دریافت خواهید کرد زیرا Rust از استفاده از مرجع نامعتبر جلوگیری می‌کند: ```console {{#include ../listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/output.txt}} ``` -If you’ve heard the terms _shallow copy_ and _deep copy_ while working with -other languages, the concept of copying the pointer, length, and capacity -without copying the data probably sounds like making a shallow copy. But -because Rust also invalidates the first variable, instead of being called a -shallow copy, it’s known as a _move_. In this example, we would say that `s1` -was _moved_ into `s2`. So, what actually happens is shown in Figure 4-4. +اگر اصطلاحات _کپی سطحی_ و _کپی عمیق_ را هنگام کار با زبان‌های دیگر شنیده‌اید، مفهوم کپی کردن اشاره‌گر (Pointer)، طول، و ظرفیت بدون کپی کردن داده احتمالاً شبیه به انجام یک کپی سطحی است. اما به دلیل اینکه Rust همچنین متغیر اول را نامعتبر می‌کند، به جای اینکه آن را کپی سطحی بنامند، به عنوان یک _انتقال_ شناخته می‌شود. در این مثال، می‌توانیم بگوییم که `s1` به `s2` _منتقل_ شده است. بنابراین، آنچه در واقع اتفاق می‌افتد در شکل ۴-۴ نشان داده شده است. -Three tables: tables s1 and s2 representing those strings on the
-stack, respectively, and both pointing to the same string data on the heap.
-Table s1 is grayed out be-cause s1 is no longer valid; only s2 can be used to
-access the heap data. +سه جدول: جدول‌های s1 و s2 که به ترتیب نمایش‌دهنده رشته‌ها روی استک هستند و هر دو به داده‌های رشته یکسان روی هیپ اشاره می‌کنند. جدول s1 خاکستری شده زیرا s1 دیگر معتبر نیست؛ تنها s2 می‌تواند برای دسترسی به داده‌های هیپ استفاده شود. -Figure 4-4: Representation in memory after `s1` has been -invalidated +شکل ۴-۴: نمایش در حافظه پس از اینکه `s1` نامعتبر شده است -That solves our problem! With only `s2` valid, when it goes out of scope it -alone will free the memory, and we’re done. +این مشکل ما را حل می‌کند! با تنها `s2` که معتبر است، وقتی از دامنه خارج می‌شود، تنها آن حافظه را آزاد خواهد کرد و کار ما تمام است. -In addition, there’s a design choice that’s implied by this: Rust will never -automatically create “deep” copies of your data. Therefore, any _automatic_ -copying can be assumed to be inexpensive in terms of runtime performance. +علاوه بر این، یک انتخاب طراحی وجود دارد که از این نتیجه‌گیری می‌شود: Rust هرگز به طور خودکار "کپی عمیق" داده‌های شما را ایجاد نمی‌کند. بنابراین، هر گونه کپی _خودکار_ می‌تواند به‌عنوان عملی ارزان از نظر عملکرد زمان اجرا در نظر گرفته شود. -#### Scope and Assignment +#### دامنه و انتساب -The inverse of this is true for the relationship between scoping, ownership, and -memory being freed via the `drop` function as well. When you assign a completely -new value to an existing variable, Rust will call `drop` and free the original -value’s memory immediately. Consider this code, for example: +عکس این رابطه بین دامنه‌بندی، مالکیت، و آزادسازی حافظه از طریق تابع `drop` نیز صحیح است. وقتی یک مقدار کاملاً جدید به یک متغیر موجود اختصاص می‌دهید، Rust تابع `drop` را فراخوانی می‌کند و حافظه مقدار اصلی را بلافاصله آزاد می‌کند. به این کد توجه کنید: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-04b-replacement-drop/src/main.rs:here}} ``` -We initially declare a variable `s` and bind it to a `String` with the value -`"hello"`. Then we immediately create a new `String` with the value `"ahoy"` and -assign it to `s`. At this point, nothing is referring to the original value on -the heap at all. +ابتدا یک متغیر `s` را اعلان می‌کنیم و آن را به یک `String` با مقدار `"hello"` اختصاص می‌دهیم. سپس بلافاصله یک `String` جدید با مقدار `"ahoy"` ایجاد می‌کنیم و آن را به `s` اختصاص می‌دهیم. در این نقطه، هیچ چیزی به مقدار اصلی روی هیپ اشاره نمی‌کند. -One table s representing the string value on the stack, pointing to
-the second piece of string data (ahoy) on the heap, with the original string
-data (hello) grayed out because it cannot be accessed anymore. +یک جدول s که نمایانگر مقدار رشته روی استک است و به بخش دوم داده‌های رشته (ahoy) روی هیپ اشاره می‌کند، با داده‌های رشته اصلی (hello) که خاکستری شده زیرا دیگر نمی‌توان به آن دسترسی داشت. -Figure 4-5: Representation in memory after the initial -value has been replaced in its entirety. +شکل ۴-۵: نمایش در حافظه پس از اینکه مقدار اولیه به طور کامل جایگزین شده است. -The original string thus immediately goes out of scope. Rust will run the `drop` -function on it and its memory will be freed right away. When we print the value -at the end, it will be `"ahoy, world!"`. +رشته اصلی بلافاصله از دامنه خارج می‌شود. Rust تابع `drop` را روی آن اجرا می‌کند و حافظه آن بلافاصله آزاد می‌شود. وقتی مقدار را در انتها چاپ می‌کنیم، مقدار `"ahoy, world!"` خواهد بود. -#### Variables and Data Interacting with Clone +#### تعامل متغیرها و داده‌ها با Clone -If we _do_ want to deeply copy the heap data of the `String`, not just the -stack data, we can use a common method called `clone`. We’ll discuss method -syntax in Chapter 5, but because methods are a common feature in many -programming languages, you’ve probably seen them before. +اگر بخواهیم داده‌های هیپ `String` را عمیقاً کپی کنیم، نه فقط داده‌های استک، می‌توانیم از یک متد معمول به نام `clone` استفاده کنیم. ما نحو متدها را در فصل ۵ بررسی خواهیم کرد، اما از آنجا که متدها یک ویژگی رایج در بسیاری از زبان‌های برنامه‌نویسی هستند، احتمالاً قبلاً آنها را دیده‌اید. -Here’s an example of the `clone` method in action: +در اینجا یک مثال از روش `clone` در عمل آورده شده است: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-05-clone/src/main.rs:here}} ``` -This works just fine and explicitly produces the behavior shown in Figure 4-3, -where the heap data _does_ get copied. +این کد به خوبی کار می‌کند و به وضوح رفتار نشان داده شده در شکل ۴-۳ را تولید می‌کند، جایی که داده‌های هیپ _کپی_ می‌شوند. -When you see a call to `clone`, you know that some arbitrary code is being -executed and that code may be expensive. It’s a visual indicator that something -different is going on. +وقتی یک فراخوانی به `clone` می‌بینید، می‌دانید که کدی دلخواه اجرا می‌شود و ممکن است این کد هزینه‌بر باشد. این یک شاخص بصری است که نشان می‌دهد چیزی متفاوت در حال رخ دادن است. -#### Stack-Only Data: Copy +#### داده‌های فقط استک: Copy -There’s another wrinkle we haven’t talked about yet. This code using -integers—part of which was shown in Listing 4-2—works and is valid: +یک نکته دیگر وجود دارد که هنوز درباره آن صحبت نکرده‌ایم. این کد که از اعداد صحیح استفاده می‌کند - بخشی از آن در لیستینگ ۴-۲ نشان داده شده است - کار می‌کند و معتبر است: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-06-copy/src/main.rs:here}} ``` -But this code seems to contradict what we just learned: we don’t have a call to -`clone`, but `x` is still valid and wasn’t moved into `y`. - -The reason is that types such as integers that have a known size at compile -time are stored entirely on the stack, so copies of the actual values are quick -to make. That means there’s no reason we would want to prevent `x` from being -valid after we create the variable `y`. In other words, there’s no difference -between deep and shallow copying here, so calling `clone` wouldn’t do anything -different from the usual shallow copying, and we can leave it out. - -Rust has a special annotation called the `Copy` trait that we can place on -types that are stored on the stack, as integers are (we’ll talk more about -traits in [Chapter 10][traits]). If a type implements the `Copy` -trait, variables that use it do not move, but rather are trivially copied, -making them still valid after assignment to another variable. - -Rust won’t let us annotate a type with `Copy` if the type, or any of its parts, -has implemented the `Drop` trait. If the type needs something special to happen -when the value goes out of scope and we add the `Copy` annotation to that type, -we’ll get a compile-time error. To learn about how to add the `Copy` annotation -to your type to implement the trait, see [“Derivable -Traits”][derivable-traits] in Appendix C. - -So, what types implement the `Copy` trait? You can check the documentation for -the given type to be sure, but as a general rule, any group of simple scalar -values can implement `Copy`, and nothing that requires allocation or is some -form of resource can implement `Copy`. Here are some of the types that -implement `Copy`: - -- All the integer types, such as `u32`. -- The Boolean type, `bool`, with values `true` and `false`. -- All the floating-point types, such as `f64`. -- The character type, `char`. -- Tuples, if they only contain types that also implement `Copy`. For example, - `(i32, i32)` implements `Copy`, but `(i32, String)` does not. - -### Ownership and Functions - -The mechanics of passing a value to a function are similar to those when -assigning a value to a variable. Passing a variable to a function will move or -copy, just as assignment does. Listing 4-3 has an example with some annotations -showing where variables go into and out of scope. - -+اما این کد به نظر می‌رسد با آنچه که به تازگی یاد گرفتیم تناقض دارد: ما یک فراخوانی به `clone` نداریم، اما `x` همچنان معتبر است و به `y` منتقل نشده است. + +دلیل این است که انواعی مانند اعداد صحیح که اندازه مشخصی در زمان کامپایل دارند، به طور کامل روی استک ذخیره می‌شوند، بنابراین کپی کردن مقادیر واقعی سریع است. این به این معناست که هیچ دلیلی وجود ندارد که بخواهیم `x` پس از ایجاد متغیر `y` نامعتبر شود. به عبارت دیگر، در اینجا تفاوتی بین کپی عمیق و کپی سطحی وجود ندارد، بنابراین فراخوانی `clone` کاری متفاوت از کپی سطحی معمول انجام نمی‌دهد و می‌توانیم آن را حذف کنیم. + +Rust دارای یک نشانه‌گذاری ویژه به نام ویژگی `Copy` است که می‌توانیم روی انواعی که روی استک ذخیره می‌شوند (مانند اعداد صحیح) اعمال کنیم (ما در [فصل ۱۰][traits] بیشتر درباره ویژگی‌ها صحبت خواهیم کرد). اگر یک نوع ویژگی `Copy` را پیاده‌سازی کند، متغیرهایی که از آن استفاده می‌کنند جابه‌جا نمی‌شوند، بلکه به سادگی کپی می‌شوند و پس از اختصاص به متغیر دیگری همچنان معتبر باقی می‌مانند. + +Rust به ما اجازه نمی‌دهد یک نوع را با `Copy` نشانه‌گذاری کنیم اگر نوع یا هر یک از اجزای آن ویژگی `Drop` را پیاده‌سازی کرده باشند. اگر نوع به چیزی خاص نیاز داشته باشد تا زمانی که مقدار از دامنه خارج شود و ما ویژگی `Copy` را به آن نوع اضافه کنیم، یک خطای زمان کامپایل دریافت خواهیم کرد. برای یادگیری نحوه افزودن ویژگی `Copy` به نوع خود برای پیاده‌سازی این ویژگی، به [“ویژگی‌های قابل اشتقاق”][derivable-traits] در ضمیمه ج مراجعه کنید. + +پس، چه نوع‌هایی ویژگی `Copy` را پیاده‌سازی می‌کنند؟ می‌توانید برای اطمینان، مستندات نوع داده شده را بررسی کنید، اما به عنوان یک قانون کلی، هر گروه از مقادیر ساده و اسکالر می‌توانند ویژگی `Copy` را پیاده‌سازی کنند و هیچ چیزی که نیاز به تخصیص یا نوعی منبع داشته باشد نمی‌تواند ویژگی `Copy` را پیاده‌سازی کند. در اینجا تعدادی از انواعی که ویژگی `Copy` را پیاده‌سازی می‌کنند آورده شده است: + +- تمام انواع اعداد صحیح، مانند `u32`. +- نوع بولی، `bool`، با مقادیر `true` و `false`. +- تمام انواع اعشاری، مانند `f64`. +- نوع کاراکتر، `char`. +- تاپل‌ها، اگر تنها شامل انواعی باشند که ویژگی `Copy` را نیز پیاده‌سازی می‌کنند. برای مثال، `(i32, i32)` ویژگی `Copy` را پیاده‌سازی می‌کند، اما `(i32, String)` این کار را نمی‌کند. + +### مالکیت و توابع + +مکانیزم‌های انتقال یک مقدار به یک تابع مشابه زمانی است که مقداری را به یک متغیر اختصاص می‌دهیم. انتقال یک متغیر به یک تابع به همان صورت که تخصیص انجام می‌شود، جابه‌جا یا کپی می‌شود. لیستینگ ۴-۳ مثالی با برخی حاشیه‌نویسی‌ها دارد که نشان می‌دهد متغیرها کجا وارد و از دامنه خارج می‌شوند. + + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-03/src/main.rs}} @@ -472,18 +304,13 @@ showing where variables go into and out of scope. -If we tried to use `s` after the call to `takes_ownership`, Rust would throw a -compile-time error. These static checks protect us from mistakes. Try adding -code to `main` that uses `s` and `x` to see where you can use them and where -the ownership rules prevent you from doing so. +اگر بخواهیم از `s` پس از فراخوانی به `takes_ownership` استفاده کنیم، Rust یک خطای زمان کامپایل صادر می‌کند. این بررسی‌های استاتیک ما را از اشتباهات محافظت می‌کنند. سعی کنید کدی به `main` اضافه کنید که از `s` و `x` استفاده کند تا ببینید کجا می‌توانید از آن‌ها استفاده کنید و کجا قوانین مالکیت مانع شما می‌شوند. -### Return Values and Scope +### مقادیر بازگشتی و دامنه -Returning values can also transfer ownership. Listing 4-4 shows an example of a -function that returns some value, with similar annotations as those in Listing -4-3. +بازگرداندن مقادیر نیز می‌تواند مالکیت را منتقل کند. لیستینگ ۴-۴ مثالی از یک تابع که مقداری را بازمی‌گرداند نشان می‌دهد، با حاشیه‌نویسی‌هایی مشابه آنچه در لیستینگ ۴-۳ وجود داشت. -+ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-04/src/main.rs}} @@ -491,20 +318,13 @@ function that returns some value, with similar annotations as those in Listing -The ownership of a variable follows the same pattern every time: assigning a -value to another variable moves it. When a variable that includes data on the -heap goes out of scope, the value will be cleaned up by `drop` unless ownership -of the data has been moved to another variable. +مالکیت یک متغیر همیشه از یک الگوی یکسان پیروی می‌کند: تخصیص یک مقدار به متغیر دیگر آن را جابه‌جا می‌کند. زمانی که یک متغیر شامل داده‌هایی در هیپ از دامنه خارج می‌شود، مقدار با استفاده از `drop` پاک‌سازی می‌شود مگر اینکه مالکیت داده‌ها به متغیر دیگری منتقل شده باشد. -While this works, taking ownership and then returning ownership with every -function is a bit tedious. What if we want to let a function use a value but -not take ownership? It’s quite annoying that anything we pass in also needs to -be passed back if we want to use it again, in addition to any data resulting -from the body of the function that we might want to return as well. +در حالی که این روش کار می‌کند، گرفتن مالکیت و سپس بازگرداندن آن با هر تابع کمی خسته‌کننده است. اگر بخواهیم اجازه دهیم یک تابع از یک مقدار استفاده کند اما مالکیت آن را نگیرد، چه می‌شود؟ این که هر چیزی که به تابع ارسال می‌کنیم باید بازگردانده شود تا بتوانیم دوباره از آن استفاده کنیم، علاوه بر هر داده‌ای که از بدنه تابع ممکن است بخواهیم بازگردانیم، کمی آزاردهنده است. -Rust does let us return multiple values using a tuple, as shown in Listing 4-5. +Rust به ما اجازه می‌دهد مقادیر متعددی را با استفاده از یک tuple بازگردانیم، همانطور که در لیستینگ ۴-۵ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-05/src/main.rs}} @@ -512,9 +332,7 @@ Rust does let us return multiple values using a tuple, as shown in Listing 4-5. -But this is too much ceremony and a lot of work for a concept that should be -common. Luckily for us, Rust has a feature for using a value without -transferring ownership, called _references_. +اما این کار بسیار رسمی و زمان‌بر است برای مفهومی که باید رایج باشد. خوشبختانه، Rust ویژگی‌ای برای استفاده از یک مقدار بدون انتقال مالکیت دارد که _ارجاعات_ نامیده می‌شود. [data-types]: ch03-02-data-types.html#data-types [ch8]: ch08-02-strings.html @@ -522,4 +340,5 @@ transferring ownership, called _references_. [derivable-traits]: appendix-03-derivable-traits.html [method-syntax]: ch05-03-method-syntax.html#method-syntax [paths-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html -[drop]: ../std/ops/trait.Drop.html#tymethod.drop +[drop]: https://doc.rust-lang.org/std/ops/trait.Drop.html#tymethod.drop + diff --git a/src/ch04-02-references-and-borrowing.md b/src/ch04-02-references-and-borrowing.md index 6b92e53a8a..2df9160f4e 100644 --- a/src/ch04-02-references-and-borrowing.md +++ b/src/ch04-02-references-and-borrowing.md @@ -1,75 +1,56 @@ -## References and Borrowing +## ارجاعات و قرض گرفتن (References and Borrowing) -The issue with the tuple code in Listing 4-5 is that we have to return the -`String` to the calling function so we can still use the `String` after the -call to `calculate_length`, because the `String` was moved into -`calculate_length`. Instead, we can provide a reference to the `String` value. -A _reference_ is like a pointer in that it’s an address we can follow to access -the data stored at that address; that data is owned by some other variable. -Unlike a pointer, a reference is guaranteed to point to a valid value of a -particular type for the life of that reference. +مشکل کدی که در لیستینگ 4-5 با استفاده از تاپل وجود دارد این است که باید +`String` را به تابع فراخوانی‌کننده بازگردانیم تا بعد از فراخوانی +`calculate_length` بتوانیم همچنان از +`String` استفاده کنیم، زیرا +`String` به +`calculate_length` منتقل شده است. در عوض، می‌توانیم یک ارجاع به مقدار +`String` ارائه دهیم. یک _ارجاع_ مشابه یک اشاره‌گر (Pointer) است، به این معنا که یک آدرس است که می‌توانیم از آن پیروی کنیم تا به داده‌هایی که در آن آدرس ذخیره شده‌اند دسترسی پیدا کنیم؛ این داده‌ها متعلق به متغیر دیگری هستند. برخلاف اشاره‌گر (Pointer)، یک ارجاع تضمین می‌کند که به یک مقدار معتبر از نوع خاصی در طول عمر آن ارجاع اشاره می‌کند. -Here is how you would define and use a `calculate_length` function that has a -reference to an object as a parameter instead of taking ownership of the value: +در اینجا نحوه تعریف و استفاده از یک تابع +`calculate_length` آورده شده است که به جای گرفتن مالکیت مقدار، یک ارجاع به یک شی به عنوان پارامتر دارد: -+Filename: src/main.rs ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:all}} ``` - - -First, notice that all the tuple code in the variable declaration and the -function return value is gone. Second, note that we pass `&s1` into -`calculate_length` and, in its definition, we take `&String` rather than -`String`. These ampersands represent _references_, and they allow you to refer -to some value without taking ownership of it. Figure 4-6 depicts this concept. +اول، توجه کنید که تمام کد مربوط به تاپل در اعلام متغیر و مقدار بازگشتی تابع حذف شده است. دوم، دقت کنید که ما +`&s1` را به +`calculate_length` می‌دهیم و در تعریف آن، +`&String` می‌گیریم به جای +`String`. این علامت‌های & نماینده‌ی _ارجاعات_ هستند و به شما اجازه می‌دهند تا به مقداری اشاره کنید بدون اینکه مالکیت آن را بگیرید. شکل 4-6 این مفهوم را نشان می‌دهد. -Three tables: the table for s contains only a pointer to the table
-for s1. The table for s1 contains the stack data for s1 and points to the
-string data on the heap. +سه جدول: جدول s فقط یک اشاره‌گر (Pointer) به جدول s1 دارد. جدول s1 شامل داده‌های استک برای s1 است و به داده‌های رشته‌ای در هیپ اشاره می‌کند. -Figure 4-6: A diagram of `&String s` pointing at `String -s1` +شکل 4-6: نمودار `&String s` که به `String s1` اشاره می‌کند -> Note: The opposite of referencing by using `&` is _dereferencing_, which is -> accomplished with the dereference operator, `*`. We’ll see some uses of the -> dereference operator in Chapter 8 and discuss details of dereferencing in -> Chapter 15. +> توجه: متضاد ارجاع دادن با استفاده از `&`، _عدم ارجاع_ است که با عملگر عدم ارجاع، یعنی `*`، انجام می‌شود. برخی از موارد استفاده از عملگر عدم ارجاع را در فصل 8 خواهیم دید و جزئیات مربوط به عدم ارجاع را در فصل 15 بحث خواهیم کرد. -Let’s take a closer look at the function call here: +بیایید نگاهی دقیق‌تر به فراخوانی تابع بیندازیم: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:here}} ``` -The `&s1` syntax lets us create a reference that _refers_ to the value of `s1` -but does not own it. Because the reference does not own it, the value it points -to will not be dropped when the reference stops being used. +سینتکس `&s1` به ما اجازه می‌دهد یک ارجاع ایجاد کنیم که به مقدار +`s1` _اشاره می‌کند_ اما مالک آن نیست. از آنجایی که ارجاع مالک آن نیست، مقداری که به آن اشاره می‌کند زمانی که ارجاع استفاده نمی‌شود حذف نخواهد شد. -Likewise, the signature of the function uses `&` to indicate that the type of -the parameter `s` is a reference. Let’s add some explanatory annotations: +به همین ترتیب، امضای تابع از `&` استفاده می‌کند تا نشان دهد که نوع پارامتر `s` یک ارجاع است. بیایید برخی توضیحات اضافه کنیم: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-08-reference-with-annotations/src/main.rs:here}} ``` -The scope in which the variable `s` is valid is the same as any function -parameter’s scope, but the value pointed to by the reference is not dropped -when `s` stops being used, because `s` doesn’t have ownership. When functions -have references as parameters instead of the actual values, we won’t need to -return the values in order to give back ownership, because we never had -ownership. +دامنه‌ای که متغیر `s` در آن معتبر است، مشابه دامنه‌ی هر پارامتر تابع است، اما مقدار اشاره‌شده توسط ارجاع زمانی که `s` استفاده نمی‌شود حذف نمی‌شود، زیرا `s` مالکیت ندارد. وقتی توابع ارجاعات را به جای مقادیر واقعی به عنوان پارامتر دارند، نیازی نخواهیم داشت مقادیر را بازگردانیم تا مالکیت را بازگردانیم، زیرا هرگز مالکیتی نداشته‌ایم. -We call the action of creating a reference _borrowing_. As in real life, if a -person owns something, you can borrow it from them. When you’re done, you have -to give it back. You don’t own it. +ما عمل ایجاد یک ارجاع را _قرض گرفتن_ می‌نامیم. همانند زندگی واقعی، اگر شخصی چیزی را مالک باشد، شما می‌توانید آن را از او قرض بگیرید. وقتی کارتان تمام شد، باید آن را بازگردانید. شما مالک آن نیستید. -So, what happens if we try to modify something we’re borrowing? Try the code in -Listing 4-6. Spoiler alert: it doesn’t work! +پس چه اتفاقی می‌افتد اگر بخواهیم چیزی که قرض گرفته‌ایم را تغییر دهیم؟ کد موجود در لیستینگ 4-6 را امتحان کنید. هشدار: این کار نمی‌کند! -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-06/src/main.rs}} @@ -77,19 +58,17 @@ Listing 4-6. Spoiler alert: it doesn’t work! -Here’s the error: +در اینجا خطا آورده شده است: ```console {{#include ../listings/ch04-understanding-ownership/listing-04-06/output.txt}} ``` -Just as variables are immutable by default, so are references. We’re not -allowed to modify something we have a reference to. +همانطور که متغیرها به صورت پیش‌فرض غیرقابل تغییر هستند، ارجاعات نیز به همین صورت هستند. ما اجازه نداریم چیزی که به آن ارجاع داریم را تغییر دهیم. -### Mutable References +### ارجاعات متغیر -We can fix the code from Listing 4-6 to allow us to modify a borrowed value -with just a few small tweaks that use, instead, a _mutable reference_: +ما می‌توانیم کد موجود در لیستینگ 4-6 را طوری اصلاح کنیم که به ما اجازه دهد یک مقدار قرض گرفته شده را تغییر دهیم، با چند تغییر کوچک که به جای آن از _ارجاع متغیر_ استفاده کنیم: @@ -99,14 +78,9 @@ with just a few small tweaks that use, instead, a _mutable reference_: -First we change `s` to be `mut`. Then we create a mutable reference with `&mut -s` where we call the `change` function, and update the function signature to -accept a mutable reference with `some_string: &mut String`. This makes it very -clear that the `change` function will mutate the value it borrows. +ابتدا `s` را به `mut` تغییر می‌دهیم. سپس یک ارجاع متغیر با `&mut s` ایجاد می‌کنیم، جایی که تابع `change` را فراخوانی می‌کنیم، و امضای تابع را به‌روزرسانی می‌کنیم تا یک ارجاع متغیر با `some_string: &mut String` بپذیرد. این بسیار واضح می‌کند که تابع `change` مقدار قرض گرفته شده را تغییر خواهد داد. -Mutable references have one big restriction: if you have a mutable reference to -a value, you can have no other references to that value. This code that -attempts to create two mutable references to `s` will fail: +ارجاعات متغیر یک محدودیت بزرگ دارند: اگر یک ارجاع متغیر به یک مقدار داشته باشید، نمی‌توانید هیچ ارجاع دیگری به آن مقدار داشته باشید. این کد که تلاش می‌کند دو ارجاع متغیر به `s` ایجاد کند، ناموفق خواهد بود: @@ -116,148 +90,102 @@ attempts to create two mutable references to `s` will fail: -Here’s the error: +در اینجا خطا آورده شده است: ```console {{#include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/output.txt}} ``` -This error says that this code is invalid because we cannot borrow `s` as -mutable more than once at a time. The first mutable borrow is in `r1` and must -last until it’s used in the `println!`, but between the creation of that -mutable reference and its usage, we tried to create another mutable reference -in `r2` that borrows the same data as `r1`. +این خطا می‌گوید که این کد نامعتبر است زیرا نمی‌توانیم `s` را به طور همزمان بیش از یک بار به صورت متغیر قرض بگیریم. اولین قرض متغیر در `r1` است و باید تا زمانی که در `println!` استفاده شود باقی بماند، اما بین ایجاد آن ارجاع متغیر و استفاده از آن، ما سعی کردیم یک ارجاع متغیر دیگر در `r2` ایجاد کنیم که همان داده‌ای را قرض می‌گیرد که `r1` نیز قرض گرفته است. -The restriction preventing multiple mutable references to the same data at the -same time allows for mutation but in a very controlled fashion. It’s something -that new Rustaceans struggle with because most languages let you mutate -whenever you’d like. The benefit of having this restriction is that Rust can -prevent data races at compile time. A _data race_ is similar to a race -condition and happens when these three behaviors occur: +محدودیتی که از ایجاد چند ارجاع متغیر به داده‌های یکسان به طور همزمان جلوگیری می‌کند، امکان تغییر داده‌ها را فراهم می‌کند اما به صورت بسیار کنترل شده. این چیزی است که تازه‌کاران زبان Rust ممکن است با آن مشکل داشته باشند زیرا اکثر زبان‌ها به شما اجازه می‌دهند هر زمان که بخواهید داده‌ها را تغییر دهید. مزیت این محدودیت این است که Rust می‌تواند از مسابقات داده (_data race_) در زمان کامپایل جلوگیری کند. یک _مسابقه داده_ مشابه یک شرایط مسابقه (_race condition_) است و زمانی رخ می‌دهد که این سه رفتار اتفاق بیفتند: -- Two or more pointers access the same data at the same time. -- At least one of the pointers is being used to write to the data. -- There’s no mechanism being used to synchronize access to the data. +- دو یا چند اشاره‌گر (Pointer) به طور همزمان به داده‌های یکسان دسترسی پیدا می‌کنند. +- حداقل یکی از اشاره‌گر (Pointer)ها برای نوشتن در داده‌ها استفاده می‌شود. +- هیچ مکانیزمی برای هماهنگ کردن دسترسی به داده‌ها استفاده نمی‌شود. -Data races cause undefined behavior and can be difficult to diagnose and fix -when you’re trying to track them down at runtime; Rust prevents this problem by -refusing to compile code with data races! +مسابقات داده باعث رفتار نامشخص می‌شوند و در زمان اجرای برنامه ممکن است یافتن و رفع آن‌ها دشوار باشد؛ Rust با عدم کامپایل کدهای دارای مسابقات داده از این مشکل جلوگیری می‌کند! -As always, we can use curly brackets to create a new scope, allowing for -multiple mutable references, just not _simultaneous_ ones: +همانطور که همیشه، می‌توانیم از آکولادها برای ایجاد یک اسکوپ جدید استفاده کنیم که امکان وجود ارجاعات متغیر متعدد را فراهم می‌کند، اما نه به صورت _همزمان_: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-11-muts-in-separate-scopes/src/main.rs:here}} ``` -Rust enforces a similar rule for combining mutable and immutable references. -This code results in an error: +Rust یک قانون مشابه برای ترکیب ارجاعات متغیر و غیرمتغیر اعمال می‌کند. این کد منجر به خطا می‌شود: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/src/main.rs:here}} ``` -Here’s the error: +در اینجا خطا آورده شده است: ```console {{#include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/output.txt}} ``` -Whew! We _also_ cannot have a mutable reference while we have an immutable one -to the same value. +ای وای! ما _همچنین_ نمی‌توانیم یک ارجاع متغیر داشته باشیم در حالی که یک ارجاع غیرمتغیر به همان مقدار داریم. -Users of an immutable reference don’t expect the value to suddenly change out -from under them! However, multiple immutable references are allowed because no -one who is just reading the data has the ability to affect anyone else’s -reading of the data. +کاربرانی که از یک ارجاع غیرمتغیر استفاده می‌کنند، انتظار ندارند که مقدار به طور ناگهانی تغییر کند! با این حال، چندین ارجاع غیرمتغیر مجاز هستند زیرا هیچ‌کسی که فقط داده‌ها را می‌خواند، نمی‌تواند خواندن دیگران را تحت تأثیر قرار دهد. -Note that a reference’s scope starts from where it is introduced and continues -through the last time that reference is used. For instance, this code will -compile because the last usage of the immutable references is in the `println!`, -before the mutable reference is introduced: +توجه داشته باشید که اسکوپ یک ارجاع از جایی که معرفی می‌شود شروع شده و تا آخرین باری که از آن استفاده می‌شود ادامه دارد. به عنوان مثال، این کد کامپایل می‌شود زیرا آخرین استفاده از ارجاعات غیرمتغیر در `println!` است، قبل از اینکه ارجاع متغیر معرفی شود: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs:here}} ``` -The scopes of the immutable references `r1` and `r2` end after the `println!` -where they are last used, which is before the mutable reference `r3` is -created. These scopes don’t overlap, so this code is allowed: the compiler can -tell that the reference is no longer being used at a point before the end of -the scope. +اسکوپ‌های ارجاعات غیرمتغیر `r1` و `r2` بعد از `println!` که در آنجا آخرین بار استفاده شده‌اند به پایان می‌رسند، که این قبل از ایجاد ارجاع متغیر `r3` است. این اسکوپ‌ها همپوشانی ندارند، بنابراین این کد مجاز است: کامپایلر می‌تواند تشخیص دهد که ارجاع دیگر در نقطه‌ای قبل از پایان اسکوپ استفاده نمی‌شود. -Even though borrowing errors may be frustrating at times, remember that it’s -the Rust compiler pointing out a potential bug early (at compile time rather -than at runtime) and showing you exactly where the problem is. Then you don’t -have to track down why your data isn’t what you thought it was. +حتی اگر خطاهای قرض گرفتن ممکن است گاهی اوقات ناامیدکننده باشند، به یاد داشته باشید که این کامپایلر Rust است که به شما نشان می‌دهد یک باگ بالقوه در اوایل (در زمان کامپایل به جای زمان اجرا) وجود دارد و دقیقا به شما می‌گوید مشکل کجاست. سپس نیازی نیست که پیگیری کنید چرا داده‌های شما آن چیزی نیست که فکر می‌کردید. -### Dangling References +### ارجاعات آویزان -In languages with pointers, it’s easy to erroneously create a _dangling -pointer_—a pointer that references a location in memory that may have been -given to someone else—by freeing some memory while preserving a pointer to that -memory. In Rust, by contrast, the compiler guarantees that references will -never be dangling references: if you have a reference to some data, the -compiler will ensure that the data will not go out of scope before the -reference to the data does. +در زبان‌هایی که از اشاره‌گر (Pointer)ها استفاده می‌کنند، ایجاد اشتباه یک _اشاره‌گر (Pointer) آویزان_ آسان است—اشاره‌گر (Pointer)ی که به مکانی در حافظه اشاره می‌کند که ممکن است به شخص دیگری داده شده باشد—با آزاد کردن مقداری حافظه در حالی که اشاره‌گر (Pointer) به آن حافظه را حفظ می‌کنید. در Rust، برعکس، کامپایلر تضمین می‌کند که ارجاعات هرگز ارجاعات آویزان نخواهند بود: اگر به داده‌هایی ارجاع دارید، کامپایلر اطمینان می‌دهد که داده‌ها قبل از ارجاع به داده‌ها از محدوده خارج نمی‌شوند. -Let’s try to create a dangling reference to see how Rust prevents them with a -compile-time error: +بیایید سعی کنیم یک ارجاع آویزان ایجاد کنیم تا ببینیم چگونه Rust با یک خطای زمان کامپایل از این اتفاق جلوگیری می‌کند: -+Filename: src/main.rs ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/src/main.rs}} ``` - - -Here’s the error: +در اینجا خطا آورده شده است: ```console {{#include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/output.txt}} ``` -This error message refers to a feature we haven’t covered yet: lifetimes. We’ll -discuss lifetimes in detail in Chapter 10. But, if you disregard the parts -about lifetimes, the message does contain the key to why this code is a problem: +این پیام خطا به ویژگی‌ای اشاره دارد که هنوز پوشش نداده‌ایم: طول عمرها (_lifetimes_). ما طول عمرها را به طور مفصل در فصل 10 مورد بحث قرار خواهیم داد. اما، اگر بخش‌های مربوط به طول عمرها را نادیده بگیرید، پیام کلید مشکل این کد را بیان می‌کند: ```text this function's return type contains a borrowed value, but there is no value for it to be borrowed from ``` -Let’s take a closer look at exactly what’s happening at each stage of our -`dangle` code: +بیایید نگاهی دقیق‌تر به آنچه که در هر مرحله از کد `dangle` اتفاق می‌افتد بیندازیم: -+Filename: src/main.rs ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs:here}} ``` - - -Because `s` is created inside `dangle`, when the code of `dangle` is finished, -`s` will be deallocated. But we tried to return a reference to it. That means -this reference would be pointing to an invalid `String`. That’s no good! Rust -won’t let us do this. +از آنجا که `s` داخل `dangle` ایجاد می‌شود، زمانی که کد `dangle` تمام می‌شود، `s` از محدوده خارج می‌شود و آزاد می‌گردد. اما ما سعی کردیم یک ارجاع به آن برگردانیم. این بدان معناست که این ارجاع به یک `String` نامعتبر اشاره می‌کند. این خوب نیست! Rust اجازه نمی‌دهد این کار را انجام دهیم. -The solution here is to return the `String` directly: +راه‌حل در اینجا این است که به جای آن `String` را به طور مستقیم برگردانید: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-16-no-dangle/src/main.rs:here}} ``` -This works without any problems. Ownership is moved out, and nothing is -deallocated. +این بدون هیچ مشکلی کار می‌کند. مالکیت به بیرون منتقل می‌شود و هیچ چیزی آزاد نمی‌شود. -### The Rules of References +### قوانین ارجاعات -Let’s recap what we’ve discussed about references: +بیایید آنچه درباره ارجاعات بحث کردیم را مرور کنیم: -- At any given time, you can have _either_ one mutable reference _or_ any - number of immutable references. -- References must always be valid. +- در هر زمان مشخص، می‌توانید _یا_ یک ارجاع متغیر داشته باشید _یا_ هر تعداد ارجاع غیرمتغیر. +- ارجاعات باید همیشه معتبر باشند. -Next, we’ll look at a different kind of reference: slices. +در مرحله بعد، به نوع دیگری از ارجاع خواهیم پرداخت: بخش‌ها (_slices_). diff --git a/src/ch04-03-slices.md b/src/ch04-03-slices.md index b0e27697b0..15822c8100 100644 --- a/src/ch04-03-slices.md +++ b/src/ch04-03-slices.md @@ -1,34 +1,42 @@ -## The Slice Type +## نوع Slice -_Slices_ let you reference a contiguous sequence of elements in a -[collection](ch08-00-common-collections.md). A slice is a kind -of reference, so it does not have ownership. +_Slices_ به شما اجازه می‌دهند که به یک دنباله‌ی متوالی +از عناصر در یک [مجموعه](ch08-00-common-collections.md) +ارجاع دهید. اسلایس نوعی ارجاع است، بنابراین مالکیت ندارد. -Here’s a small programming problem: write a function that takes a string of -words separated by spaces and returns the first word it finds in that string. -If the function doesn’t find a space in the string, the whole string must be -one word, so the entire string should be returned. +یک مسئله‌ی کوچک برنامه‌نویسی داریم: +تابعی بنویسید که یک رشته شامل کلمات جداشده +با فاصله دریافت کند و اولین کلمه‌ای را که در +آن رشته پیدا می‌کند بازگرداند. اگر تابع +فاصله‌ای در رشته نیابد، کل رشته یک کلمه محسوب +می‌شود و باید کل رشته بازگردانده شود. -> Note: For the purposes of introducing string slices, we are assuming ASCII -> only in this section; a more thorough discussion of UTF-8 handling is in the -> [“Storing UTF-8 Encoded Text with Strings”][strings] section -> of Chapter 8. +> نکته: برای معرفی اسلایس‌های رشته‌ای در این بخش، +> فرض بر این است که تنها با ASCII سروکار داریم؛ +> بحث جامع‌تر درباره‌ی مدیریت UTF-8 در بخش +> [«ذخیره متن کدگذاری‌شده UTF-8 با رشته‌ها»][strings] +> در فصل ۸ ارائه شده است. -Let’s work through how we’d write the signature of this function without using -slices, to understand the problem that slices will solve: +بیایید بررسی کنیم چگونه امضای این تابع را بدون استفاده +از اسلایس‌ها می‌نویسیم تا مشکل‌هایی که اسلایس‌ها حل می‌کنند را +درک کنیم: ```rust,ignore fn first_word(s: &String) -> ? ``` -The `first_word` function has a parameter of type `&String`. We don’t need -ownership, so this is fine. (In idiomatic Rust, functions do not take ownership -of their arguments unless they need to, and the reasons for that will become -clear as we keep going.) But what should we return? We don’t really have a way -to talk about *part* of a string. However, we could return the index of the end -of the word, indicated by a space. Let’s try that, as shown in Listing 4-7. +تابع `first_word` یک پارامتر از نوع `&String` دریافت می‌کند. +ما نیازی به مالکیت نداریم، پس این کار درست است. +(در Rust ایدئومیک، توابع معمولاً مالکیت آرگومان‌های +خود را نمی‌گیرند مگر اینکه واقعاً لازم باشد، و دلایل +این موضوع با پیش رفتن توضیح داده خواهد شد.) اما +چه چیزی باید بازگردانیم؟ در واقع راهی برای اشاره به +*بخشی* از رشته نداریم. با این حال، می‌توانیم اندیس +پایان کلمه را که با یک فاصله مشخص می‌شود، بازگردانیم. +بیایید این روش را امتحان کنیم، همان‌طور که در فهرست 4-7 +نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:here}} @@ -36,50 +44,31 @@ of the word, indicated by a space. Let’s try that, as shown in Listing 4-7. -Because we need to go through the `String` element by element and check whether -a value is a space, we’ll convert our `String` to an array of bytes using the -`as_bytes` method. +زیرا ما نیاز داریم عنصر به عنصر از `String` عبور کنیم و بررسی کنیم که آیا یک مقدار فاصله است یا خیر، رشته خود را به یک آرایه از بایت‌ها با استفاده از متد `as_bytes` تبدیل می‌کنیم. ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:as_bytes}} ``` -Next, we create an iterator over the array of bytes using the `iter` method: +در مرحله بعد، یک iterator روی آرایه بایت‌ها با استفاده از متد `iter` ایجاد می‌کنیم: ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:iter}} ``` -We’ll discuss iterators in more detail in [Chapter 13][ch13]. -For now, know that `iter` is a method that returns each element in a collection -and that `enumerate` wraps the result of `iter` and returns each element as -part of a tuple instead. The first element of the tuple returned from -`enumerate` is the index, and the second element is a reference to the element. -This is a bit more convenient than calculating the index ourselves. +ما در [فصل 13][ch13] بیشتر درباره iterators بحث خواهیم کرد. فعلاً بدانید که `iter` یک متد است که هر عنصر در یک مجموعه را برمی‌گرداند و `enumerate` نتیجه `iter` را می‌پیچد و هر عنصر را به عنوان بخشی از یک tuple برمی‌گرداند. اولین عنصر tuple برگردانده شده از `enumerate` شاخص است و دومین عنصر ارجاع به عنصر است. این کار کمی راحت‌تر از محاسبه شاخص به صورت دستی است. -Because the `enumerate` method returns a tuple, we can use patterns to -destructure that tuple. We’ll be discussing patterns more in [Chapter -6][ch6]. In the `for` loop, we specify a pattern that has `i` -for the index in the tuple and `&item` for the single byte in the tuple. -Because we get a reference to the element from `.iter().enumerate()`, we use -`&` in the pattern. +زیرا متد `enumerate` یک tuple برمی‌گرداند، می‌توانیم از الگوها برای جدا کردن این tuple استفاده کنیم. ما در [فصل 6][ch6] بیشتر درباره الگوها صحبت خواهیم کرد. در حلقه `for`، الگویی مشخص می‌کنیم که `i` برای شاخص در tuple و `&item` برای بایت منفرد در tuple باشد. زیرا ما یک ارجاع به عنصر از `.iter().enumerate()` دریافت می‌کنیم، از `&` در الگو استفاده می‌کنیم. -Inside the `for` loop, we search for the byte that represents the space by -using the byte literal syntax. If we find a space, we return the position. -Otherwise, we return the length of the string by using `s.len()`. +داخل حلقه `for`، به دنبال بایتی که نماینده فاصله باشد می‌گردیم با استفاده از نحوه نوشتن بایت به صورت literale. اگر یک فاصله پیدا کردیم، موقعیت را برمی‌گردانیم. در غیر این صورت، طول رشته را با استفاده از `s.len()` برمی‌گردانیم. ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:inside_for}} ``` -We now have a way to find out the index of the end of the first word in the -string, but there’s a problem. We’re returning a `usize` on its own, but it’s -only a meaningful number in the context of the `&String`. In other words, -because it’s a separate value from the `String`, there’s no guarantee that it -will still be valid in the future. Consider the program in Listing 4-8 that -uses the `first_word` function from Listing 4-7. +اکنون راهی برای یافتن شاخص انتهای اولین کلمه در رشته داریم، اما مشکلی وجود دارد. ما یک `usize` به تنهایی برمی‌گردانیم، اما این تنها یک عدد معنادار در زمینه `&String` است. به عبارت دیگر، زیرا این مقدار از `String` جدا است، هیچ تضمینی وجود ندارد که در آینده همچنان معتبر باشد. برنامه‌ای که در لیستینگ 4-8 استفاده می‌شود و از تابع `first_word` از لیستینگ 4-7 استفاده می‌کند را در نظر بگیرید. -+ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-08/src/main.rs:here}} @@ -87,59 +76,49 @@ uses the `first_word` function from Listing 4-7. -This program compiles without any errors and would also do so if we used `word` -after calling `s.clear()`. Because `word` isn’t connected to the state of `s` -at all, `word` still contains the value `5`. We could use that value `5` with -the variable `s` to try to extract the first word out, but this would be a bug -because the contents of `s` have changed since we saved `5` in `word`. +این برنامه بدون هیچ خطایی کامپایل می‌شود و حتی اگر `word` را بعد از فراخوانی `s.clear()` استفاده کنیم، همچنان درست کار خواهد کرد. زیرا `word` اصلاً به حالت `s` متصل نیست، `word` همچنان مقدار `5` را دارد. ما می‌توانیم از مقدار `5` همراه با متغیر `s` استفاده کنیم تا تلاش کنیم اولین کلمه را استخراج کنیم، اما این یک باگ خواهد بود زیرا محتوای `s` از زمانی که `5` را در `word` ذخیره کردیم، تغییر کرده است. -Having to worry about the index in `word` getting out of sync with the data in -`s` is tedious and error prone! Managing these indices is even more brittle if -we write a `second_word` function. Its signature would have to look like this: +نگران هماهنگ نگه داشتن شاخص در `word` با داده‌های موجود در `s` بودن، خسته‌کننده و مستعد خطاست! مدیریت این شاخص‌ها حتی شکننده‌تر می‌شود اگر بخواهیم یک تابع `second_word` بنویسیم. امضای آن باید به این صورت باشد: ```rust,ignore fn second_word(s: &String) -> (usize, usize) { ``` -Now we’re tracking a starting _and_ an ending index, and we have even more -values that were calculated from data in a particular state but aren’t tied to -that state at all. We have three unrelated variables floating around that need -to be kept in sync. +حالا ما یک شاخص شروع و یک شاخص پایان را دنبال می‌کنیم و مقادیر بیشتری داریم که از داده‌ها در یک وضعیت خاص محاسبه شده‌اند اما اصلاً به آن وضعیت مرتبط نیستند. ما سه متغیر نامرتبط داریم که باید همگام نگه داشته شوند. -Luckily, Rust has a solution to this problem: string slices. +خوشبختانه، Rust یک راه‌حل برای این مشکل دارد: برش‌های رشته‌ای. -### String Slices +### برش‌های رشته‌ای + +_string slice_ یک ارجاع به دنباله‌ای متوالی از عناصر +یک `String` است و به این صورت نمایش داده می‌شود: -A _string slice_ is a reference to a contiguous sequence of the elements of a -`String`, and it looks like this: ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-17-slice/src/main.rs:here}} ``` -Rather than a reference to the entire `String`, `hello` is a reference to a -portion of the `String`, specified in the extra `[0..5]` bit. We create slices -using a range within brackets by specifying `[starting_index..ending_index]`, -where _`starting_index`_ is the first position in the slice and _`ending_index`_ -is one more than the last position in the slice. Internally, the slice data -structure stores the starting position and the length of the slice, which -corresponds to _`ending_index`_ minus _`starting_index`_. So, in the case of `let -world = &s[6..11];`, `world` would be a slice that contains a pointer to the -byte at index 6 of `s` with a length value of `5`. +به‌جای یک رفرنس به کل `String`، مقدار `hello` یک رفرنس به بخشی از `String` است +که در بخش اضافی `[0..5]` مشخص شده است. + +برای ساختن slice، از یک بازه در داخل براکت‌ها استفاده می‌کنیم +و آن را به صورت `[starting_index..ending_index]` می‌نویسیم؛ +که در آن، *`starting_index`* اولین موقعیت در slice است +و *`ending_index`* یکی بیشتر از آخرین موقعیت در slice است. + +درونی‌سازی ساختار داده‌ی slice، موقعیت شروع و طول slice را ذخیره می‌کند +که این طول برابر است با *`ending_index`* منهای *`starting_index`*. + +پس در مورد دستور `let world = &s[6..11];`، متغیر `world` یک slice خواهد بود +که اشاره‌گری به بایت در اندیس ۶ از `s` دارد، به همراه یک مقدار طول برابر با `5`. -Figure 4-7 shows this in a diagram. +شکل 4-7 این موضوع را در یک نمودار نشان می‌دهد. -Three tables: a table representing the stack data of s, which points
-to the byte at index 0 in a table of the string data "hello world" on
-the heap. The third table rep-resents the stack data of the slice world, which
-has a length value of 5 and points to byte 6 of the heap data table. +سه جدول: جدولی که داده‌های پشته‌ای s را نشان می‌دهد، که به بایت در شاخص 0 در یک جدول از داده‌های رشته "hello world" در heap اشاره می‌کند. جدول سوم داده‌های پشته‌ای برش world را نشان می‌دهد که دارای مقدار طول 5 است و به بایت 6 از جدول داده‌های heap اشاره می‌کند. -Figure 4-7: String slice referring to part of a -`String` +شکل 4-7: برش رشته‌ای اشاره به بخشی از یک `String` -With Rust’s `..` range syntax, if you want to start at index 0, you can drop -the value before the two periods. In other words, these are equal: +با استفاده از نحوی محدوده `..` در Rust، اگر می‌خواهید از شاخص 0 شروع کنید، می‌توانید مقدار قبل از دو نقطه را حذف کنید. به عبارت دیگر، این دو معادل هستند: ```rust let s = String::from("hello"); @@ -148,8 +127,7 @@ let slice = &s[0..2]; let slice = &s[..2]; ``` -By the same token, if your slice includes the last byte of the `String`, you -can drop the trailing number. That means these are equal: +به همین ترتیب، اگر برش شما شامل آخرین بایت `String` باشد، می‌توانید عدد پایانی را حذف کنید. این به این معناست که این دو معادل هستند: ```rust let s = String::from("hello"); @@ -160,8 +138,7 @@ let slice = &s[3..len]; let slice = &s[3..]; ``` -You can also drop both values to take a slice of the entire string. So these -are equal: +شما همچنین می‌توانید هر دو مقدار را حذف کنید تا یک برش از کل رشته بگیرید. بنابراین این دو معادل هستند: ```rust let s = String::from("hello"); @@ -172,12 +149,11 @@ let slice = &s[0..len]; let slice = &s[..]; ``` -> Note: String slice range indices must occur at valid UTF-8 character -> boundaries. If you attempt to create a string slice in the middle of a -> multibyte character, your program will exit with an error. +> توجه: اندیس‌های بازه‌ی slice برای `String` باید در مرزهای معتبر کاراکترهای UTF-8 قرار داشته باشند. +> اگر سعی کنید یک slice از رشته را در میانه‌ی یک کاراکتر چندبایتی ایجاد کنید، +> برنامه‌ی شما با خطا متوقف خواهد شد. -With all this information in mind, let’s rewrite `first_word` to return a -slice. The type that signifies “string slice” is written as `&str`: +با در نظر گرفتن این اطلاعات، بیایید `first_word` را بازنویسی کنیم تا یک برش برگرداند. نوعی که نشان‌دهنده "برش رشته‌ای" است به صورت `&str` نوشته می‌شود: @@ -187,30 +163,17 @@ slice. The type that signifies “string slice” is written as `&str`: -We get the index for the end of the word the same way we did in Listing 4-7, by -looking for the first occurrence of a space. When we find a space, we return a -string slice using the start of the string and the index of the space as the -starting and ending indices. +ما شاخص پایان کلمه را به همان روشی که در لیستینگ 4-7 انجام دادیم، پیدا می‌کنیم، یعنی با جستجوی اولین فضای خالی. وقتی یک فضای خالی پیدا می‌کنیم، یک برش رشته‌ای با استفاده از شروع رشته و شاخص فضای خالی به‌عنوان شاخص‌های شروع و پایان برمی‌گردانیم. -Now when we call `first_word`, we get back a single value that is tied to the -underlying data. The value is made up of a reference to the starting point of -the slice and the number of elements in the slice. +اکنون وقتی `first_word` را فراخوانی می‌کنیم، یک مقدار واحد دریافت می‌کنیم که به داده‌های پایه متصل است. این مقدار شامل یک ارجاع به نقطه شروع برش و تعداد عناصر موجود در برش است. -Returning a slice would also work for a `second_word` function: +بازگرداندن یک برش برای یک تابع `second_word` نیز کار می‌کند: ```rust,ignore fn second_word(s: &String) -> &str { ``` -We now have a straightforward API that’s much harder to mess up because the -compiler will ensure the references into the `String` remain valid. Remember -the bug in the program in Listing 4-8, when we got the index to the end of the -first word but then cleared the string so our index was invalid? That code was -logically incorrect but didn’t show any immediate errors. The problems would -show up later if we kept trying to use the first word index with an emptied -string. Slices make this bug impossible and let us know we have a problem with -our code much sooner. Using the slice version of `first_word` will throw a -compile-time error: +اکنون یک API ساده داریم که بسیار سخت‌تر است اشتباه شود زیرا کامپایلر اطمینان حاصل می‌کند که ارجاع‌ها به داخل `String` معتبر باقی می‌مانند. به یاد دارید خطای منطقی برنامه در لیستینگ 4-8، وقتی شاخص انتهای اولین کلمه را به دست آوردیم اما سپس رشته را پاک کردیم، بنابراین شاخص ما نامعتبر شد؟ آن کد منطقی نادرست بود اما هیچ خطای فوری نشان نمی‌داد. مشکلات بعداً وقتی تلاش می‌کردیم از شاخص اولین کلمه با یک رشته خالی استفاده کنیم، ظاهر می‌شد. برش‌ها این خطا را غیرممکن می‌کنند و به ما اطلاع می‌دهند که مشکلی در کد ما وجود دارد خیلی زودتر. استفاده از نسخه برش `first_word` یک خطای زمان کامپایل ایجاد می‌کند: @@ -220,52 +183,39 @@ compile-time error: -Here’s the compiler error: +این هم خطای کامپایلر: ```console {{#include ../listings/ch04-understanding-ownership/no-listing-19-slice-error/output.txt}} ``` -Recall from the borrowing rules that if we have an immutable reference to -something, we cannot also take a mutable reference. Because `clear` needs to -truncate the `String`, it needs to get a mutable reference. The `println!` -after the call to `clear` uses the reference in `word`, so the immutable -reference must still be active at that point. Rust disallows the mutable -reference in `clear` and the immutable reference in `word` from existing at the -same time, and compilation fails. Not only has Rust made our API easier to use, -but it has also eliminated an entire class of errors at compile time! +به یاد بیاورید از قوانین وام گرفتن که اگر ما یک ارجاع غیرقابل تغییر به چیزی داشته باشیم، نمی‌توانیم یک ارجاع قابل تغییر نیز بگیریم. از آنجایی که `clear` نیاز دارد که `String` را کوتاه کند، نیاز دارد یک ارجاع قابل تغییر بگیرد. `println!` بعد از فراخوانی به `clear` از ارجاع در `word` استفاده می‌کند، بنابراین ارجاع غیرقابل تغییر باید هنوز در آن نقطه فعال باشد. Rust ارجاع قابل تغییر در `clear` و ارجاع غیرقابل تغییر در `word` را از همزمان وجود داشتن ممنوع می‌کند و کامپایل شکست می‌خورد. نه تنها Rust API ما را آسان‌تر کرده، بلکه یک دسته کامل از خطاها را در زمان کامپایل حذف کرده است! - + -#### String Literals as Slices +#### رشته‌های متنی به عنوان برش -Recall that we talked about string literals being stored inside the binary. Now -that we know about slices, we can properly understand string literals: +به یاد بیاورید که ما درباره ذخیره رشته‌های متنی در داخل باینری صحبت کردیم. اکنون که درباره برش‌ها می‌دانیم، می‌توانیم رشته‌های متنی را به درستی درک کنیم: ```rust let s = "Hello, world!"; ``` -The type of `s` here is `&str`: it’s a slice pointing to that specific point of -the binary. This is also why string literals are immutable; `&str` is an -immutable reference. +نوع `s` در اینجا `&str` است: این یک برش است که به یک نقطه خاص از باینری اشاره می‌کند. این همچنین دلیل غیرقابل تغییر بودن رشته‌های متنی است؛ `&str` یک ارجاع غیرقابل تغییر است. -#### String Slices as Parameters +#### برش‌های رشته‌ای به عنوان پارامترها -Knowing that you can take slices of literals and `String` values leads us to -one more improvement on `first_word`, and that’s its signature: +دانستن اینکه می‌توانید برش‌هایی از رشته‌های متنی و مقادیر `String` بگیرید ما را به یک بهبود دیگر در `first_word` می‌رساند، و آن امضای آن است: ```rust,ignore fn first_word(s: &String) -> &str { ``` -A more experienced Rustacean would write the signature shown in Listing 4-9 -instead because it allows us to use the same function on both `&String` values -and `&str` values. +یک برنامه‌نویس باتجربه‌تر Rust امضای نشان داده شده در لیستینگ 4-9 را می‌نویسد زیرا این اجازه را می‌دهد که از همان تابع برای مقادیر `&String` و `&str` استفاده کنیم. -+ ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-09/src/main.rs:here}} @@ -273,14 +223,9 @@ and `&str` values. -If we have a string slice, we can pass that directly. If we have a `String`, we -can pass a slice of the `String` or a reference to the `String`. This -flexibility takes advantage of _deref coercions_, a feature we will cover in the -[“Implicit Deref Coercions with Functions and -Methods”][deref-coercions] section of Chapter 15. +اگر ما یک برش رشته‌ای داشته باشیم، می‌توانیم آن را مستقیماً ارسال کنیم. اگر یک `String` داشته باشیم، می‌توانیم یک برش از `String` یا یک ارجاع به `String` ارسال کنیم. این انعطاف‌پذیری از ویژگی _دریف کوئرسین_ استفاده می‌کند، که در بخش [“Implicit Deref Coercions with Functions and Methods”][deref-coercions] در فصل 15 به آن خواهیم پرداخت. -Defining a function to take a string slice instead of a reference to a `String` -makes our API more general and useful without losing any functionality: +تعریف یک تابع برای گرفتن یک برش رشته‌ای به جای یک ارجاع به `String`، API ما را عمومی‌تر و مفیدتر می‌کند بدون اینکه هیچ کاربردی از دست برود: @@ -290,17 +235,15 @@ makes our API more general and useful without losing any functionality: -### Other Slices +### برش‌های دیگر -String slices, as you might imagine, are specific to strings. But there’s a -more general slice type too. Consider this array: +برش‌های رشته‌ای، همانطور که تصور می‌کنید، مختص رشته‌ها هستند. اما یک نوع برش عمومی‌تر نیز وجود دارد. این آرایه را در نظر بگیرید: ```rust let a = [1, 2, 3, 4, 5]; ``` -Just as we might want to refer to part of a string, we might want to refer to -part of an array. We’d do so like this: +همانطور که ممکن است بخواهیم به بخشی از یک رشته ارجاع دهیم، ممکن است بخواهیم به بخشی از یک آرایه نیز ارجاع دهیم. این کار را می‌توانیم به این شکل انجام دهیم: ```rust let a = [1, 2, 3, 4, 5]; @@ -310,22 +253,13 @@ let slice = &a[1..3]; assert_eq!(slice, &[2, 3]); ``` -This slice has the type `&[i32]`. It works the same way as string slices do, by -storing a reference to the first element and a length. You’ll use this kind of -slice for all sorts of other collections. We’ll discuss these collections in -detail when we talk about vectors in Chapter 8. +این برش دارای نوع `&[i32]` است. این دقیقاً همانطور که برش‌های رشته‌ای کار می‌کنند، با ذخیره یک ارجاع به اولین عنصر و یک طول عمل می‌کند. شما از این نوع برش برای انواع دیگر مجموعه‌ها نیز استفاده خواهید کرد. ما این مجموعه‌ها را به تفصیل وقتی درباره وکتورها در فصل 8 صحبت کنیم، بررسی خواهیم کرد. -## Summary +## خلاصه -The concepts of ownership, borrowing, and slices ensure memory safety in Rust -programs at compile time. The Rust language gives you control over your memory -usage in the same way as other systems programming languages, but having the -owner of data automatically clean up that data when the owner goes out of scope -means you don’t have to write and debug extra code to get this control. +مفاهیم مالکیت، وام گرفتن، و برش‌ها، ایمنی حافظه را در برنامه‌های Rust در زمان کامپایل تضمین می‌کنند. زبان Rust به شما همان کنترلی بر استفاده از حافظه می‌دهد که سایر زبان‌های برنامه‌نویسی سیستم ارائه می‌دهند، اما این واقعیت که مالک داده به طور خودکار آن داده را هنگامی که مالک از حوزه خارج می‌شود، پاکسازی می‌کند، به این معنی است که نیازی به نوشتن و اشکال‌زدایی کد اضافی برای دستیابی به این کنترل ندارید. -Ownership affects how lots of other parts of Rust work, so we’ll talk about -these concepts further throughout the rest of the book. Let’s move on to -Chapter 5 and look at grouping pieces of data together in a `struct`. +مالکیت بر نحوه عملکرد بسیاری از بخش‌های دیگر Rust تأثیر می‌گذارد، بنابراین در طول بقیه کتاب این مفاهیم را بیشتر بررسی خواهیم کرد. بیایید به فصل 5 برویم و نگاهی به گروه‌بندی قطعات داده در یک `struct` بیندازیم. [ch13]: ch13-02-iterators.html [ch6]: ch06-02-match.html#patterns-that-bind-to-values diff --git a/src/ch05-00-structs.md b/src/ch05-00-structs.md index ee064224d9..85f46dde65 100644 --- a/src/ch05-00-structs.md +++ b/src/ch05-00-structs.md @@ -1,14 +1,5 @@ -# Using Structs to Structure Related Data +# استفاده از Structها برای سازماندهی داده‌های مرتبط -A _struct_, or _structure_, is a custom data type that lets you package -together and name multiple related values that make up a meaningful group. If -you’re familiar with an object-oriented language, a _struct_ is like an -object’s data attributes. In this chapter, we’ll compare and contrast tuples -with structs to build on what you already know and demonstrate when structs are -a better way to group data. +یک _struct_ یا _ساختار_، نوع داده‌ای سفارشی است که به شما اجازه می‌دهد چندین مقدار مرتبط را به صورت گروهی در کنار هم بسته‌بندی و نام‌گذاری کنید. اگر با یک زبان برنامه‌نویسی شیءگرا آشنا باشید، یک _struct_ شبیه به ویژگی‌های داده‌ای یک شیء است. در این فصل، ما ساختارها را با تاپل‌ها مقایسه و مقایسه خواهیم کرد تا نشان دهیم چه زمانی ساختارها روش بهتری برای گروه‌بندی داده‌ها هستند. -We’ll demonstrate how to define and instantiate structs. We’ll discuss how to -define associated functions, especially the kind of associated functions called -_methods_, to specify behavior associated with a struct type. Structs and enums -(discussed in Chapter 6) are the building blocks for creating new types in your -program’s domain to take full advantage of Rust’s compile-time type checking. +ما نحوه تعریف و نمونه‌سازی ساختارها را نشان خواهیم داد. همچنین بحث خواهیم کرد که چگونه توابع مرتبط، به‌ویژه نوعی از توابع مرتبط به نام _متدها_ را تعریف کنیم تا رفتار مرتبط با یک نوع ساختار را مشخص کنیم. ساختارها و Enumها (که در فصل ۶ مورد بحث قرار گرفته‌اند) بلوک‌های سازنده‌ای برای ایجاد انواع جدید در حوزه برنامه شما هستند که از بررسی نوع در زمان کامپایل در Rust به طور کامل استفاده می‌کنند. diff --git a/src/ch05-01-defining-structs.md b/src/ch05-01-defining-structs.md index 52f3118fa3..861c543885 100644 --- a/src/ch05-01-defining-structs.md +++ b/src/ch05-01-defining-structs.md @@ -1,19 +1,10 @@ -## Defining and Instantiating Structs +## تعریف و نمونه‌سازی Structها -Structs are similar to tuples, discussed in [“The Tuple Type”][tuples] section, in that both hold multiple related values. Like tuples, the -pieces of a struct can be different types. Unlike with tuples, in a struct -you’ll name each piece of data so it’s clear what the values mean. Adding these -names means that structs are more flexible than tuples: you don’t have to rely -on the order of the data to specify or access the values of an instance. +ساختارها مشابه تاپل‌ها هستند که در بخش [«نوع Tuple»][tuples] مورد بحث قرار گرفتند، به این معنا که هر دو شامل مقادیر مرتبط متعددی هستند. مانند تاپل‌ها، اجزای یک ساختار می‌توانند از انواع مختلفی باشند. اما برخلاف تاپل‌ها، در یک ساختار شما برای هر جزء داده نام تعیین می‌کنید تا معنای مقادیر روشن‌تر شود. افزودن این نام‌ها باعث می‌شود که ساختارها از تاپل‌ها انعطاف‌پذیرتر باشند: شما مجبور نیستید برای مشخص کردن یا دسترسی به مقادیر یک نمونه به ترتیب داده‌ها تکیه کنید. -To define a struct, we enter the keyword `struct` and name the entire struct. A -struct’s name should describe the significance of the pieces of data being -grouped together. Then, inside curly brackets, we define the names and types of -the pieces of data, which we call _fields_. For example, Listing 5-1 shows a -struct that stores information about a user account. +برای تعریف یک ساختار، کلمه کلیدی `struct` را وارد کرده و نام کل ساختار را تعیین می‌کنیم. نام یک ساختار باید توصیف‌کننده اهمیت اجزای داده‌ای باشد که با هم گروه‌بندی می‌شوند. سپس، داخل آکولادها، نام‌ها و انواع اجزای داده‌ای را که به آن‌ها _فیلد_ می‌گوییم، تعریف می‌کنیم. برای مثال، لیست ۵-۱ یک ساختار را نشان می‌دهد که اطلاعات مربوط به یک حساب کاربری را ذخیره می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-01/src/main.rs:here}} @@ -21,17 +12,21 @@ struct that stores information about a user account. -To use a struct after we’ve defined it, we create an _instance_ of that struct -by specifying concrete values for each of the fields. We create an instance by -stating the name of the struct and then add curly brackets containing _`key: -value`_ pairs, where the keys are the names of the fields and the values are the -data we want to store in those fields. We don’t have to specify the fields in -the same order in which we declared them in the struct. In other words, the -struct definition is like a general template for the type, and instances fill -in that template with particular data to create values of the type. For -example, we can declare a particular user as shown in Listing 5-2. +برای استفاده از یک `struct` پس از تعریف آن، باید یک *instance* از آن ایجاد کنیم +با مشخص کردن مقادیر مشخص برای هر یک از فیلدها. -+برای ساختن یک instance، نام `struct` را می‌نویسیم +و سپس داخل کروشه‌ها جفت‌های *`کلید: مقدار`* قرار می‌دهیم؛ +که در آن‌ها، کلیدها نام فیلدها هستند و مقادیر، داده‌هایی هستند که می‌خواهیم در آن فیلدها ذخیره کنیم. + +لازم نیست فیلدها را به همان ترتیبی بنویسیم که در تعریف struct آمده‌اند. + +به عبارت دیگر، تعریف `struct` مانند یک الگوی کلی برای نوع داده است +و instanceها آن الگو را با داده‌های مشخص پر می‌کنند تا مقادیر آن نوع را بسازند. + +برای نمونه، می‌توانیم یک کاربر خاص را همان‌طور که در لیست ۵-۲ نشان داده شده تعریف کنیم. + + ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-02/src/main.rs:here}} @@ -39,13 +34,9 @@ example, we can declare a particular user as shown in Listing 5-2. -To get a specific value from a struct, we use dot notation. For example, to -access this user’s email address, we use `user1.email`. If the instance is -mutable, we can change a value by using the dot notation and assigning into a -particular field. Listing 5-3 shows how to change the value in the `email` -field of a mutable `User` instance. +برای به‌دست‌آوردن مقدار خاصی از یک ساختار، از نشانه‌گذاری نقطه استفاده می‌کنیم. به عنوان مثال، برای دسترسی به آدرس ایمیل این کاربر، از `user1.email` استفاده می‌کنیم. اگر نمونه قابل تغییر باشد، می‌توانیم مقدار را با استفاده از نشانه‌گذاری نقطه تغییر داده و در یک فیلد خاص مقداردهی کنیم. لیست ۵-۳ نشان می‌دهد که چگونه مقدار در فیلد `email` یک نمونه قابل تغییر `User` را تغییر دهیم. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-03/src/main.rs:here}} @@ -53,16 +44,11 @@ field of a mutable `User` instance. -Note that the entire instance must be mutable; Rust doesn’t allow us to mark -only certain fields as mutable. As with any expression, we can construct a new -instance of the struct as the last expression in the function body to -implicitly return that new instance. +توجه داشته باشید که کل نمونه باید قابل تغییر باشد؛ Rust به ما اجازه نمی‌دهد که فقط برخی از فیلدها را به صورت قابل تغییر علامت‌گذاری کنیم. مانند هر عبارت دیگری، می‌توانیم یک نمونه جدید از ساختار را به عنوان آخرین عبارت در بدنه یک تابع بسازیم تا به طور ضمنی آن نمونه جدید را بازگردانیم. -Listing 5-4 shows a `build_user` function that returns a `User` instance with -the given email and username. The `active` field gets the value of `true`, and -the `sign_in_count` gets a value of `1`. +لیست ۵-۴ یک تابع `build_user` را نشان می‌دهد که یک نمونه از `User` را با ایمیل و نام کاربری مشخص برمی‌گرداند. فیلد `active` مقدار `true` می‌گیرد و `sign_in_count` مقدار `1` دریافت می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-04/src/main.rs:here}} @@ -70,23 +56,15 @@ the `sign_in_count` gets a value of `1`. -It makes sense to name the function parameters with the same name as the struct -fields, but having to repeat the `email` and `username` field names and -variables is a bit tedious. If the struct had more fields, repeating each name -would get even more annoying. Luckily, there’s a convenient shorthand! - - +نوشتن نام پارامترهای تابع با همان نام فیلدهای ساختار منطقی است، اما تکرار نام‌های `email` و `username` برای هر دو فیلد و متغیرها کمی خسته‌کننده است. اگر ساختار فیلدهای بیشتری داشت، تکرار هر نام حتی آزاردهنده‌تر می‌شد. خوشبختانه، یک راه میانبر راحت وجود دارد! -### Using the Field Init Shorthand +### استفاده از میانبر مقداردهی فیلد -Because the parameter names and the struct field names are exactly the same in -Listing 5-4, we can use the _field init shorthand_ syntax to rewrite -`build_user` so it behaves exactly the same but doesn’t have the repetition of -`username` and `email`, as shown in Listing 5-5. +از آنجا که نام پارامترها و نام فیلدهای ساختار دقیقاً یکسان هستند، می‌توانیم از نحو _میانبر مقداردهی فیلد_ برای بازنویسی `build_user` استفاده کنیم تا همان رفتار را داشته باشد اما تکرار `username` و `email` را نداشته باشد، همان‌طور که در لیست ۵-۵ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-05/src/main.rs:here}} @@ -94,23 +72,19 @@ Listing 5-4, we can use the _field init shorthand_ syntax to rewrite -Here, we’re creating a new instance of the `User` struct, which has a field -named `email`. We want to set the `email` field’s value to the value in the -`email` parameter of the `build_user` function. Because the `email` field and -the `email` parameter have the same name, we only need to write `email` rather -than `email: email`. +اینجا، ما یک نمونه جدید از ساختار `User` می‌سازیم که فیلدی به نام `email` دارد. ما می‌خواهیم مقدار فیلد `email` را به مقداری که در پارامتر `email` تابع `build_user` وجود دارد تنظیم کنیم. از آنجا که فیلد `email` و پارامتر `email` نام یکسانی دارند، فقط نیاز داریم `email` بنویسیم، نه `email: email`. + +### ایجاد نمونه‌ها از نمونه‌های دیگر با استفاده از نحو به‌روزرسانی Struct -### Creating Instances from Other Instances with Struct Update Syntax +اغلب مفید است که یک instance جدید از یک `struct` ایجاد کنیم +که بیشتر مقادیر آن از یک instance دیگر با همان نوع گرفته شده باشد، +اما برخی از مقادیر آن تغییر کرده باشند. +برای انجام این کار می‌توانید از *syntax به‌روزرسانی struct* استفاده کنید. -It’s often useful to create a new instance of a struct that includes most of -the values from another instance of the same type, but changes some. You can do -this using _struct update syntax_. -First, in Listing 5-6 we show how to create a new `User` instance in `user2` -regularly, without the update syntax. We set a new value for `email` but -otherwise use the same values from `user1` that we created in Listing 5-2. +ابتدا، در لیست ۵-۶ نشان داده شده است که چگونه می‌توان یک نمونه جدید `User` در `user2` ایجاد کرد، بدون استفاده از نحو به‌روزرسانی. ما یک مقدار جدید برای `email` تنظیم می‌کنیم اما در غیر این صورت از همان مقادیر در `user1` که قبلاً در لیست ۵-۲ ایجاد شده است، استفاده می‌کنیم. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-06/src/main.rs:here}} @@ -118,11 +92,9 @@ otherwise use the same values from `user1` that we created in Listing 5-2. -Using struct update syntax, we can achieve the same effect with less code, as -shown in Listing 5-7. The syntax `..` specifies that the remaining fields not -explicitly set should have the same value as the fields in the given instance. +با استفاده از نحو به‌روزرسانی Struct، می‌توانیم همان نتیجه را با کد کمتری به دست آوریم، همان‌طور که در لیست ۵-۷ نشان داده شده است. نحو `..` مشخص می‌کند که فیلدهای باقی‌مانده‌ای که به صورت صریح تنظیم نشده‌اند باید همان مقادیری را داشته باشند که در نمونه داده شده هستند. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-07/src/main.rs:here}} @@ -130,38 +102,26 @@ explicitly set should have the same value as the fields in the given instance. -The code in Listing 5-7 also creates an instance in `user2` that has a -different value for `email` but has the same values for the `username`, -`active`, and `sign_in_count` fields from `user1`. The `..user1` must come last -to specify that any remaining fields should get their values from the -corresponding fields in `user1`, but we can choose to specify values for as -many fields as we want in any order, regardless of the order of the fields in -the struct’s definition. - -Note that the struct update syntax uses `=` like an assignment; this is because -it moves the data, just as we saw in the [“Variables and Data Interacting with -Move”][move] section. In this example, we can no longer use -`user1` after creating `user2` because the `String` in the `username` field of -`user1` was moved into `user2`. If we had given `user2` new `String` values for -both `email` and `username`, and thus only used the `active` and `sign_in_count` -values from `user1`, then `user1` would still be valid after creating `user2`. -Both `active` and `sign_in_count` are types that implement the `Copy` trait, so -the behavior we discussed in the [“Stack-Only Data: Copy”][copy] -section would apply. We can also still use `user1.email` in this example, -because its value was not moved out of `user1`. - -### Using Tuple Structs Without Named Fields to Create Different Types - -Rust also supports structs that look similar to tuples, called _tuple structs_. -Tuple structs have the added meaning the struct name provides but don’t have -names associated with their fields; rather, they just have the types of the -fields. Tuple structs are useful when you want to give the whole tuple a name -and make the tuple a different type from other tuples, and when naming each -field as in a regular struct would be verbose or redundant. - -To define a tuple struct, start with the `struct` keyword and the struct name -followed by the types in the tuple. For example, here we define and use two -tuple structs named `Color` and `Point`: +کد در لیست ۵-۷ همچنین نمونه‌ای در `user2` ایجاد می‌کند که مقدار متفاوتی برای `email` دارد اما دارای مقادیر مشابهی برای فیلدهای `username`، `active` و `sign_in_count` از `user1` است. `..user1` باید در انتها بیاید تا مشخص کند که فیلدهای باقی‌مانده باید مقادیر خود را از فیلدهای مربوطه در `user1` دریافت کنند، اما می‌توانیم مقادیر را برای هر تعداد فیلدی که می‌خواهیم به هر ترتیبی مشخص کنیم، بدون توجه به ترتیب فیلدها در تعریف ساختار. + + +توجه داشته باشید که نحو به‌روزرسانی struct از `=` مانند عمل انتساب استفاده می‌کند؛ +زیرا داده را *منتقل* می‌کند، همان‌طور که در بخش [«تعامل متغیرها و داده‌ها با Move»][move] دیدیم. +در این مثال، پس از ایجاد `user2` دیگر نمی‌توانیم از `user1` استفاده کنیم +چون `String` موجود در فیلد `username` از `user1` به `user2` منتقل شده است. +اگر برای `user2` مقادیر جدیدی از نوع `String` برای هر دو فیلد `email` و `username` مشخص کرده بودیم +و تنها از مقادیر `active` و `sign_in_count` از `user1` استفاده کرده بودیم، +آنگاه `user1` پس از ساختن `user2` همچنان معتبر باقی می‌ماند. +زیرا `active` و `sign_in_count` از نوع‌هایی هستند که `Copy` trait را پیاده‌سازی می‌کنند، +و بنابراین رفتاری که در بخش [«داده‌های فقط-پشته: Copy»][copy] توضیح دادیم، اعمال می‌شود. +در این مثال، همچنان می‌توانیم از `user1.email` استفاده کنیم، +چون مقدار آن از `user1` خارج نشده است. + +### استفاده از ساختارهای Tuple بدون فیلدهای نام‌گذاری‌شده برای ایجاد انواع مختلف + +Rust همچنین از ساختارهایی که شبیه تاپل‌ها هستند پشتیبانی می‌کند که به آن‌ها _ساختارهای Tuple_ می‌گویند. ساختارهای Tuple به دلیل نام ساختار معنای بیشتری دارند اما نام‌هایی برای فیلدهای خود ندارند؛ بلکه فقط نوع فیلدها را دارند. ساختارهای Tuple زمانی مفید هستند که بخواهید به کل تاپل یک نام بدهید و آن را به عنوان نوعی متفاوت از تاپل‌های دیگر مشخص کنید، و وقتی نام‌گذاری هر فیلد مانند یک ساختار معمولی طولانی یا زائد باشد. + +برای تعریف یک ساختار Tuple، با کلمه کلیدی `struct` و نام ساختار شروع کنید و سپس نوع‌های موجود در تاپل را مشخص کنید. به عنوان مثال، در اینجا ما دو ساختار Tuple به نام‌های `Color` و `Point` تعریف و استفاده کرده‌ایم: @@ -171,27 +131,24 @@ tuple structs named `Color` and `Point`: -Note that the `black` and `origin` values are different types because they’re -instances of different tuple structs. Each struct you define is its own type, -even though the fields within the struct might have the same types. For -example, a function that takes a parameter of type `Color` cannot take a -`Point` as an argument, even though both types are made up of three `i32` -values. Otherwise, tuple struct instances are similar to tuples in that you can -destructure them into their individual pieces, and you can use a `.` followed -by the index to access an individual value. Unlike tuples, tuple structs -require you to name the type of the struct when you destructure them. For -example, we would write `let Point(x, y, z) = origin;` to destructure the -values in the `origin` point into variables named `x`, `y`, and `z`. - -### Unit-Like Structs Without Any Fields - -You can also define structs that don’t have any fields! These are called -_unit-like structs_ because they behave similarly to `()`, the unit type that -we mentioned in [“The Tuple Type”][tuples] section. Unit-like -structs can be useful when you need to implement a trait on some type but don’t -have any data that you want to store in the type itself. We’ll discuss traits -in Chapter 10. Here’s an example of declaring and instantiating a unit struct -named `AlwaysEqual`: +توجه داشته باشید که مقادیر `black` و `origin` از انواع متفاوتی هستند +چون آن‌ها instanceهای دو `tuple struct` مختلف‌اند. +هر `struct`ای که تعریف می‌کنید، نوع خاص خود را دارد، +حتی اگر فیلدهای داخل آن `struct` نوع‌های یکسانی داشته باشند. +برای مثال، یک تابع که پارامتری از نوع `Color` می‌گیرد، +نمی‌تواند یک `Point` را به عنوان آرگومان دریافت کند، +حتی اگر هر دو نوع از سه مقدار `i32` تشکیل شده باشند. +به جز این مورد، `tuple struct`ها شبیه به `tuple`ها هستند +از این جهت که می‌توانید آن‌ها را به اجزای منفردشان destructure کنید، +و با استفاده از `.` و اندیس، به مقدار خاصی دسترسی پیدا کنید. +برخلاف `tuple`ها، `tuple struct`ها نیاز دارند که هنگام destructure کردن، +نام نوع struct را مشخص کنید. +برای مثال، برای destructure کردن مقادیر موجود در `origin` به متغیرهای `x`، `y` و `z`، +باید بنویسیم: `let Point(x, y, z) = origin;` + +### ساختارهای شبیه به Unit بدون هیچ فیلدی + +شما همچنین می‌توانید ساختارهایی تعریف کنید که هیچ فیلدی ندارند! این‌ها به عنوان _ساختارهای شبیه Unit_ شناخته می‌شوند زیرا شبیه به نوع `()`، نوع Unit، رفتار می‌کنند که در بخش [«نوع Tuple»][tuples] مورد اشاره قرار گرفت. ساختارهای شبیه Unit زمانی مفید هستند که نیاز به پیاده‌سازی یک ویژگی بر روی یک نوع داشته باشید اما هیچ داده‌ای برای ذخیره در خود نوع نداشته باشید. ما ویژگی‌ها را در فصل ۱۰ بحث خواهیم کرد. در اینجا مثالی از اعلام و نمونه‌سازی یک ساختار شبیه Unit به نام `AlwaysEqual` آورده شده است: @@ -201,92 +158,73 @@ named `AlwaysEqual`: -To define `AlwaysEqual`, we use the `struct` keyword, the name we want, and -then a semicolon. No need for curly brackets or parentheses! Then we can get an -instance of `AlwaysEqual` in the `subject` variable in a similar way: using the -name we defined, without any curly brackets or parentheses. Imagine that later -we’ll implement behavior for this type such that every instance of -`AlwaysEqual` is always equal to every instance of any other type, perhaps to -have a known result for testing purposes. We wouldn’t need any data to -implement that behavior! You’ll see in Chapter 10 how to define traits and -implement them on any type, including unit-like structs. - -> ### Ownership of Struct Data -> -> In the `User` struct definition in Listing 5-1, we used the owned `String` -> type rather than the `&str` string slice type. This is a deliberate choice -> because we want each instance of this struct to own all of its data and for -> that data to be valid for as long as the entire struct is valid. -> -> It’s also possible for structs to store references to data owned by something -> else, but to do so requires the use of _lifetimes_, a Rust feature that we’ll -> discuss in Chapter 10. Lifetimes ensure that the data referenced by a struct -> is valid for as long as the struct is. Let’s say you try to store a reference -> in a struct without specifying lifetimes, like the following; this won’t work: -> -> -> -> -> -> ```rust,ignore,does_not_compile -> struct User { -> active: bool, -> username: &str, -> email: &str, -> sign_in_count: u64, -> } -> -> fn main() { -> let user1 = User { -> active: true, -> username: "someusername123", -> email: "someone@example.com", -> sign_in_count: 1, -> }; -> } -> ``` -> -> -> -> The compiler will complain that it needs lifetime specifiers: -> -> ```console -> $ cargo run -> Compiling structs v0.1.0 (file:///projects/structs) -> error[E0106]: missing lifetime specifier -> --> src/main.rs:3:15 -> | -> 3 | username: &str, -> | ^ expected named lifetime parameter -> | -> help: consider introducing a named lifetime parameter -> | -> 1 ~ struct User<'a> { -> 2 | active: bool, -> 3 ~ username: &'a str, -> | -> -> error[E0106]: missing lifetime specifier -> --> src/main.rs:4:12 -> | -> 4 | email: &str, -> | ^ expected named lifetime parameter -> | -> help: consider introducing a named lifetime parameter -> | -> 1 ~ struct User<'a> { -> 2 | active: bool, -> 3 | username: &str, -> 4 ~ email: &'a str, -> | +برای تعریف `AlwaysEqual`، از کلمه کلیدی `struct`، نام دلخواه و سپس یک نقطه ویرگول استفاده می‌کنیم. نیازی به آکولاد یا پرانتز نیست! سپس می‌توانیم یک نمونه از `AlwaysEqual` را در متغیر `subject` با استفاده از همان نامی که تعریف کرده‌ایم، بدون هیچ آکولاد یا پرانتزی دریافت کنیم. تصور کنید که در آینده رفتاری را برای این نوع پیاده‌سازی خواهیم کرد که همه نمونه‌های `AlwaysEqual` همیشه با تمام نمونه‌های دیگر برابر باشند، شاید برای داشتن نتیجه‌ای مشخص برای اهداف آزمایشی. برای پیاده‌سازی آن رفتار نیازی به هیچ داده‌ای نداریم! شما در فصل ۱۰ خواهید دید که چگونه می‌توانید ویژگی‌ها را تعریف و آن‌ها را بر روی هر نوعی، از جمله ساختارهای شبیه به Unit، پیاده‌سازی کنید. + +> ### مالکیت داده‌های Struct > -> For more information about this error, try `rustc --explain E0106`. -> error: could not compile `structs` (bin "structs") due to 2 previous errors -> ``` +> در تعریف ساختار `User` در لیست ۵-۱، ما از نوع مالک `String` به جای نوع برش رشته `&str` استفاده کردیم. این یک انتخاب عمدی است زیرا ما می‌خواهیم هر نمونه از این ساختار همه داده‌های خود را مالک باشد و این داده‌ها به مدت زمانی که کل ساختار معتبر است، معتبر باقی بمانند. > -> In Chapter 10, we’ll discuss how to fix these errors so you can store -> references in structs, but for now, we’ll fix errors like these using owned -> types like `String` instead of references like `&str`. +> همچنین ممکن است ساختارهایی وجود داشته باشند که به داده‌های متعلق به چیز دیگری ارجاع می‌دهند، اما برای انجام این کار نیاز به استفاده از _طول عمر‌ها_ داریم، یک ویژگی از Rust که ما در فصل ۱۰ مورد بحث قرار خواهیم داد. طول عمرها اطمینان حاصل می‌کنند که داده‌هایی که توسط یک ساختار ارجاع داده شده‌اند تا زمانی که ساختار معتبر است، معتبر باقی می‌مانند. بیایید بگوییم شما سعی دارید یک ارجاع را در یک ساختار ذخیره کنید بدون اینکه طول عمرها را مشخص کنید، مانند مثال زیر؛ این کار نخواهد کرد: + ++ +```rust,ignore,does_not_compile +struct User { + active: bool, + username: &str, + email: &str, + sign_in_count: u64, +} + +fn main() { + let user1 = User { + active: true, + username: "someusername123", + email: "someone@example.com", + sign_in_count: 1, + }; +} +``` + + + +کامپایلر شکایت خواهد کرد که به مشخص‌کننده‌های طول عمر نیاز دارد: + +```console +$ cargo run + Compiling structs v0.1.0 (file:///projects/structs) +error[E0106]: missing lifetime specifier + --> src/main.rs:3:15 + | +3 | username: &str, + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +1 ~ struct User<'a> { +2 | active: bool, +3 ~ username: &'a str, + | + +error[E0106]: missing lifetime specifier + --> src/main.rs:4:12 + | +4 | email: &str, + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +1 ~ struct User<'a> { +2 | active: bool, +3 | username: &str, +4 ~ email: &'a str, + | + +For more information about this error, try `rustc --explain E0106`. +error: could not compile `structs` (bin "structs") due to 2 previous errors +``` + +در فصل ۱۰، ما بحث خواهیم کرد که چگونه این خطاها را برطرف کنید تا بتوانید ارجاع‌ها را در ساختارها ذخیره کنید، اما در حال حاضر، ما این خطاها را با استفاده از انواع مالک مانند `String` به جای ارجاع‌ها مانند `&str` برطرف خواهیم کرد. section -of Chapter 3: by using tuples. +تابع `area` قرار است مساحت یک مستطیل را محاسبه کند، اما تابعی که نوشتیم دو پارامتر دارد و هیچ‌کجا در برنامه مشخص نیست که این پارامترها به هم مرتبط هستند. بهتر است عرض و ارتفاع را به صورت گروهی تعریف کنیم تا خوانایی و مدیریت کد بهتر شود. یکی از روش‌هایی که قبلاً در بخش [«نوع Tuple»][the-tuple-type] فصل ۳ بحث کردیم این است که از تاپل‌ها استفاده کنیم. -### Refactoring with Tuples +### بازنویسی با استفاده از Tupleها -Listing 5-9 shows another version of our program that uses tuples. +لیست ۵-۹ نسخه دیگری از برنامه ما را نشان می‌دهد که از تاپل‌ها استفاده می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-09/src/main.rs}} @@ -52,25 +40,15 @@ Listing 5-9 shows another version of our program that uses tuples. -In one way, this program is better. Tuples let us add a bit of structure, and -we’re now passing just one argument. But in another way, this version is less -clear: tuples don’t name their elements, so we have to index into the parts of -the tuple, making our calculation less obvious. +از یک منظر، این برنامه بهتر است. تاپل‌ها کمی ساختار اضافه می‌کنند و اکنون ما فقط یک آرگومان ارسال می‌کنیم. اما از منظر دیگر، این نسخه کمتر واضح است: تاپل‌ها اجزای خود را نام‌گذاری نمی‌کنند، بنابراین باید به بخش‌های تاپل با استفاده از ایندکس‌ها دسترسی پیدا کنیم که محاسبات ما را کمتر شفاف می‌کند. -Mixing up the width and height wouldn’t matter for the area calculation, but if -we want to draw the rectangle on the screen, it would matter! We would have to -keep in mind that `width` is the tuple index `0` and `height` is the tuple -index `1`. This would be even harder for someone else to figure out and keep in -mind if they were to use our code. Because we haven’t conveyed the meaning of -our data in our code, it’s now easier to introduce errors. +اگر بخواهیم مستطیل را روی صفحه نمایش بکشیم، جابه‌جایی عرض و ارتفاع اهمیتی ندارد، اما برای رسم آن اهمیت پیدا می‌کند! ما باید به خاطر داشته باشیم که `width` ایندکس `0` تاپل و `height` ایندکس `1` تاپل است. این کار حتی برای کسی که از کد ما استفاده می‌کند سخت‌تر خواهد بود و به اشتباهات بیشتری منجر می‌شود. چون معنای داده‌های ما در کد مشخص نشده است، احتمال خطا بیشتر می‌شود. -### Refactoring with Structs: Adding More Meaning +### بازنویسی با استفاده از Structها: افزودن معنای بیشتر -We use structs to add meaning by labeling the data. We can transform the tuple -we’re using into a struct with a name for the whole as well as names for the -parts, as shown in Listing 5-10. +ما از ساختارها استفاده می‌کنیم تا با نام‌گذاری داده‌ها، معنای بیشتری به آن‌ها بدهیم. می‌توانیم تاپلی که استفاده می‌کنیم را به یک ساختار تبدیل کنیم که برای کل داده‌ها یک نام و همچنین برای بخش‌های مختلف آن نام‌هایی مشخص کنیم، همان‌طور که در لیست ۵-۱۰ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-10/src/main.rs}} @@ -78,35 +56,20 @@ parts, as shown in Listing 5-10. -Here, we’ve defined a struct and named it `Rectangle`. Inside the curly -brackets, we defined the fields as `width` and `height`, both of which have -type `u32`. Then, in `main`, we created a particular instance of `Rectangle` -that has a width of `30` and a height of `50`. +در این‌جا یک `struct` تعریف کرده‌ایم و نام آن را `Rectangle` گذاشته‌ایم. +درون آکولادها، فیلدهایی با نام‌های `width` و `height` تعریف کرده‌ایم +که هر دو دارای نوع `u32` هستند. سپس، در تابع `main`، یک نمونه خاص از `Rectangle` ایجاد کرده‌ایم +که `width` آن برابر با `30` و `height` آن برابر با `50` است. -Our `area` function is now defined with one parameter, which we’ve named -`rectangle`, whose type is an immutable borrow of a struct `Rectangle` -instance. As mentioned in Chapter 4, we want to borrow the struct rather than -take ownership of it. This way, `main` retains its ownership and can continue -using `rect1`, which is the reason we use the `&` in the function signature and -where we call the function. +تابع `area` ما اکنون با یک پارامتر تعریف شده است که آن را `rectangle` نامیده‌ایم و نوع آن یک ارجاع غیرقابل تغییر به یک نمونه از ساختار `Rectangle` است. همان‌طور که در فصل ۴ اشاره شد، ما می‌خواهیم ساختار را قرض بگیریم نه اینکه مالکیت آن را بگیریم. به این ترتیب، `main` مالکیت خود را حفظ می‌کند و می‌تواند همچنان از `rect1` استفاده کند. به همین دلیل است که از `&` در امضای تابع و در جایی که تابع را فراخوانی می‌کنیم استفاده می‌کنیم. -The `area` function accesses the `width` and `height` fields of the `Rectangle` -instance (note that accessing fields of a borrowed struct instance does not -move the field values, which is why you often see borrows of structs). Our -function signature for `area` now says exactly what we mean: calculate the area -of `Rectangle`, using its `width` and `height` fields. This conveys that the -width and height are related to each other, and it gives descriptive names to -the values rather than using the tuple index values of `0` and `1`. This is a -win for clarity. +تابع `area` به فیلدهای `width` و `height` در نمونه `Rectangle` دسترسی پیدا می‌کند (توجه داشته باشید که دسترسی به فیلدهای یک نمونه قرض‌گرفته‌شده باعث انتقال مقادیر فیلدها نمی‌شود، به همین دلیل است که اغلب قرض‌گیری ساختارها را مشاهده می‌کنید). امضای تابع `area` ما اکنون دقیقاً همان چیزی را می‌گوید که منظور ماست: مساحت `Rectangle` را با استفاده از فیلدهای `width` و `height` آن محاسبه کن. این کار نشان می‌دهد که عرض و ارتفاع به یکدیگر مرتبط هستند و نام‌های توصیفی به مقادیر می‌دهد، به جای استفاده از مقادیر ایندکس تاپل‌ها مانند `0` و `1`. این یک پیروزی برای شفافیت است. -### Adding Useful Functionality with Derived Traits +### افزودن قابلیت‌های مفید با Traits مشتق‌شده -It’d be useful to be able to print an instance of `Rectangle` while we’re -debugging our program and see the values for all its fields. Listing 5-11 tries -using the [`println!` macro][println] as we have used in -previous chapters. This won’t work, however. +زمانی که در حال اشکال‌زدایی برنامه خود هستیم، مفید است که بتوانیم نمونه‌ای از `Rectangle` را چاپ کرده و مقادیر تمام فیلدهای آن را ببینیم. لیست ۵-۱۱ تلاش می‌کند با استفاده از ماکروی [`println!`][println] که در فصل‌های قبلی استفاده کرده‌ایم، این کار را انجام دهد. با این حال، این کار موفق نخواهد بود. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/src/main.rs}} @@ -114,53 +77,37 @@ previous chapters. This won’t work, however. -When we compile this code, we get an error with this core message: +وقتی این کد را کامپایل می‌کنیم، با خطایی مواجه می‌شویم که پیام اصلی آن به این صورت است: ```text {{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/output.txt:3}} ``` -The `println!` macro can do many kinds of formatting, and by default, the curly -brackets tell `println!` to use formatting known as `Display`: output intended -for direct end user consumption. The primitive types we’ve seen so far -implement `Display` by default because there’s only one way you’d want to show -a `1` or any other primitive type to a user. But with structs, the way -`println!` should format the output is less clear because there are more -display possibilities: Do you want commas or not? Do you want to print the -curly brackets? Should all the fields be shown? Due to this ambiguity, Rust -doesn’t try to guess what we want, and structs don’t have a provided -implementation of `Display` to use with `println!` and the `{}` placeholder. +ماکروی `println!` می‌تواند بسیاری از انواع فرمت‌بندی را انجام دهد، و به صورت پیش‌فرض، آکولادها به `println!` می‌گویند که از فرمت‌بندی‌ای که به نام `Display` شناخته می‌شود استفاده کند: خروجی‌ای که برای مصرف مستقیم کاربر نهایی در نظر گرفته شده است. انواع ابتدایی که تاکنون دیده‌ایم به صورت پیش‌فرض ویژگی `Display` را پیاده‌سازی می‌کنند زیرا تنها یک روش برای نمایش یک مقدار مانند `1` یا هر نوع ابتدایی دیگری به کاربر وجود دارد. اما با ساختارها، روش فرمت‌بندی خروجی کمتر واضح است زیرا امکانات بیشتری برای نمایش وجود دارد: آیا می‌خواهید از ویرگول استفاده شود یا خیر؟ آیا می‌خواهید آکولادها چاپ شوند؟ آیا تمام فیلدها باید نشان داده شوند؟ به دلیل این ابهام، Rust سعی نمی‌کند حدس بزند که ما چه می‌خواهیم، و ساختارها پیاده‌سازی‌ای برای `Display` ندارند که بتوان با `println!` و جایگزین `{}` استفاده کرد. -If we continue reading the errors, we’ll find this helpful note: +اگر به خواندن خطاها ادامه دهیم، به این یادداشت مفید خواهیم رسید: ```text {{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/output.txt:9:10}} ``` -Let’s try it! The `println!` macro call will now look like `println!("rect1 is -{rect1:?}");`. Putting the specifier `:?` inside the curly brackets tells -`println!` we want to use an output format called `Debug`. The `Debug` trait -enables us to print our struct in a way that is useful for developers so we can -see its value while we’re debugging our code. +بیایید آن را امتحان کنیم! اکنون فراخوانی ماکروی `println!` به صورت `println!("rect1 is {rect1:?}");` خواهد بود. قرار دادن مشخص‌کننده `:?` داخل آکولادها به `println!` می‌گوید که می‌خواهیم از یک فرمت خروجی به نام `Debug` استفاده کنیم. ویژگی `Debug` به ما اجازه می‌دهد تا ساختار خود را به روشی که برای توسعه‌دهندگان مفید است چاپ کنیم تا مقدار آن را هنگام اشکال‌زدایی کد خود ببینیم. -Compile the code with this change. Drat! We still get an error: +کد را با این تغییر کامپایل کنید. خب، باز هم یک خطا دریافت می‌کنیم: ```text {{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-01-debug/output.txt:3}} ``` -But again, the compiler gives us a helpful note: +اما باز هم کامپایلر یادداشتی مفید به ما می‌دهد: ```text {{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-01-debug/output.txt:9:10}} ``` -Rust _does_ include functionality to print out debugging information, but we -have to explicitly opt in to make that functionality available for our struct. -To do that, we add the outer attribute `#[derive(Debug)]` just before the -struct definition, as shown in Listing 5-12. +Rust _در واقع_ قابلیت چاپ اطلاعات اشکال‌زدایی را دارد، اما باید به صورت صریح این قابلیت را برای ساختار خود فعال کنیم. برای انجام این کار، ویژگی بیرونی `#[derive(Debug)]` را دقیقاً قبل از تعریف ساختار اضافه می‌کنیم، همان‌طور که در لیست ۵-۱۲ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/src/main.rs}} @@ -168,77 +115,43 @@ struct definition, as shown in Listing 5-12. -Now when we run the program, we won’t get any errors, and we’ll see the -following output: +اکنون وقتی برنامه را اجرا می‌کنیم، هیچ خطایی دریافت نخواهیم کرد و خروجی زیر را خواهیم دید: ```console {{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/output.txt}} ``` -Nice! It’s not the prettiest output, but it shows the values of all the fields -for this instance, which would definitely help during debugging. When we have -larger structs, it’s useful to have output that’s a bit easier to read; in -those cases, we can use `{:#?}` instead of `{:?}` in the `println!` string. In -this example, using the `{:#?}` style will output the following: +عالی! این خروجی ممکن است زیباترین نباشد، اما مقادیر تمام فیلدها را برای این نمونه نشان می‌دهد که قطعاً در هنگام اشکال‌زدایی کمک می‌کند. زمانی که ساختارهای بزرگ‌تری داریم، مفید است که خروجی کمی آسان‌تر خوانده شود؛ در چنین مواردی می‌توانیم به جای `{:?}` از `{:#?}` در رشته `println!` استفاده کنیم. در این مثال، استفاده از سبک `{:#?}` خروجی زیر را ایجاد خواهد کرد: ```console {{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-02-pretty-debug/output.txt}} ``` -Another way to print out a value using the `Debug` format is to use the [`dbg!` -macro][dbg], which takes ownership of an expression (as opposed -to `println!`, which takes a reference), prints the file and line number of -where that `dbg!` macro call occurs in your code along with the resultant value -of that expression, and returns ownership of the value. +روش دیگر برای چاپ مقدار با استفاده از فرمت `Debug`، استفاده از ماکروی [`dbg!`][dbg] است که مالکیت یک عبارت را می‌گیرد (برخلاف `println!`، که ارجاع می‌گیرد)، فایل و شماره خطی که فراخوانی `dbg!` در آن اتفاق می‌افتد همراه با مقدار حاصل از آن عبارت را چاپ می‌کند و مالکیت مقدار را بازمی‌گرداند. -> Note: Calling the `dbg!` macro prints to the standard error console stream -> (`stderr`), as opposed to `println!`, which prints to the standard output -> console stream (`stdout`). We’ll talk more about `stderr` and `stdout` in the -> [“Writing Error Messages to Standard Error Instead of Standard Output” -> section in Chapter 12][err]. -Here’s an example where we’re interested in the value that gets assigned to the -`width` field, as well as the value of the whole struct in `rect1`: +Here is the continuation of the translation for **"ch05-02-example-structs.md"** into Persian: + +در اینجا مثالی آورده شده است که در آن ما به مقدار اختصاص داده شده به فیلد `width` و همچنین مقدار کل ساختار در `rect1` علاقه‌مند هستیم: ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/src/main.rs}} ``` -We can put `dbg!` around the expression `30 * scale` and, because `dbg!` -returns ownership of the expression’s value, the `width` field will get the -same value as if we didn’t have the `dbg!` call there. We don’t want `dbg!` to -take ownership of `rect1`, so we use a reference to `rect1` in the next call. -Here’s what the output of this example looks like: +ما می‌توانیم `dbg!` را در اطراف عبارت `30 * scale` قرار دهیم و چون `dbg!` مالکیت مقدار عبارت را بازمی‌گرداند، فیلد `width` همان مقداری را خواهد داشت که اگر فراخوانی `dbg!` در آنجا وجود نداشت. ما نمی‌خواهیم `dbg!` مالکیت `rect1` را بگیرد، بنابراین از یک ارجاع به `rect1` در فراخوانی بعدی استفاده می‌کنیم. در اینجا خروجی این مثال آورده شده است: ```console {{#include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/output.txt}} ``` -We can see the first bit of output came from _src/main.rs_ line 10 where we’re -debugging the expression `30 * scale`, and its resultant value is `60` (the -`Debug` formatting implemented for integers is to print only their value). The -`dbg!` call on line 14 of _src/main.rs_ outputs the value of `&rect1`, which is -the `Rectangle` struct. This output uses the pretty `Debug` formatting of the -`Rectangle` type. The `dbg!` macro can be really helpful when you’re trying to -figure out what your code is doing! - -In addition to the `Debug` trait, Rust has provided a number of traits for us -to use with the `derive` attribute that can add useful behavior to our custom -types. Those traits and their behaviors are listed in [Appendix C][app-c]. We’ll cover how to implement these traits with custom behavior as -well as how to create your own traits in Chapter 10. There are also many -attributes other than `derive`; for more information, see [the “Attributes” -section of the Rust Reference][attributes]. - -Our `area` function is very specific: it only computes the area of rectangles. -It would be helpful to tie this behavior more closely to our `Rectangle` struct -because it won’t work with any other type. Let’s look at how we can continue to -refactor this code by turning the `area` function into an `area` _method_ -defined on our `Rectangle` type. +می‌توانیم ببینیم که اولین بخش خروجی از خط ۱۰ در _src/main.rs_ آمده است، جایی که ما در حال اشکال‌زدایی عبارت `30 * scale` هستیم، و مقدار حاصل آن `60` است (فرمت‌بندی `Debug` که برای اعداد صحیح پیاده‌سازی شده است فقط مقدار آن‌ها را چاپ می‌کند). فراخوانی `dbg!` در خط ۱۴ از _src/main.rs_ مقدار `&rect1` را چاپ می‌کند که ساختار `Rectangle` است. این خروجی از فرمت‌بندی زیبا و مفید `Debug` برای نوع `Rectangle` استفاده می‌کند. ماکروی `dbg!` می‌تواند در هنگام تلاش برای درک رفتار کدتان بسیار مفید باشد! + +علاوه بر ویژگی `Debug`، Rust تعدادی ویژگی برای ما فراهم کرده است که می‌توانیم با استفاده از ویژگی `derive` آن‌ها را به نوع‌های سفارشی خود اضافه کنیم و رفتار مفیدی ارائه دهند. این ویژگی‌ها و رفتار آن‌ها در [ضمیمه ج][app-c] فهرست شده‌اند. ما در فصل ۱۰ به نحوه پیاده‌سازی این ویژگی‌ها با رفتار سفارشی و همچنین نحوه ایجاد ویژگی‌های خود می‌پردازیم. همچنین بسیاری از ویژگی‌های دیگر به غیر از `derive` وجود دارند؛ برای اطلاعات بیشتر، به [بخش «ویژگی‌ها» در مرجع Rust][attributes] مراجعه کنید. + +تابع `area` ما بسیار خاص است: فقط مساحت مستطیل‌ها را محاسبه می‌کند. مفید خواهد بود اگر این رفتار را به صورت نزدیک‌تر با ساختار `Rectangle` مرتبط کنیم، زیرا این تابع با هیچ نوع دیگری کار نخواهد کرد. بیایید ببینیم که چگونه می‌توانیم با تبدیل تابع `area` به یک _متد_ که برای نوع `Rectangle` تعریف شده است، این کد را بازنویسی کنیم. [the-tuple-type]: ch03-02-data-types.html#the-tuple-type [app-c]: appendix-03-derivable-traits.md -[println]: ../std/macro.println.html -[dbg]: ../std/macro.dbg.html -[err]: ch12-06-writing-to-stderr-instead-of-stdout.html -[attributes]: ../reference/attributes.html +[println]: https://doc.rust-lang.org/std/macro.println.html +[dbg]: https://doc.rust-lang.org/std/macro.dbg.html +[attributes]: ../reference/attributes.html \ No newline at end of file diff --git a/src/ch05-03-method-syntax.md b/src/ch05-03-method-syntax.md index ab92d19ac0..fcdef0521a 100644 --- a/src/ch05-03-method-syntax.md +++ b/src/ch05-03-method-syntax.md @@ -1,21 +1,17 @@ -## Method Syntax +## متد -_Methods_ are similar to functions: we declare them with the `fn` keyword and a -name, they can have parameters and a return value, and they contain some code -that’s run when the method is called from somewhere else. Unlike functions, -methods are defined within the context of a struct (or an enum or a trait -object, which we cover in [Chapter 6][enums] and [Chapter -18][trait-objects], respectively), and their first parameter is -always `self`, which represents the instance of the struct the method is being -called on. +_متدها_ شبیه به توابع هستند: آن‌ها را با کلیدواژه‌ی `fn` و یک نام تعریف می‌کنیم، +می‌توانند پارامتر و مقدار بازگشتی داشته باشند، و شامل مقداری کد هستند +که هنگام فراخوانی متد از جایی دیگر اجرا می‌شود. برخلاف توابع، +متدها در بستر یک `struct` (یا یک `enum` یا یک `trait object` که به ترتیب در [فصل ۶][enums] و [فصل ۱۸][trait-objects] بررسی می‌شوند) تعریف می‌شوند، +و اولین پارامتر آن‌ها همیشه `self` است، که نشان‌دهنده‌ی نمونه‌ای از `struct` است +که متد روی آن فراخوانی شده است. -### Defining Methods +### تعریف متدها -Let’s change the `area` function that has a `Rectangle` instance as a parameter -and instead make an `area` method defined on the `Rectangle` struct, as shown -in Listing 5-13. +بیایید تابع `area` که یک نمونه از `Rectangle` را به عنوان پارامتر می‌گیرد، تغییر دهیم و به جای آن، یک متد `area` تعریف کنیم که روی ساختار `Rectangle` تعریف شده است، همان‌طور که در لیست ۵-۱۳ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-13/src/main.rs}} @@ -23,46 +19,15 @@ in Listing 5-13. -To define the function within the context of `Rectangle`, we start an `impl` -(implementation) block for `Rectangle`. Everything within this `impl` block -will be associated with the `Rectangle` type. Then we move the `area` function -within the `impl` curly brackets and change the first (and in this case, only) -parameter to be `self` in the signature and everywhere within the body. In -`main`, where we called the `area` function and passed `rect1` as an argument, -we can instead use _method syntax_ to call the `area` method on our `Rectangle` -instance. The method syntax goes after an instance: we add a dot followed by -the method name, parentheses, and any arguments. - -In the signature for `area`, we use `&self` instead of `rectangle: &Rectangle`. -The `&self` is actually short for `self: &Self`. Within an `impl` block, the -type `Self` is an alias for the type that the `impl` block is for. Methods must -have a parameter named `self` of type `Self` for their first parameter, so Rust -lets you abbreviate this with only the name `self` in the first parameter spot. -Note that we still need to use the `&` in front of the `self` shorthand to -indicate that this method borrows the `Self` instance, just as we did in -`rectangle: &Rectangle`. Methods can take ownership of `self`, borrow `self` -immutably, as we’ve done here, or borrow `self` mutably, just as they can any -other parameter. - -We chose `&self` here for the same reason we used `&Rectangle` in the function -version: we don’t want to take ownership, and we just want to read the data in -the struct, not write to it. If we wanted to change the instance that we’ve -called the method on as part of what the method does, we’d use `&mut self` as -the first parameter. Having a method that takes ownership of the instance by -using just `self` as the first parameter is rare; this technique is usually -used when the method transforms `self` into something else and you want to -prevent the caller from using the original instance after the transformation. - -The main reason for using methods instead of functions, in addition to -providing method syntax and not having to repeat the type of `self` in every -method’s signature, is for organization. We’ve put all the things we can do -with an instance of a type in one `impl` block rather than making future users -of our code search for capabilities of `Rectangle` in various places in the -library we provide. - -Note that we can choose to give a method the same name as one of the struct’s -fields. For example, we can define a method on `Rectangle` that is also named -`width`: +برای تعریف تابع در زمینه `Rectangle`، یک بلوک `impl` (پیاده‌سازی) برای `Rectangle` شروع می‌کنیم. هر چیزی در این بلوک `impl` با نوع `Rectangle` مرتبط خواهد بود. سپس، تابع `area` را به درون آکولادهای `impl` منتقل کرده و اولین (و در اینجا تنها) پارامتر آن را در امضا و در هر جایی در بدنه به `self` تغییر می‌دهیم. در `main`، جایی که تابع `area` را فراخوانی می‌کردیم و `rect1` را به عنوان آرگومان ارسال می‌کردیم، اکنون می‌توانیم از _نحو متد_ برای فراخوانی متد `area` روی نمونه `Rectangle` خود استفاده کنیم. نحو متد بعد از یک نمونه قرار می‌گیرد: نقطه‌ای اضافه می‌کنیم و به دنبال آن نام متد، پرانتزها و هر آرگومان دیگری قرار می‌دهیم. + +در امضای `area`، از `&self` به جای `rectangle: &Rectangle` استفاده می‌کنیم. `&self` در واقع معادل کوتاه‌شده‌ای از `self: &Self` است. درون یک بلوک `impl`، نوع `Self` نام مستعاری برای نوعی است که بلوک `impl` برای آن تعریف شده است. متدها باید به عنوان پارامتر اول خود یک پارامتری به نام `self` از نوع `Self` داشته باشند، بنابراین Rust به شما اجازه می‌دهد این عبارت را با فقط نوشتن `self` در محل اولین پارامتر کوتاه کنید. توجه داشته باشید که همچنان باید از `&` در مقابل اختصار `self` استفاده کنیم تا نشان دهیم که این متد نمونه `Self` را قرض می‌گیرد، دقیقاً همان‌طور که در `rectangle: &Rectangle` استفاده می‌کردیم. متدها می‌توانند مالکیت `self` را بگیرند، `self` را به صورت غیرقابل تغییر قرض بگیرند، همان‌طور که در اینجا انجام داده‌ایم، یا `self` را به صورت قابل تغییر قرض بگیرند، دقیقاً مانند هر پارامتر دیگری. + +ما در اینجا `&self` را انتخاب کرده‌ایم به همان دلیلی که در نسخه تابع از `&Rectangle` استفاده کردیم: ما نمی‌خواهیم مالکیت را بگیریم و فقط می‌خواهیم داده‌ها را در ساختار بخوانیم، نه اینکه آن‌ها را تغییر دهیم. اگر بخواهیم نمونه‌ای که متد روی آن فراخوانی شده است را به عنوان بخشی از کاری که متد انجام می‌دهد تغییر دهیم، به عنوان پارامتر اول از `&mut self` استفاده می‌کنیم. داشتن متدی که مالکیت نمونه را می‌گیرد با استفاده از فقط `self` به عنوان پارامتر اول به ندرت اتفاق می‌افتد؛ این تکنیک معمولاً زمانی استفاده می‌شود که متد `self` را به چیز دیگری تبدیل کند و شما بخواهید از استفاده از نمونه اصلی پس از تبدیل جلوگیری کنید. + +دلیل اصلی استفاده از متدها به جای توابع، علاوه بر ارائه نحو متد و عدم نیاز به تکرار نوع `self` در امضای هر متد، سازمان‌دهی است. ما تمام کارهایی که می‌توانیم با یک نمونه از یک نوع انجام دهیم را در یک بلوک `impl` قرار داده‌ایم، به جای اینکه کاربران آینده کد ما به دنبال قابلیت‌های `Rectangle` در مکان‌های مختلف در کتابخانه‌ای که ارائه می‌دهیم بگردند. + +توجه داشته باشید که می‌توانیم تصمیم بگیریم متدی با همان نام یک فیلد ساختار تعریف کنیم. برای مثال، می‌توانیم متدی روی `Rectangle` تعریف کنیم که نام آن نیز `width` باشد: @@ -72,37 +37,19 @@ fields. For example, we can define a method on `Rectangle` that is also named -Here, we’re choosing to make the `width` method return `true` if the value in -the instance’s `width` field is greater than `0` and `false` if the value is -`0`: we can use a field within a method of the same name for any purpose. In -`main`, when we follow `rect1.width` with parentheses, Rust knows we mean the -method `width`. When we don’t use parentheses, Rust knows we mean the field -`width`. - -Often, but not always, when we give a method the same name as a field we want -it to only return the value in the field and do nothing else. Methods like this -are called _getters_, and Rust does not implement them automatically for struct -fields as some other languages do. Getters are useful because you can make the -field private but the method public, and thus enable read-only access to that -field as part of the type’s public API. We will discuss what public and private -are and how to designate a field or method as public or private in [Chapter -7][public]. - -> ### Where’s the `->` Operator? +Here is the continuation of the translation for **"ch05-03-method-syntax.md"** into Persian: + +در اینجا ما تصمیم گرفته‌ایم متد `width` را طوری تعریف کنیم که اگر مقدار در فیلد `width` نمونه بزرگ‌تر از `0` باشد مقدار `true` و در غیر این صورت مقدار `false` برگرداند: ما می‌توانیم از یک فیلد درون یک متد با همان نام برای هر منظوری استفاده کنیم. در `main`، وقتی که ما `rect1.width` را با پرانتز دنبال می‌کنیم، Rust می‌داند که منظور ما متد `width` است. وقتی از پرانتز استفاده نمی‌کنیم، Rust می‌داند که منظور ما فیلد `width` است. + +اغلب، اما نه همیشه، زمانی که به یک متد نامی مشابه یک فیلد می‌دهیم، می‌خواهیم که این متد تنها مقدار موجود در فیلد را بازگرداند و هیچ کار دیگری انجام ندهد. متدهایی مانند این‌ها _getter_ نامیده می‌شوند، و Rust آن‌ها را به صورت خودکار برای فیلدهای ساختار پیاده‌سازی نمی‌کند، همان‌طور که برخی از زبان‌های دیگر انجام می‌دهند. Getterها مفید هستند زیرا می‌توانید فیلد را خصوصی کنید اما متد را عمومی کنید و به این ترتیب دسترسی فقط-خواندنی به آن فیلد را به عنوان بخشی از API عمومی نوع فعال کنید. ما در فصل [۷][public] در مورد عمومی و خصوصی بودن و چگونگی تعیین عمومی یا خصوصی بودن یک فیلد یا متد بحث خواهیم کرد. + +> ### کجاست عملگر `->`؟ > -> In C and C++, two different operators are used for calling methods: you use -> `.` if you’re calling a method on the object directly and `->` if you’re -> calling the method on a pointer to the object and need to dereference the -> pointer first. In other words, if `object` is a pointer, -> `object->something()` is similar to `(*object).something()`. +> در C و C++، دو عملگر مختلف برای فراخوانی متدها استفاده می‌شود: شما از `.` استفاده می‌کنید اگر متد را روی خود شیء فراخوانی می‌کنید و از `->` اگر متد را روی یک اشاره‌گر (Pointer) به شیء فراخوانی می‌کنید و نیاز دارید ابتدا اشاره‌گر (Pointer) را اشاره‌برداری کنید. به عبارت دیگر، اگر `object` یک اشاره‌گر (Pointer) باشد، `object->something()` شبیه به `(*object).something()` است. > -> Rust doesn’t have an equivalent to the `->` operator; instead, Rust has a -> feature called _automatic referencing and dereferencing_. Calling methods is -> one of the few places in Rust with this behavior. +> Rust معادل عملگر `->` را ندارد؛ به جای آن، Rust یک ویژگی به نام _ارجاع‌دهی و اشاره‌برداری خودکار_ دارد. فراخوانی متدها یکی از معدود مکان‌هایی در Rust است که این رفتار را دارد. > -> Here’s how it works: when you call a method with `object.something()`, Rust -> automatically adds in `&`, `&mut`, or `*` so `object` matches the signature of -> the method. In other words, the following are the same: +> این‌گونه کار می‌کند: وقتی یک متد را با `object.something()` فراخوانی می‌کنید، Rust به طور خودکار `&`، `&mut` یا `*` را اضافه می‌کند تا `object` با امضای متد مطابقت داشته باشد. به عبارت دیگر، موارد زیر یکسان هستند: > > > @@ -127,23 +74,13 @@ are and how to designate a field or method as public or private in [Chapter > (&p1).distance(&p2); > ``` > -> The first one looks much cleaner. This automatic referencing behavior works -> because methods have a clear receiver—the type of `self`. Given the receiver -> and name of a method, Rust can figure out definitively whether the method is -> reading (`&self`), mutating (`&mut self`), or consuming (`self`). The fact -> that Rust makes borrowing implicit for method receivers is a big part of -> making ownership ergonomic in practice. +> اولین مورد خیلی تمیزتر به نظر می‌رسد. این رفتار ارجاع‌دهی خودکار کار می‌کند زیرا متدها یک گیرنده واضح دارند—نوع `self`. با توجه به گیرنده و نام یک متد، Rust می‌تواند به طور قطعی تعیین کند که آیا متد در حال خواندن (`&self`)، تغییر (`&mut self`) یا مصرف (`self`) است. این واقعیت که Rust قرض‌گیری را برای گیرنده‌های متد ضمنی می‌کند، بخش بزرگی از راحتی کار با مالکیت در عمل است. -### Methods with More Parameters +### متدهایی با پارامترهای بیشتر -Let’s practice using methods by implementing a second method on the `Rectangle` -struct. This time we want an instance of `Rectangle` to take another instance -of `Rectangle` and return `true` if the second `Rectangle` can fit completely -within `self` (the first `Rectangle`); otherwise, it should return `false`. -That is, once we’ve defined the `can_hold` method, we want to be able to write -the program shown in Listing 5-14. +بیایید با تعریف یک متد دیگر روی ساختار `Rectangle` تمرین کنیم. این بار می‌خواهیم یک نمونه از `Rectangle` نمونه دیگری از `Rectangle` را بگیرد و مقدار `true` برگرداند اگر `Rectangle` دوم کاملاً در `self` (اولین `Rectangle`) جای گیرد؛ در غیر این صورت مقدار `false` برگرداند. به عبارت دیگر، پس از تعریف متد `can_hold`، می‌خواهیم بتوانیم برنامه‌ای بنویسیم که در لیست ۵-۱۴ نشان داده شده است. -+ ```rust,ignore {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-14/src/main.rs}} @@ -151,30 +88,16 @@ the program shown in Listing 5-14. -The expected output would look like the following because both dimensions of -`rect2` are smaller than the dimensions of `rect1`, but `rect3` is wider than -`rect1`: +خروجی مورد انتظار به صورت زیر خواهد بود زیرا هر دو بُعد `rect2` کوچکتر از ابعاد `rect1` هستند، اما `rect3` از `rect1` عریض‌تر است: ```text Can rect1 hold rect2? true Can rect1 hold rect3? false ``` -We know we want to define a method, so it will be within the `impl Rectangle` -block. The method name will be `can_hold`, and it will take an immutable borrow -of another `Rectangle` as a parameter. We can tell what the type of the -parameter will be by looking at the code that calls the method: -`rect1.can_hold(&rect2)` passes in `&rect2`, which is an immutable borrow to -`rect2`, an instance of `Rectangle`. This makes sense because we only need to -read `rect2` (rather than write, which would mean we’d need a mutable borrow), -and we want `main` to retain ownership of `rect2` so we can use it again after -calling the `can_hold` method. The return value of `can_hold` will be a -Boolean, and the implementation will check whether the width and height of -`self` are greater than the width and height of the other `Rectangle`, -respectively. Let’s add the new `can_hold` method to the `impl` block from -Listing 5-13, shown in Listing 5-15. - -+ما می‌دانیم که می‌خواهیم یک متد تعریف کنیم، بنابراین این متد در بلوک `impl Rectangle` خواهد بود. نام متد `can_hold` خواهد بود و یک قرض غیرقابل تغییر از یک `Rectangle` دیگر به عنوان پارامتر خواهد گرفت. می‌توانیم نوع پارامتر را با نگاه به کدی که متد را فراخوانی می‌کند تشخیص دهیم: `rect1.can_hold(&rect2)` مقدار `&rect2` را ارسال می‌کند، که یک قرض غیرقابل تغییر به `rect2`، یک نمونه از `Rectangle` است. این منطقی است زیرا ما فقط نیاز به خواندن `rect2` داریم (نه نوشتن، که به یک قرض قابل تغییر نیاز داشت) و می‌خواهیم مالکیت `rect2` در `main` باقی بماند تا بتوانیم پس از فراخوانی متد `can_hold` دوباره از آن استفاده کنیم. مقدار بازگشتی `can_hold` یک مقدار بولی خواهد بود و پیاده‌سازی بررسی می‌کند که آیا عرض و ارتفاع `self` به ترتیب بزرگ‌تر از عرض و ارتفاع `Rectangle` دیگر هستند. بیایید متد جدید `can_hold` را به بلوک `impl` از لیست ۵-۱۳ اضافه کنیم، همان‌طور که در لیست ۵-۱۵ نشان داده شده است. + + ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-15/src/main.rs:here}} @@ -182,27 +105,16 @@ Listing 5-13, shown in Listing 5-15. -When we run this code with the `main` function in Listing 5-14, we’ll get our -desired output. Methods can take multiple parameters that we add to the -signature after the `self` parameter, and those parameters work just like -parameters in functions. -### Associated Functions +Here is the continuation of the translation for **"ch05-03-method-syntax.md"** into Persian: + +وقتی این کد را با تابع `main` موجود در لیست ۵-۱۴ اجرا می‌کنیم، خروجی دلخواه را دریافت خواهیم کرد. متدها می‌توانند چندین پارامتر بگیرند که ما آن‌ها را پس از پارامتر `self` به امضا اضافه می‌کنیم، و این پارامترها همانند پارامترهای توابع عمل می‌کنند. + +### توابع مرتبط -All functions defined within an `impl` block are called _associated functions_ -because they’re associated with the type named after the `impl`. We can define -associated functions that don’t have `self` as their first parameter (and thus -are not methods) because they don’t need an instance of the type to work with. -We’ve already used one function like this: the `String::from` function that’s -defined on the `String` type. +تمام توابعی که در یک بلوک `impl` تعریف شده‌اند _توابع مرتبط_ نامیده می‌شوند، زیرا با نوعی که بعد از `impl` نام‌گذاری شده است، مرتبط هستند. ما می‌توانیم توابع مرتبطی را تعریف کنیم که `self` را به عنوان اولین پارامتر خود ندارند (و بنابراین متد نیستند) زیرا نیازی به کار با یک نمونه از نوع ندارند. ما قبلاً از یک تابع مشابه استفاده کرده‌ایم: تابع `String::from` که روی نوع `String` تعریف شده است. -Associated functions that aren’t methods are often used for constructors that -will return a new instance of the struct. These are often called `new`, but -`new` isn’t a special name and isn’t built into the language. For example, we -could choose to provide an associated function named `square` that would have -one dimension parameter and use that as both width and height, thus making it -easier to create a square `Rectangle` rather than having to specify the same -value twice: +توابع مرتبطی که متد نیستند اغلب برای سازنده‌ها استفاده می‌شوند که نمونه جدیدی از ساختار را بازمی‌گردانند. این توابع معمولاً `new` نامیده می‌شوند، اما `new` یک نام خاص نیست و در زبان به صورت داخلی تعریف نشده است. برای مثال، ما می‌توانیم تصمیم بگیریم تابع مرتبطی به نام `square` ارائه دهیم که یک پارامتر برای ابعاد بگیرد و از آن به عنوان عرض و ارتفاع استفاده کند، بنابراین ایجاد یک `Rectangle` مربعی را آسان‌تر می‌کند به جای اینکه مجبور باشیم مقدار یکسان را دو بار مشخص کنیم: Filename: src/main.rs @@ -210,23 +122,15 @@ value twice: {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-03-associated-functions/src/main.rs:here}} ``` -The `Self` keywords in the return type and in the body of the function are -aliases for the type that appears after the `impl` keyword, which in this case -is `Rectangle`. +کلمات کلیدی `Self` در نوع بازگشتی و در بدنه تابع، نام مستعاری برای نوعی هستند که بعد از کلمه کلیدی `impl` ظاهر می‌شود، که در اینجا `Rectangle` است. -To call this associated function, we use the `::` syntax with the struct name; -`let sq = Rectangle::square(3);` is an example. This function is namespaced by -the struct: the `::` syntax is used for both associated functions and -namespaces created by modules. We’ll discuss modules in [Chapter -7][modules]. +برای فراخوانی این تابع مرتبط، از نحو `::` همراه با نام ساختار استفاده می‌کنیم؛ برای مثال: `let sq = Rectangle::square(3);`. این تابع با ساختار فضای نام‌گذاری شده است: نحو `::` برای توابع مرتبط و فضای نام‌های ایجاد شده توسط ماژول‌ها استفاده می‌شود. ما ماژول‌ها را در [فصل ۷][modules] بررسی خواهیم کرد. -### Multiple `impl` Blocks +### بلوک‌های متعدد `impl` -Each struct is allowed to have multiple `impl` blocks. For example, Listing -5-15 is equivalent to the code shown in Listing 5-16, which has each method in -its own `impl` block. +هر ساختار اجازه دارد چندین بلوک `impl` داشته باشد. برای مثال، لیست ۵-۱۵ معادل کدی است که در لیست ۵-۱۶ نشان داده شده است، که هر متد در بلوک `impl` خود قرار دارد. -+ ```rust {{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-16/src/main.rs:here}} @@ -234,21 +138,13 @@ its own `impl` block. -There’s no reason to separate these methods into multiple `impl` blocks here, -but this is valid syntax. We’ll see a case in which multiple `impl` blocks are -useful in Chapter 10, where we discuss generic types and traits. +هیچ دلیلی برای جدا کردن این متدها به بلوک‌های متعدد `impl` در اینجا وجود ندارد، اما این یک نحو معتبر است. ما در فصل ۱۰ موردی را خواهیم دید که در آن بلوک‌های متعدد `impl` مفید هستند، جایی که ما نوع‌های عمومی و ویژگی‌ها را بررسی خواهیم کرد. -## Summary +## خلاصه -Structs let you create custom types that are meaningful for your domain. By -using structs, you can keep associated pieces of data connected to each other -and name each piece to make your code clear. In `impl` blocks, you can define -functions that are associated with your type, and methods are a kind of -associated function that let you specify the behavior that instances of your -structs have. +ساختارها به شما اجازه می‌دهند تا نوع‌های سفارشی ایجاد کنید که برای حوزه کاری شما معنادار باشند. با استفاده از ساختارها، می‌توانید قطعات داده‌ای مرتبط را به هم متصل کنید و برای هر قطعه نامی تعیین کنید تا کد شما شفاف شود. در بلوک‌های `impl`، شما می‌توانید توابعی را تعریف کنید که با نوع شما مرتبط هستند، و متدها نوعی از توابع مرتبط هستند که به شما اجازه می‌دهند رفتار نمونه‌های ساختارهایتان را مشخص کنید. -But structs aren’t the only way you can create custom types: let’s turn to -Rust’s enum feature to add another tool to your toolbox. +اما ساختارها تنها راه ایجاد نوع‌های سفارشی نیستند: بیایید به ویژگی Enum در Rust بپردازیم تا ابزار دیگری به جعبه ابزار شما اضافه کنیم. [enums]: ch06-00-enums.html [trait-objects]: ch18-02-trait-objects.md diff --git a/src/ch06-00-enums.md b/src/ch06-00-enums.md index 0b9fc7806c..69cf50ae43 100644 --- a/src/ch06-00-enums.md +++ b/src/ch06-00-enums.md @@ -1,11 +1,8 @@ -# Enums and Pattern Matching +# شمارنده‌ها و تطابق الگو -In this chapter, we’ll look at _enumerations_, also referred to as _enums_. -Enums allow you to define a type by enumerating its possible _variants_. First -we’ll define and use an enum to show how an enum can encode meaning along with -data. Next, we’ll explore a particularly useful enum, called `Option`, which -expresses that a value can be either something or nothing. Then we’ll look at -how pattern matching in the `match` expression makes it easy to run different -code for different values of an enum. Finally, we’ll cover how the `if let` -construct is another convenient and concise idiom available to handle enums in -your code. +در این فصل، به *شمارنده‌ها* که همچنین به عنوان *enums* شناخته می‌شوند، می‌پردازیم. شمارنده‌ها به شما اجازه می‌دهند +یک نوع را با شمردن *مقادیر ممکن* آن تعریف کنید. ابتدا، یک شمارنده تعریف کرده و از آن استفاده می‌کنیم تا نشان دهیم چگونه +شمارنده می‌تواند معنی را همراه با داده کدگذاری کند. سپس، به شمارنده‌ای بسیار مفید به نام `Option` خواهیم پرداخت که +بیان می‌کند یک مقدار می‌تواند *چیزی* باشد یا *هیچ چیز*. بعد، بررسی خواهیم کرد که چگونه تطابق الگو در عبارت `match` +باعث می‌شود اجرای کد مختلف برای مقادیر مختلف یک شمارنده آسان شود. در نهایت، پوشش خواهیم داد که ساختار `if let` چگونه +ایده‌آل و مختصر برای مدیریت شمارنده‌ها در کد شما است. diff --git a/src/ch06-01-defining-an-enum.md b/src/ch06-01-defining-an-enum.md index 4e1443c703..3d1933a11c 100644 --- a/src/ch06-01-defining-an-enum.md +++ b/src/ch06-01-defining-an-enum.md @@ -1,65 +1,42 @@ -## Defining an Enum - -Where structs give you a way of grouping together related fields and data, like -a `Rectangle` with its `width` and `height`, enums give you a way of saying a -value is one of a possible set of values. For example, we may want to say that -`Rectangle` is one of a set of possible shapes that also includes `Circle` and -`Triangle`. To do this, Rust allows us to encode these possibilities as an enum. - -Let’s look at a situation we might want to express in code and see why enums -are useful and more appropriate than structs in this case. Say we need to work -with IP addresses. Currently, two major standards are used for IP addresses: -version four and version six. Because these are the only possibilities for an -IP address that our program will come across, we can _enumerate_ all possible -variants, which is where enumeration gets its name. - -Any IP address can be either a version four or a version six address, but not -both at the same time. That property of IP addresses makes the enum data -structure appropriate because an enum value can only be one of its variants. -Both version four and version six addresses are still fundamentally IP -addresses, so they should be treated as the same type when the code is handling -situations that apply to any kind of IP address. - -We can express this concept in code by defining an `IpAddrKind` enumeration and -listing the possible kinds an IP address can be, `V4` and `V6`. These are the -variants of the enum: +## تعریف یک Enum + +در حالی که ساختارها (Structs) روشی برای گروه‌بندی فیلدها و داده‌های مرتبط فراهم می‌کنند، Enumها به شما امکان می‌دهند که بگویید یک مقدار یکی از مجموعه مقادیر ممکن است. به عنوان مثال، ممکن است بخواهیم بگوییم که `Rectangle` یکی از مجموعه اشکالی است که همچنین شامل `Circle` و `Triangle` می‌شود. برای انجام این کار، زبان Rust به ما اجازه می‌دهد تا این امکان‌ها را به‌عنوان یک Enum کدگذاری کنیم. + +بیایید نگاهی به یک موقعیت بیندازیم که ممکن است بخواهیم در کد بیان کنیم و ببینیم چرا Enumها مفیدتر و مناسب‌تر از Structها هستند. فرض کنید باید با آدرس‌های IP کار کنیم. در حال حاضر، دو استاندارد اصلی برای آدرس‌های IP وجود دارد: نسخه چهار و نسخه شش. از آنجا که این تنها حالت‌های ممکن برای آدرس‌های IP هستند که برنامه ما با آن‌ها مواجه خواهد شد، می‌توانیم تمام حالت‌های ممکن را شمارش کنیم، که همین موضوع اساس نام‌گذاری Enumها است. + +هر آدرس IP می‌تواند یا نسخه چهار یا نسخه شش باشد، اما نمی‌تواند به‌طور همزمان هر دو باشد. این ویژگی آدرس‌های IP استفاده از ساختار داده Enum را مناسب می‌کند زیرا مقدار یک Enum می‌تواند فقط یکی از حالت‌هایش باشد. هر دو آدرس نسخه چهار و نسخه شش همچنان اساساً آدرس IP هستند، بنابراین باید هنگام کار با کدی که به هر نوع آدرس IP اعمال می‌شود، به‌عنوان یک نوع یکسان رفتار شوند. + +ما می‌توانیم این مفهوم را در کد با تعریف یک Enumeration به نام `IpAddrKind` و فهرست کردن انواع ممکن یک آدرس IP، یعنی `V4` و `V6`، بیان کنیم. این‌ها حالت‌های Enum هستند: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-01-defining-enums/src/main.rs:def}} ``` -`IpAddrKind` is now a custom data type that we can use elsewhere in our code. +اکنون `IpAddrKind` یک نوع داده سفارشی است که می‌توانیم در قسمت‌های دیگر کد خود استفاده کنیم. -### Enum Values +### مقادیر Enum -We can create instances of each of the two variants of `IpAddrKind` like this: +می‌توانیم نمونه‌هایی از هر یک از دو حالت `IpAddrKind` را به این صورت ایجاد کنیم: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-01-defining-enums/src/main.rs:instance}} ``` -Note that the variants of the enum are namespaced under its identifier, and we -use a double colon to separate the two. This is useful because now both values -`IpAddrKind::V4` and `IpAddrKind::V6` are of the same type: `IpAddrKind`. We -can then, for instance, define a function that takes any `IpAddrKind`: +توجه داشته باشید که حالت‌های Enum تحت شناسه آن نام‌گذاری شده‌اند و برای جدا کردن دو حالت از یکدیگر از دو نقطه استفاده می‌کنیم. این ویژگی مفید است زیرا اکنون هر دو مقدار `IpAddrKind::V4` و `IpAddrKind::V6` از نوع یکسان `IpAddrKind` هستند. سپس می‌توانیم به عنوان مثال یک تابع تعریف کنیم که هر نوع `IpAddrKind` را به عنوان ورودی بپذیرد: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-01-defining-enums/src/main.rs:fn}} ``` -And we can call this function with either variant: +و می‌توانیم این تابع را با هر دو حالت فراخوانی کنیم: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-01-defining-enums/src/main.rs:fn_call}} ``` -Using enums has even more advantages. Thinking more about our IP address type, -at the moment we don’t have a way to store the actual IP address _data_; we -only know what _kind_ it is. Given that you just learned about structs in -Chapter 5, you might be tempted to tackle this problem with structs as shown in -Listing 6-1. +استفاده از Enumها مزایای بیشتری دارد. اگر بیشتر به نوع آدرس IP خود فکر کنیم، متوجه می‌شویم که در حال حاضر راهی برای ذخیره داده‌های واقعی آدرس IP نداریم؛ فقط می‌دانیم که چه نوعی است. با توجه به اینکه به‌تازگی درباره Structها در فصل 5 یاد گرفته‌اید، ممکن است وسوسه شوید این مشکل را با Structها همانطور که در فهرست 6-1 نشان داده شده است، حل کنید. -+ ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-01/src/main.rs:here}} @@ -67,51 +44,23 @@ Listing 6-1. -Here, we’ve defined a struct `IpAddr` that has two fields: a `kind` field that -is of type `IpAddrKind` (the enum we defined previously) and an `address` field -of type `String`. We have two instances of this struct. The first is `home`, -and it has the value `IpAddrKind::V4` as its `kind` with associated address -data of `127.0.0.1`. The second instance is `loopback`. It has the other -variant of `IpAddrKind` as its `kind` value, `V6`, and has address `::1` -associated with it. We’ve used a struct to bundle the `kind` and `address` -values together, so now the variant is associated with the value. +در اینجا ما یک Struct به نام `IpAddr` تعریف کرده‌ایم که دو فیلد دارد: یک فیلد `kind` که از نوع `IpAddrKind` است (همان Enum که قبلاً تعریف کردیم) و یک فیلد `address` از نوع `String`. ما دو نمونه از این Struct داریم. اولین مورد `home` نام دارد و مقدار `IpAddrKind::V4` به‌عنوان `kind` با داده‌های آدرس مرتبط `127.0.0.1` دارد. نمونه دوم `loopback` نام دارد. این نمونه حالت دیگر Enum یعنی `V6` را به‌عنوان مقدار `kind` دارد و آدرس مرتبط `::1` است. ما از یک Struct برای بسته‌بندی مقادیر `kind` و `address` با هم استفاده کرده‌ایم، بنابراین اکنون حالت با مقدار مرتبط شده است. -However, representing the same concept using just an enum is more concise: -rather than an enum inside a struct, we can put data directly into each enum -variant. This new definition of the `IpAddr` enum says that both `V4` and `V6` -variants will have associated `String` values: +با این حال، نمایش همان مفهوم با استفاده از فقط یک Enum مختصرتر است: به‌جای استفاده از Enum داخل یک Struct، می‌توانیم داده‌ها را مستقیماً به هر حالت Enum متصل کنیم. این تعریف جدید Enum `IpAddr` نشان می‌دهد که هر دو حالت `V4` و `V6` مقادیر `String` مرتبط دارند: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-02-enum-with-data/src/main.rs:here}} ``` -We attach data to each variant of the enum directly, so there is no need for an -extra struct. Here, it’s also easier to see another detail of how enums work: -the name of each enum variant that we define also becomes a function that -constructs an instance of the enum. That is, `IpAddr::V4()` is a function call -that takes a `String` argument and returns an instance of the `IpAddr` type. We -automatically get this constructor function defined as a result of defining the -enum. - -There’s another advantage to using an enum rather than a struct: each variant -can have different types and amounts of associated data. Version four IP -addresses will always have four numeric components that will have values -between 0 and 255. If we wanted to store `V4` addresses as four `u8` values but -still express `V6` addresses as one `String` value, we wouldn’t be able to with -a struct. Enums handle this case with ease: +ما داده‌ها را مستقیماً به هر حالت Enum متصل کرده‌ایم، بنابراین نیازی به یک Struct اضافی نیست. در اینجا همچنین می‌توان جزئیات دیگری از نحوه عملکرد Enumها را مشاهده کرد: نام هر حالت Enum که تعریف می‌کنیم به‌صورت یک تابع تبدیل می‌شود که نمونه‌ای از Enum ایجاد می‌کند. یعنی `IpAddr::V4()` یک فراخوانی تابع است که یک آرگومان از نوع `String` می‌گیرد و نمونه‌ای از نوع `IpAddr` برمی‌گرداند. این تابع سازنده به‌طور خودکار به‌عنوان نتیجه تعریف Enum تعریف می‌شود. + +یک مزیت دیگر استفاده از Enum به‌جای Struct این است که هر حالت می‌تواند انواع و مقادیر داده مرتبط متفاوتی داشته باشد. آدرس‌های IP نسخه چهار همیشه چهار مؤلفه عددی خواهند داشت که مقادیرشان بین 0 و 255 است. اگر بخواهیم آدرس‌های `V4` را به‌صورت چهار مقدار `u8` ذخیره کنیم اما همچنان آدرس‌های `V6` را به‌صورت یک مقدار `String` بیان کنیم، با یک Struct نمی‌توانیم این کار را انجام دهیم. Enumها به‌راحتی این حالت را مدیریت می‌کنند: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-03-variants-with-different-data/src/main.rs:here}} ``` -We’ve shown several different ways to define data structures to store version -four and version six IP addresses. However, as it turns out, wanting to store -IP addresses and encode which kind they are is so common that [the standard -library has a definition we can use!][IpAddr] Let’s look at how -the standard library defines `IpAddr`: it has the exact enum and variants that -we’ve defined and used, but it embeds the address data inside the variants in -the form of two different structs, which are defined differently for each -variant: +ما چندین روش مختلف برای تعریف ساختارهای داده برای ذخیره آدرس‌های IP نسخه چهار و نسخه شش نشان داده‌ایم. با این حال، همان‌طور که مشخص است، ذخیره آدرس‌های IP و کدگذاری نوع آن‌ها به‌قدری رایج است که [کتابخانه استاندارد تعریفی برای این کار ارائه می‌دهد!][IpAddr] بیایید نگاهی به نحوه تعریف `IpAddr` در کتابخانه استاندارد بیندازیم: این کتابخانه دارای همان Enum و حالت‌هایی است که ما تعریف کرده و استفاده کرده‌ایم، اما داده‌های آدرس را به‌صورت داخلی در حالت‌ها در قالب دو Struct مختلف تعبیه کرده است، که به‌طور متفاوت برای هر حالت تعریف شده‌اند: ```rust struct Ipv4Addr { @@ -128,20 +77,13 @@ enum IpAddr { } ``` -This code illustrates that you can put any kind of data inside an enum variant: -strings, numeric types, or structs, for example. You can even include another -enum! Also, standard library types are often not much more complicated than -what you might come up with. +این کد نشان می‌دهد که شما می‌توانید هر نوع داده‌ای مانند رشته‌ها، انواع عددی، یا Structها را داخل حالت‌های Enum قرار دهید. حتی می‌توانید یک Enum دیگر را نیز شامل کنید! همچنین، انواع استاندارد کتابخانه معمولاً خیلی پیچیده‌تر از چیزی نیستند که ممکن است خودتان ارائه دهید. -Note that even though the standard library contains a definition for `IpAddr`, -we can still create and use our own definition without conflict because we -haven’t brought the standard library’s definition into our scope. We’ll talk -more about bringing types into scope in Chapter 7. +توجه داشته باشید که با وجود اینکه کتابخانه استاندارد تعریفی برای `IpAddr` دارد، ما همچنان می‌توانیم تعریف خودمان را ایجاد و استفاده کنیم بدون اینکه تضادی پیش بیاید زیرا تعریف کتابخانه استاندارد را به محدوده خود وارد نکرده‌ایم. ما در فصل 7 درباره وارد کردن انواع به محدوده بیشتر صحبت خواهیم کرد. -Let’s look at another example of an enum in Listing 6-2: this one has a wide -variety of types embedded in its variants. +بیایید به مثال دیگری از یک Enum در فهرست 6-2 نگاه کنیم: این مورد دارای انواع متنوعی از داده‌های جاسازی‌شده در حالت‌های خود است. -+ ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-02/src/main.rs:here}} @@ -149,86 +91,50 @@ variety of types embedded in its variants. -This enum has four variants with different types: +این Enum دارای چهار حالت با انواع مختلف است: -- `Quit`: Has no data associated with it at all -- `Move`: Has named fields, like a struct does -- `Write`: Includes a single `String` -- `ChangeColor`: Includes three `i32` values +* `Quit`: هیچ داده‌ای همراه خود ندارد +* `Move`: دارای فیلدهای نام‌گذاری‌شده است، مانند یک `struct` +* `Write`: شامل یک `String` واحد است +* `ChangeColor`: شامل سه مقدار از نوع `i32` است -Defining an enum with variants such as the ones in Listing 6-2 is similar to -defining different kinds of struct definitions, except the enum doesn’t use the -`struct` keyword and all the variants are grouped together under the `Message` -type. The following structs could hold the same data that the preceding enum -variants hold: +تعریف یک Enum با حالت‌هایی مانند حالت‌های فهرست 6-2 مشابه تعریف انواع مختلف ساختارها است، با این تفاوت که Enum از کلمه کلیدی `struct` استفاده نمی‌کند و تمام حالت‌ها تحت نوع `Message` گروه‌بندی شده‌اند. ساختارهای زیر می‌توانند همان داده‌هایی را نگه دارند که حالت‌های Enum قبلی نگه می‌دارند: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-04-structs-similar-to-message-enum/src/main.rs:here}} ``` -But if we used the different structs, each of which has its own type, we -couldn’t as easily define a function to take any of these kinds of messages as -we could with the `Message` enum defined in Listing 6-2, which is a single type. +اما اگر از ساختارهای مختلفی استفاده کنیم که هر یک نوع خاص خود را دارند، نمی‌توانیم به‌راحتی یک تابع تعریف کنیم که بتواند هر یک از این انواع پیام‌ها را مانند چیزی که با Enum `Message` تعریف‌شده در فهرست 6-2 امکان‌پذیر است، دریافت کند. -There is one more similarity between enums and structs: just as we’re able to -define methods on structs using `impl`, we’re also able to define methods on -enums. Here’s a method named `call` that we could define on our `Message` enum: +یک شباهت دیگر بین Enumها و ساختارها این است: همان‌طور که می‌توانیم متدها را با استفاده از `impl` برای ساختارها تعریف کنیم، می‌توانیم متدها را برای Enumها نیز تعریف کنیم. اینجا یک متد به نام `call` است که می‌توانیم برای Enum `Message` خود تعریف کنیم: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-05-methods-on-enums/src/main.rs:here}} ``` -The body of the method would use `self` to get the value that we called the -method on. In this example, we’ve created a variable `m` that has the value -`Message::Write(String::from("hello"))`, and that is what `self` will be in the -body of the `call` method when `m.call()` runs. - -Let’s look at another enum in the standard library that is very common and -useful: `Option`. - -### The `Option` Enum and Its Advantages Over Null Values - -This section explores a case study of `Option`, which is another enum defined -by the standard library. The `Option` type encodes the very common scenario in -which a value could be something or it could be nothing. - -For example, if you request the first item in a non-empty list, you would get -a value. If you request the first item in an empty list, you would get nothing. -Expressing this concept in terms of the type system means the compiler can -check whether you’ve handled all the cases you should be handling; this -functionality can prevent bugs that are extremely common in other programming -languages. - -Programming language design is often thought of in terms of which features you -include, but the features you exclude are important too. Rust doesn’t have the -null feature that many other languages have. _Null_ is a value that means there -is no value there. In languages with null, variables can always be in one of -two states: null or not-null. - -In his 2009 presentation “Null References: The Billion Dollar Mistake,” Tony -Hoare, the inventor of null, had this to say: - -> I call it my billion-dollar mistake. At that time, I was designing the first -> comprehensive type system for references in an object-oriented language. My -> goal was to ensure that all use of references should be absolutely safe, with -> checking performed automatically by the compiler. But I couldn’t resist the -> temptation to put in a null reference, simply because it was so easy to -> implement. This has led to innumerable errors, vulnerabilities, and system -> crashes, which have probably caused a billion dollars of pain and damage in -> the last forty years. - -The problem with null values is that if you try to use a null value as a -not-null value, you’ll get an error of some kind. Because this null or not-null -property is pervasive, it’s extremely easy to make this kind of error. - -However, the concept that null is trying to express is still a useful one: a -null is a value that is currently invalid or absent for some reason. - -The problem isn’t really with the concept but with the particular -implementation. As such, Rust does not have nulls, but it does have an enum -that can encode the concept of a value being present or absent. This enum is -`Option`, and it is [defined by the standard library][option] -as follows: +بدنه این متد از `self` برای دسترسی به مقداری که متد روی آن فراخوانی شده است استفاده می‌کند. در این مثال، ما یک متغیر به نام `m` ایجاد کرده‌ایم که مقدار `Message::Write(String::from("hello"))` را دارد و این همان چیزی است که `self` در بدن متد `call` هنگام اجرای `m.call()` خواهد بود. + +بیایید به یک Enum دیگر در کتابخانه استاندارد که بسیار متداول و مفید است نگاهی بیندازیم: `Option`. + + +### Enum `Option` و مزایای آن نسبت به مقادیر Null + +این بخش به مطالعه موردی `Option` می‌پردازد که یکی دیگر از Enumهای تعریف شده در کتابخانه استاندارد است. نوع `Option` سناریوی بسیار رایجی را نشان می‌دهد که در آن یک مقدار می‌تواند وجود داشته باشد یا هیچ مقداری وجود نداشته باشد. + +به عنوان مثال، اگر اولین مورد را در یک لیست غیر خالی درخواست کنید، مقداری دریافت خواهید کرد. اگر اولین مورد را در یک لیست خالی درخواست کنید، هیچ مقداری دریافت نخواهید کرد. بیان این مفهوم در قالب سیستم نوع به کامپایلر امکان می‌دهد تا بررسی کند آیا تمام مواردی که باید مدیریت شوند را در نظر گرفته‌اید؛ این ویژگی می‌تواند از بروز باگ‌هایی که در دیگر زبان‌های برنامه‌نویسی بسیار رایج هستند جلوگیری کند. + +طراحی زبان‌های برنامه‌نویسی اغلب از نظر ویژگی‌هایی که شامل می‌شوند بررسی می‌شود، اما ویژگی‌هایی که کنار گذاشته می‌شوند نیز مهم هستند. Rust ویژگی null را که بسیاری از زبان‌های دیگر دارند، ندارد. _Null_ یک مقدار است که به معنای وجود نداشتن مقدار می‌باشد. در زبان‌هایی که دارای null هستند، متغیرها می‌توانند همیشه در یکی از دو حالت باشند: null یا not-null. + +در ارائه‌ی سال ۲۰۰۹ خود با عنوان «ارجاعات تهی: اشتباه میلیارد دلاری»(Null References: The Billion Dollar Mistake,”)، تونی هوار، +مخترع `null`، این سخن را بیان کرد: + +> من آن را اشتباه میلیارد دلاری خود می‌نامم. در آن زمان، من در حال طراحی اولین سیستم نوع جامع برای مراجع در یک زبان شیءگرا بودم. هدف من اطمینان از این بود که تمام استفاده‌های از مراجع کاملاً امن باشند، با بررسی‌هایی که به‌طور خودکار توسط کامپایلر انجام می‌شوند. اما نتوانستم در برابر وسوسه قرار دادن یک مرجع null مقاومت کنم، فقط به این دلیل که پیاده‌سازی آن بسیار آسان بود. این منجر به خطاها، آسیب‌پذیری‌ها، و خرابی‌های سیستم‌های بی‌شماری شده است که احتمالاً باعث یک میلیارد دلار درد و ضرر در چهل سال گذشته شده‌اند. + +مشکل مقادیر null این است که اگر بخواهید از یک مقدار null به‌عنوان یک مقدار not-null استفاده کنید، نوعی خطا دریافت خواهید کرد. از آنجا که خاصیت null یا not-null فراگیر است، بسیار آسان است که این نوع خطا را مرتکب شوید. + +با این حال، مفهومی که null سعی در بیان آن دارد همچنان مفید است: null یک مقدار است که در حال حاضر به دلایلی نامعتبر یا غایب است. + +مشکل واقعاً با مفهوم نیست، بلکه با پیاده‌سازی خاص است. به این ترتیب، Rust مقادیر null ندارد، اما یک Enum دارد که می‌تواند مفهوم وجود داشتن یا نداشتن یک مقدار را کدگذاری کند. این Enum `Option` است که به صورت زیر توسط کتابخانه استاندارد تعریف شده است: ```rust enum Option { @@ -237,90 +143,40 @@ enum Option { } ``` -The `Option` enum is so useful that it’s even included in the prelude; you -don’t need to bring it into scope explicitly. Its variants are also included in -the prelude: you can use `Some` and `None` directly without the `Option::` -prefix. The `Option` enum is still just a regular enum, and `Some(T)` and -`None` are still variants of type `Option`. +Enum `Option` آن‌قدر مفید است که حتی در بخش پیش‌فرض (Prelude) گنجانده شده است؛ نیازی نیست که به‌طور صریح آن را به محدوده بیاورید. حالت‌های آن نیز در بخش پیش‌فرض هستند: می‌توانید مستقیماً از `Some` و `None` بدون پیشوند `Option::` استفاده کنید. Enum `Option` همچنان یک Enum معمولی است، و `Some(T)` و `None` همچنان حالت‌هایی از نوع `Option` هستند. -The `` syntax is a feature of Rust we haven’t talked about yet. It’s a -generic type parameter, and we’ll cover generics in more detail in Chapter 10. -For now, all you need to know is that `` means that the `Some` variant of -the `Option` enum can hold one piece of data of any type, and that each -concrete type that gets used in place of `T` makes the overall `Option` type -a different type. Here are some examples of using `Option` values to hold -number types and char types: +سینتکس `` یک ویژگی از Rust است که هنوز درباره آن صحبت نکرده‌ایم. این یک پارامتر نوع عمومی (Generic) است و ما در فصل 10 به جزئیات بیشتری درباره آن خواهیم پرداخت. برای حالا، تنها چیزی که باید بدانید این است که `` به این معنا است که حالت `Some` از Enum `Option` می‌تواند یک قطعه داده از هر نوعی را نگه دارد، و هر نوع مشخصی که به جای `T` استفاده شود، کل نوع `Option` را به یک نوع متفاوت تبدیل می‌کند. در اینجا چند مثال از استفاده از مقادیر `Option` برای نگه‌داری انواع عددی و کاراکتری آورده شده است: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-06-option-examples/src/main.rs:here}} ``` -The type of `some_number` is `Option`. The type of `some_char` is -`Option`, which is a different type. Rust can infer these types because -we’ve specified a value inside the `Some` variant. For `absent_number`, Rust -requires us to annotate the overall `Option` type: the compiler can’t infer the -type that the corresponding `Some` variant will hold by looking only at a -`None` value. Here, we tell Rust that we mean for `absent_number` to be of type -`Option`. +نوع `some_number` برابر با `Option` است. نوع `some_char` برابر با `Option` است که یک نوع متفاوت است. Rust می‌تواند این انواع را تشخیص دهد زیرا ما مقداری را در حالت `Some` مشخص کرده‌ایم. برای `absent_number`، Rust از ما می‌خواهد که نوع کلی `Option` را مشخص کنیم: کامپایلر نمی‌تواند نوعی را که حالت `Some` مرتبط نگه خواهد داشت فقط با نگاه کردن به یک مقدار `None` تشخیص دهد. در اینجا، ما به Rust می‌گوییم که منظور ما این است که `absent_number` از نوع `Option` باشد. -When we have a `Some` value, we know that a value is present and the value is -held within the `Some`. When we have a `None` value, in some sense it means the -same thing as null: we don’t have a valid value. So why is having `Option` -any better than having null? +هنگامی که ما یک مقدار `Some` داریم، می‌دانیم که یک مقدار وجود دارد و این مقدار درون `Some` نگه‌داری می‌شود. هنگامی که ما یک مقدار `None` داریم، از یک نظر، این همان معنای null را دارد: ما یک مقدار معتبر نداریم. پس چرا داشتن `Option` بهتر از داشتن null است؟ -In short, because `Option` and `T` (where `T` can be any type) are different -types, the compiler won’t let us use an `Option` value as if it were -definitely a valid value. For example, this code won’t compile, because it’s -trying to add an `i8` to an `Option`: +به طور خلاصه، به این دلیل که `Option` و `T` (جایی که `T` می‌تواند هر نوعی باشد) انواع متفاوتی هستند، کامپایلر به ما اجازه نمی‌دهد که یک مقدار `Option` را به‌عنوان یک مقدار قطعاً معتبر استفاده کنیم. به عنوان مثال، این کد کامپایل نخواهد شد، زیرا سعی در جمع یک `i8` با یک `Option` دارد: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-07-cant-use-option-directly/src/main.rs:here}} ``` -If we run this code, we get an error message like this one: +اگر این کد را اجرا کنیم، پیام خطایی شبیه به این دریافت می‌کنیم: ```console {{#include ../listings/ch06-enums-and-pattern-matching/no-listing-07-cant-use-option-directly/output.txt}} ``` -Intense! In effect, this error message means that Rust doesn’t understand how -to add an `i8` and an `Option`, because they’re different types. When we -have a value of a type like `i8` in Rust, the compiler will ensure that we -always have a valid value. We can proceed confidently without having to check -for null before using that value. Only when we have an `Option` (or -whatever type of value we’re working with) do we have to worry about possibly -not having a value, and the compiler will make sure we handle that case before -using the value. - -In other words, you have to convert an `Option` to a `T` before you can -perform `T` operations with it. Generally, this helps catch one of the most -common issues with null: assuming that something isn’t null when it actually is. - -Eliminating the risk of incorrectly assuming a not-null value helps you to be -more confident in your code. In order to have a value that can possibly be -null, you must explicitly opt in by making the type of that value `Option`. -Then, when you use that value, you are required to explicitly handle the case -when the value is null. Everywhere that a value has a type that isn’t an -`Option`, you _can_ safely assume that the value isn’t null. This was a -deliberate design decision for Rust to limit null’s pervasiveness and increase -the safety of Rust code. - -So how do you get the `T` value out of a `Some` variant when you have a value -of type `Option` so that you can use that value? The `Option` enum has a -large number of methods that are useful in a variety of situations; you can -check them out in [its documentation][docs]. Becoming familiar -with the methods on `Option` will be extremely useful in your journey with -Rust. - -In general, in order to use an `Option` value, you want to have code that -will handle each variant. You want some code that will run only when you have a -`Some(T)` value, and this code is allowed to use the inner `T`. You want some -other code to run only if you have a `None` value, and that code doesn’t have a -`T` value available. The `match` expression is a control flow construct that -does just this when used with enums: it will run different code depending on -which variant of the enum it has, and that code can use the data inside the -matching value. - -[IpAddr]: ../std/net/enum.IpAddr.html -[option]: ../std/option/enum.Option.html -[docs]: ../std/option/enum.Option.html +شدید است! در واقع، این پیام خطا به این معنا است که Rust نمی‌داند چگونه یک `i8` و یک `Option` را جمع کند، زیرا آن‌ها انواع مختلفی هستند. هنگامی که یک مقدار از نوعی مانند `i8` در Rust داریم، کامپایلر اطمینان می‌دهد که همیشه یک مقدار معتبر داریم. می‌توانیم با اطمینان ادامه دهیم بدون اینکه مجبور باشیم قبل از استفاده از آن مقدار، null را بررسی کنیم. فقط زمانی که یک `Option` (یا هر نوع مقداری که با آن کار می‌کنیم) داریم باید نگران احتمال عدم وجود مقدار باشیم، و کامپایلر اطمینان می‌دهد که ما آن حالت را قبل از استفاده از مقدار مدیریت کرده‌ایم. + +به عبارت دیگر، شما باید یک مقدار `Option` را به یک مقدار `T` تبدیل کنید قبل از اینکه بتوانید عملیات `T` را با آن انجام دهید. به طور کلی، این به جلوگیری از یکی از شایع‌ترین مشکلات null کمک می‌کند: فرض غلط که چیزی null نیست در حالی که واقعاً null است. + +از بین بردن خطر فرض نادرست درباره یک مقدار not-null به شما کمک می‌کند تا در کد خود اطمینان بیشتری داشته باشید. برای داشتن مقداری که ممکن است null باشد، باید صریحاً با تعیین نوع آن مقدار به‌عنوان `Option` به آن رضایت دهید. سپس، هنگامی که از آن مقدار استفاده می‌کنید، موظف هستید که به‌طور صریح حالتی را که مقدار null است مدیریت کنید. هر جا که مقداری از نوعی است که `Option` نیست، می‌توانید با خیال راحت فرض کنید که مقدار null نیست. این تصمیم طراحی برای محدود کردن شیوع null و افزایش ایمنی کدهای Rust بود. + +پس چگونه مقدار `T` را از حالت `Some` وقتی که یک مقدار از نوع `Option` دارید استخراج می‌کنید تا بتوانید از آن مقدار استفاده کنید؟ Enum `Option` تعداد زیادی متد دارد که در موقعیت‌های مختلف مفید هستند؛ می‌توانید آن‌ها را در [مستندات آن][docs] بررسی کنید. آشنایی با متدهای موجود در `Option` در مسیر یادگیری Rust بسیار مفید خواهد بود. + +به طور کلی، برای استفاده از یک مقدار `Option`، می‌خواهید کدی داشته باشید که هر حالت را مدیریت کند. می‌خواهید کدی داشته باشید که تنها زمانی اجرا شود که یک مقدار `Some(T)` دارید، و این کد اجازه دارد از مقدار داخلی `T` استفاده کند. همچنین، می‌خواهید کدی داشته باشید که فقط در صورت وجود مقدار `None` اجرا شود، و این کد به هیچ مقدار `T` دسترسی ندارد. عبارت `match` یک سازه جریان کنترلی است که وقتی با Enumها استفاده می‌شود دقیقاً این کار را انجام می‌دهد: این عبارت کد متفاوتی را بسته به اینکه کدام حالت از Enum موجود است اجرا می‌کند، و آن کد می‌تواند از داده داخل مقدار منطبق شده استفاده کند. + +[IpAddr]: https://doc.rust-lang.org/std/net/enum.IpAddr.html +[option]: https://doc.rust-lang.org/std/option/enum.Option.html +[docs]: https://doc.rust-lang.org/std/option/enum.Option.html diff --git a/src/ch06-02-match.md b/src/ch06-02-match.md index 6fa9de9e76..fb9e4511ae 100644 --- a/src/ch06-02-match.md +++ b/src/ch06-02-match.md @@ -1,30 +1,17 @@ + -## The `match` Control Flow Construct +## سازه جریان کنترلی `match` -Rust has an extremely powerful control flow construct called `match` that -allows you to compare a value against a series of patterns and then execute -code based on which pattern matches. Patterns can be made up of literal values, -variable names, wildcards, and many other things; [Chapter -19][ch19-00-patterns] covers all the different kinds of patterns -and what they do. The power of `match` comes from the expressiveness of the -patterns and the fact that the compiler confirms that all possible cases are -handled. +زبان Rust دارای یک سازه جریان کنترلی بسیار قدرتمند به نام `match` است که به شما اجازه می‌دهد تا یک مقدار را با یک سری الگوها مقایسه کنید و سپس بر اساس الگویی که مطابقت دارد، کد مربوطه را اجرا کنید. الگوها می‌توانند شامل مقادیر ثابت، نام متغیرها، wildcardها و چیزهای دیگر باشند. [فصل 19][ch19-00-patterns] انواع مختلف الگوها و عملکرد آن‌ها را پوشش می‌دهد. قدرت `match` از بیان‌پذیری الگوها و این واقعیت ناشی می‌شود که کامپایلر تأیید می‌کند که همه حالت‌های ممکن مدیریت شده‌اند. -Think of a `match` expression as being like a coin-sorting machine: coins slide -down a track with variously sized holes along it, and each coin falls through -the first hole it encounters that it fits into. In the same way, values go -through each pattern in a `match`, and at the first pattern the value “fits,” -the value falls into the associated code block to be used during execution. +می‌توانید یک عبارت `match` را مانند یک دستگاه مرتب‌کننده سکه تصور کنید: سکه‌ها در یک مسیر با سوراخ‌هایی با اندازه‌های مختلف قرار می‌گیرند و هر سکه از اولین سوراخی که در آن جا می‌شود عبور می‌کند. به همین ترتیب، مقادیر از هر الگو در یک `match` عبور می‌کنند و در اولین الگویی که مقدار "جا می‌شود"، مقدار به بلوک کد مرتبط می‌افتد و برای اجرا استفاده می‌شود. -Speaking of coins, let’s use them as an example using `match`! We can write a -function that takes an unknown US coin and, in a similar way as the counting -machine, determines which coin it is and returns its value in cents, as shown -in Listing 6-3. +حال بیایید از یک مثال واقعی با سکه‌ها استفاده کنیم! می‌توانیم تابعی بنویسیم که یک سکه ناشناخته از ایالات متحده را بگیرد و به شیوه‌ای مشابه دستگاه شمارنده سکه‌ها، تعیین کند که آن سکه کدام نوع است و ارزش آن را به سنت برگرداند، همانطور که در فهرست 6-3 نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-03/src/main.rs:here}} @@ -32,53 +19,29 @@ in Listing 6-3. -Let’s break down the `match` in the `value_in_cents` function. First we list -the `match` keyword followed by an expression, which in this case is the value -`coin`. This seems very similar to a conditional expression used with `if`, but -there’s a big difference: with `if`, the condition needs to evaluate to a -Boolean value, but here it can be any type. The type of `coin` in this example -is the `Coin` enum that we defined on the first line. - -Next are the `match` arms. An arm has two parts: a pattern and some code. The -first arm here has a pattern that is the value `Coin::Penny` and then the `=>` -operator that separates the pattern and the code to run. The code in this case -is just the value `1`. Each arm is separated from the next with a comma. - -When the `match` expression executes, it compares the resultant value against -the pattern of each arm, in order. If a pattern matches the value, the code -associated with that pattern is executed. If that pattern doesn’t match the -value, execution continues to the next arm, much as in a coin-sorting machine. -We can have as many arms as we need: in Listing 6-3, our `match` has four arms. - -The code associated with each arm is an expression, and the resultant value of -the expression in the matching arm is the value that gets returned for the -entire `match` expression. - -We don’t typically use curly brackets if the match arm code is short, as it is -in Listing 6-3 where each arm just returns a value. If you want to run multiple -lines of code in a match arm, you must use curly brackets, and the comma -following the arm is then optional. For example, the following code prints -“Lucky penny!” every time the method is called with a `Coin::Penny`, but still -returns the last value of the block, `1`: +### بازبینی تابع `value_in_cents` + +ابتدا کلمه کلیدی `match` و سپس یک عبارت را فهرست می‌کنیم که در این مورد مقدار `coin` است. این کار بسیار مشابه یک عبارت شرطی که با `if` استفاده می‌شود به نظر می‌رسد، اما تفاوت بزرگی دارد: با `if`، شرط باید به یک مقدار بولین ارزیابی شود، اما اینجا می‌تواند هر نوعی باشد. نوع `coin` در این مثال enum `Coin` است که در اولین خط تعریف کردیم. + +بازوهای `match` دو قسمت دارند: یک الگو و مقداری کد. اولین بازو در اینجا دارای الگویی است که مقدار `Coin::Penny` است و سپس اپراتور `=>` که الگو و کد اجرایی را از هم جدا می‌کند. کد در اینجا فقط مقدار `1` است. هر بازو با یک کاما از بازوی بعدی جدا می‌شود. + +هنگامی که عبارت `match` اجرا می‌شود، مقدار حاصل را با الگوی هر بازو به ترتیب مقایسه می‌کند. اگر الگویی با مقدار مطابقت داشته باشد، کدی که با آن الگو مرتبط است اجرا می‌شود. اگر آن الگو با مقدار مطابقت نداشته باشد، اجرا به بازوی بعدی ادامه می‌یابد، همانطور که در یک دستگاه مرتب‌کننده سکه‌ها عمل می‌کند. ما می‌توانیم به هر تعداد بازو که نیاز داریم داشته باشیم: در فهرست 6-3، `match` ما چهار بازو دارد. + +کد مرتبط با هر بازو یک عبارت است و مقدار حاصل از عبارت در بازوی منطبق شده، مقداری است که برای کل عبارت `match` بازگردانده می‌شود. + +معمولاً اگر کد بازوی `match` کوتاه باشد، از آکولاد استفاده نمی‌کنیم، همانطور که در فهرست 6-3 که هر بازو فقط یک مقدار را برمی‌گرداند. اگر بخواهید چندین خط کد را در یک بازو اجرا کنید، باید از آکولاد استفاده کنید، و در این صورت کاما پس از بازو اختیاری است. به عنوان مثال، کد زیر هر بار که متد با یک `Coin::Penny` فراخوانی می‌شود، “Lucky penny!” را چاپ می‌کند، اما همچنان آخرین مقدار بلوک یعنی `1` را بازمی‌گرداند: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-08-match-arm-multiple-lines/src/main.rs:here}} ``` -### Patterns That Bind to Values +### الگوهایی که به مقادیر متصل می‌شوند -Another useful feature of match arms is that they can bind to the parts of the -values that match the pattern. This is how we can extract values out of enum -variants. +یکی دیگر از ویژگی‌های مفید بازوهای `match` این است که می‌توانند به بخش‌هایی از مقادیر که با الگو مطابقت دارند متصل شوند. این همان روشی است که می‌توانیم مقادیر را از حالت‌های enum استخراج کنیم. -As an example, let’s change one of our enum variants to hold data inside it. -From 1999 through 2008, the United States minted quarters with different -designs for each of the 50 states on one side. No other coins got state -designs, so only quarters have this extra value. We can add this information to -our `enum` by changing the `Quarter` variant to include a `UsState` value -stored inside it, which we’ve done in Listing 6-4. +به عنوان مثال، بیایید یکی از حالت‌های enum خود را تغییر دهیم تا داده‌هایی را درون خود نگه دارد. از سال 1999 تا 2008، ایالات متحده ربع‌هایی با طرح‌های مختلف برای هر یک از 50 ایالت در یک طرف ضرب کرد. هیچ سکه دیگری طرح ایالتی نداشت، بنابراین فقط ربع‌ها این مقدار اضافی را دارند. می‌توانیم این اطلاعات را به enum خود با تغییر حالت `Quarter` به گونه‌ای که یک مقدار `UsState` درون آن ذخیره شود اضافه کنیم، همانطور که در فهرست 6-4 انجام دادیم. -+ ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-04/src/main.rs:here}} @@ -86,174 +49,100 @@ stored inside it, which we’ve done in Listing 6-4. -Let’s imagine that a friend is trying to collect all 50 state quarters. While -we sort our loose change by coin type, we’ll also call out the name of the -state associated with each quarter so that if it’s one our friend doesn’t have, -they can add it to their collection. +بیایید تصور کنیم که یک دوست ما سعی دارد تمام 50 ربع ایالتی را جمع‌آوری کند. در حالی که ما پول‌های خود را بر اساس نوع سکه مرتب می‌کنیم، همچنین نام ایالتی که با هر ربع مرتبط است را اعلام می‌کنیم تا اگر این یکی از آن‌هایی باشد که دوست ما ندارد، بتوانند آن را به مجموعه خود اضافه کنند. -In the match expression for this code, we add a variable called `state` to the -pattern that matches values of the variant `Coin::Quarter`. When a -`Coin::Quarter` matches, the `state` variable will bind to the value of that -quarter’s state. Then we can use `state` in the code for that arm, like so: +در عبارت `match` برای این کد، یک متغیر به نام `state` به الگو اضافه می‌کنیم که مقادیری از حالت `Coin::Quarter` را تطبیق می‌دهد. وقتی که یک مقدار `Coin::Quarter` منطبق می‌شود، متغیر `state` به مقدار ایالت آن ربع متصل خواهد شد. سپس می‌توانیم از `state` در کد بازوی آن استفاده کنیم، به این صورت: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-09-variable-in-pattern/src/main.rs:here}} ``` -If we were to call `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin` -would be `Coin::Quarter(UsState::Alaska)`. When we compare that value with each -of the match arms, none of them match until we reach `Coin::Quarter(state)`. At -that point, the binding for `state` will be the value `UsState::Alaska`. We can -then use that binding in the `println!` expression, thus getting the inner -state value out of the `Coin` enum variant for `Quarter`. +اگر ما `value_in_cents(Coin::Quarter(UsState::Alaska))` را فراخوانی کنیم، مقدار `coin` برابر با `Coin::Quarter(UsState::Alaska)` خواهد بود. هنگامی که آن مقدار را با هر بازوی `match` مقایسه می‌کنیم، هیچ‌کدام از آن‌ها مطابقت ندارند تا اینکه به `Coin::Quarter(state)` برسیم. در این نقطه، اتصال برای `state` مقدار `UsState::Alaska` خواهد بود. سپس می‌توانیم از آن اتصال در عبارت `println!` استفاده کنیم و به این ترتیب مقدار داخلی ایالت را از حالت `Quarter` enum `Coin` استخراج کنیم. -### Matching with `Option` +### تطبیق با `Option` -In the previous section, we wanted to get the inner `T` value out of the `Some` -case when using `Option`; we can also handle `Option` using `match`, as -we did with the `Coin` enum! Instead of comparing coins, we’ll compare the -variants of `Option`, but the way the `match` expression works remains the -same. +در بخش قبلی، ما می‌خواستیم مقدار داخلی `T` را از حالت `Some` استخراج کنیم زمانی که از `Option` استفاده می‌کردیم؛ همچنین می‌توانیم با استفاده از `match` حالت‌های `Option` را مدیریت کنیم، همانطور که با enum `Coin` انجام دادیم! به جای مقایسه سکه‌ها، حالت‌های `Option` را مقایسه می‌کنیم، اما روش کار عبارت `match` همان باقی می‌ماند. -Let’s say we want to write a function that takes an `Option` and, if -there’s a value inside, adds 1 to that value. If there isn’t a value inside, -the function should return the `None` value and not attempt to perform any -operations. +بیایید فرض کنیم که می‌خواهیم تابعی بنویسیم که یک `Option` بگیرد و اگر یک مقدار درون آن باشد، مقدار 1 را به آن اضافه کند. اگر هیچ مقداری درون آن نباشد، تابع باید مقدار `None` را بازگرداند و هیچ عملیاتی را انجام ندهد. -This function is very easy to write, thanks to `match`, and will look like -Listing 6-5. - -+نوشتن این تابع با استفاده از `match` بسیار آسان است و به این صورت خواهد بود: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:here}} ``` - - -Let’s examine the first execution of `plus_one` in more detail. When we call -`plus_one(five)`, the variable `x` in the body of `plus_one` will have the -value `Some(5)`. We then compare that against each match arm: +اجازه دهید اولین اجرای `plus_one` را با جزئیات بیشتری بررسی کنیم. وقتی که `plus_one(five)` را فراخوانی می‌کنیم، متغیر `x` در بدنه `plus_one` مقدار `Some(5)` خواهد داشت. سپس آن را با هر بازوی `match` مقایسه می‌کنیم: ```rust,ignore {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:first_arm}} ``` -The `Some(5)` value doesn’t match the pattern `None`, so we continue to the -next arm: +مقدار `Some(5)` با الگوی `None` مطابقت ندارد، بنابراین به بازوی بعدی می‌رویم: ```rust,ignore {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:second_arm}} ``` -Does `Some(5)` match `Some(i)`? It does! We have the same variant. The `i` -binds to the value contained in `Some`, so `i` takes the value `5`. The code in -the match arm is then executed, so we add 1 to the value of `i` and create a -new `Some` value with our total `6` inside. +آیا `Some(5)` با `Some(i)` مطابقت دارد؟ بله! ما همان حالت را داریم. مقدار `i` به مقدار داخل `Some` متصل می‌شود، بنابراین `i` مقدار `5` می‌گیرد. سپس کد موجود در بازوی `match` اجرا می‌شود، بنابراین مقدار 1 به مقدار `i` اضافه می‌کنیم و یک مقدار جدید `Some` با مقدار کل `6` ایجاد می‌کنیم. -Now let’s consider the second call of `plus_one` in Listing 6-5, where `x` is -`None`. We enter the `match` and compare to the first arm: +حالا اجازه دهید اجرای دوم `plus_one` را در فهرست 6-5 در نظر بگیریم، جایی که مقدار `x` برابر با `None` است. ما وارد `match` می‌شویم و آن را با اولین بازو مقایسه می‌کنیم: ```rust,ignore {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-05/src/main.rs:first_arm}} ``` -It matches! There’s no value to add to, so the program stops and returns the -`None` value on the right side of `=>`. Because the first arm matched, no other -arms are compared. +این بار مطابقت دارد! هیچ مقداری برای اضافه کردن وجود ندارد، بنابراین برنامه متوقف می‌شود و مقدار `None` در سمت راست `=>` را بازمی‌گرداند. از آنجا که اولین بازو مطابقت داشت، بازوهای دیگر بررسی نمی‌شوند. -Combining `match` and enums is useful in many situations. You’ll see this -pattern a lot in Rust code: `match` against an enum, bind a variable to the -data inside, and then execute code based on it. It’s a bit tricky at first, but -once you get used to it, you’ll wish you had it in all languages. It’s -consistently a user favorite. +ترکیب `match` و enumها در بسیاری از موقعیت‌ها مفید است. این الگو را در کد Rust زیاد خواهید دید: `match` روی یک enum، اتصال یک متغیر به داده داخل، و سپس اجرای کد بر اساس آن. ممکن است در ابتدا کمی سخت باشد، اما وقتی به آن عادت کنید، آرزو خواهید کرد که در همه زبان‌ها وجود داشته باشد. این سازه همواره یکی از ویژگی‌های مورد علاقه کاربران است. -### Matches Are Exhaustive +### تطابق‌ها Exhaustive هستند -There’s one other aspect of `match` we need to discuss: the arms’ patterns must -cover all possibilities. Consider this version of our `plus_one` function, -which has a bug and won’t compile: +یکی دیگر از جنبه‌های عبارت `match` این است که الگوهای بازوها باید تمام حالت‌های ممکن را پوشش دهند. به این نسخه از تابع `plus_one` که یک باگ دارد و کامپایل نمی‌شود توجه کنید: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/src/main.rs:here}} ``` -We didn’t handle the `None` case, so this code will cause a bug. Luckily, it’s -a bug Rust knows how to catch. If we try to compile this code, we’ll get this -error: +ما حالت `None` را مدیریت نکرده‌ایم، بنابراین این کد باعث بروز یک باگ خواهد شد. خوشبختانه، این یک باگ است که Rust می‌تواند آن را تشخیص دهد. اگر تلاش کنیم این کد را کامپایل کنیم، این خطا را دریافت خواهیم کرد: ```console {{#include ../listings/ch06-enums-and-pattern-matching/no-listing-10-non-exhaustive-match/output.txt}} ``` -Rust knows that we didn’t cover every possible case, and even knows which -pattern we forgot! Matches in Rust are _exhaustive_: we must exhaust every last -possibility in order for the code to be valid. Especially in the case of -`Option`, when Rust prevents us from forgetting to explicitly handle the -`None` case, it protects us from assuming that we have a value when we might -have null, thus making the billion-dollar mistake discussed earlier impossible. - -### Catch-All Patterns and the `_` Placeholder - -Using enums, we can also take special actions for a few particular values, but -for all other values take one default action. Imagine we’re implementing a game -where, if you roll a 3 on a dice roll, your player doesn’t move, but instead -gets a new fancy hat. If you roll a 7, your player loses a fancy hat. For all -other values, your player moves that number of spaces on the game board. Here’s -a `match` that implements that logic, with the result of the dice roll -hardcoded rather than a random value, and all other logic represented by -functions without bodies because actually implementing them is out of scope for -this example: +Rust می‌داند که ما هر حالت ممکن را پوشش نداده‌ایم و حتی می‌داند که کدام الگو را فراموش کرده‌ایم! تطابق‌ها در Rust _exhaustive_ هستند: ما باید هر حالت ممکن را مدیریت کنیم تا کد معتبر باشد. به ویژه در مورد `Option`، وقتی که Rust از فراموش کردن مدیریت صریح حالت `None` جلوگیری می‌کند، از فرض نادرست وجود مقدار زمانی که ممکن است null باشد محافظت می‌کند و به این ترتیب اشتباه میلیارد دلاری که قبلاً بحث شد را غیرممکن می‌سازد. + +### الگوهای Catch-all و Placeholder `_` + +با استفاده از Enumها، می‌توانیم اقدامات ویژه‌ای برای چند مقدار خاص انجام دهیم، اما برای تمام مقادیر دیگر یک عمل پیش‌فرض داشته باشیم. تصور کنید که در حال پیاده‌سازی یک بازی هستید که اگر بازیکن عدد 3 روی تاس بیاورد، حرکت نمی‌کند اما یک کلاه زیبا جدید می‌گیرد. اگر عدد 7 بیاورد، بازیکن یک کلاه زیبا از دست می‌دهد. برای تمام مقادیر دیگر، بازیکن به اندازه عدد روی تخته بازی حرکت می‌کند. در اینجا یک عبارت `match` آورده شده است که این منطق را پیاده‌سازی می‌کند. نتیجه‌ی پرتاب تاس به جای مقدار تصادفی، به صورت هاردکد شده قرار داده شده است، و تمام منطق دیگر با توابعی بدون بدنه نشان داده شده‌اند زیرا پیاده‌سازی آن‌ها خارج از محدوده این مثال است: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-15-binding-catchall/src/main.rs:here}} ``` -For the first two arms, the patterns are the literal values `3` and `7`. For -the last arm that covers every other possible value, the pattern is the -variable we’ve chosen to name `other`. The code that runs for the `other` arm -uses the variable by passing it to the `move_player` function. +برای دو بازوی اول، الگوها مقادیر ثابت `3` و `7` هستند. برای بازوی آخر که تمام مقادیر ممکن دیگر را پوشش می‌دهد، الگو یک متغیر است که ما آن را `other` نامیده‌ایم. کدی که برای بازوی `other` اجرا می‌شود، متغیر را با استفاده از تابع `move_player` می‌فرستد. -This code compiles, even though we haven’t listed all the possible values a -`u8` can have, because the last pattern will match all values not specifically -listed. This catch-all pattern meets the requirement that `match` must be -exhaustive. Note that we have to put the catch-all arm last because the -patterns are evaluated in order. If we put the catch-all arm earlier, the other -arms would never run, so Rust will warn us if we add arms after a catch-all! +این کد کامپایل می‌شود، حتی اگر تمام مقادیر ممکن یک `u8` را فهرست نکرده باشیم، زیرا بازوی آخر همه مقادیر ذکر نشده را تطبیق می‌دهد. این الگوی catch-all نیاز تطابق exhaustive را برآورده می‌کند. توجه داشته باشید که باید بازوی catch-all را در آخر قرار دهیم زیرا الگوها به ترتیب ارزیابی می‌شوند. اگر بازوی catch-all را زودتر قرار دهیم، بازوهای دیگر هرگز اجرا نخواهند شد، بنابراین Rust به ما هشدار می‌دهد اگر بعد از یک بازوی catch-all بازوهای دیگری اضافه کنیم! -Rust also has a pattern we can use when we want a catch-all but don’t want to -_use_ the value in the catch-all pattern: `_` is a special pattern that matches -any value and does not bind to that value. This tells Rust we aren’t going to -use the value, so Rust won’t warn us about an unused variable. +Rust همچنین یک الگو به نام `_` دارد که می‌توانیم از آن استفاده کنیم وقتی که می‌خواهیم یک catch-all داشته باشیم اما نمی‌خواهیم مقدار در الگوی catch-all را _استفاده کنیم_. این به Rust می‌گوید که ما قصد نداریم مقدار را استفاده کنیم، بنابراین Rust درباره یک متغیر استفاده نشده به ما هشدار نمی‌دهد. -Let’s change the rules of the game: now, if you roll anything other than a 3 or -a 7, you must roll again. We no longer need to use the catch-all value, so we -can change our code to use `_` instead of the variable named `other`: +بیایید قوانین بازی را تغییر دهیم: حالا اگر بازیکن هر چیزی به غیر از 3 یا 7 بیاورد، باید دوباره تاس بیندازد. دیگر نیازی به استفاده از مقدار catch-all نیست، بنابراین می‌توانیم کد خود را به‌جای متغیری به نام `other` از `_` استفاده کنیم: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-16-underscore-catchall/src/main.rs:here}} ``` -This example also meets the exhaustiveness requirement because we’re explicitly -ignoring all other values in the last arm; we haven’t forgotten anything. +این مثال نیز نیاز تطابق exhaustive را برآورده می‌کند زیرا ما صریحاً تمام مقادیر دیگر را در بازوی آخر نادیده گرفته‌ایم و چیزی را فراموش نکرده‌ایم. -Finally, we’ll change the rules of the game one more time so that nothing else -happens on your turn if you roll anything other than a 3 or a 7. We can express -that by using the unit value (the empty tuple type we mentioned in [“The Tuple -Type”][tuples] section) as the code that goes with the `_` arm: +در نهایت، قوانین بازی را یک بار دیگر تغییر می‌دهیم، بنابراین اگر بازیکن هر چیزی غیر از 3 یا 7 بیاورد، هیچ کار دیگری در نوبت او انجام نمی‌شود. می‌توانیم این موضوع را با استفاده از مقدار واحد (نوع `tuple` خالی که قبلاً در بخش [“نوع Tuple”][tuples] ذکر شد) به عنوان کدی که با بازوی `_` همراه است بیان کنیم: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-17-underscore-unit/src/main.rs:here}} ``` -Here, we’re telling Rust explicitly that we aren’t going to use any other value -that doesn’t match a pattern in an earlier arm, and we don’t want to run any -code in this case. +اینجا، ما به Rust صریحاً می‌گوییم که قصد نداریم هیچ مقدار دیگری را که با هیچ الگویی در بازوهای قبلی مطابقت ندارد استفاده کنیم و نمی‌خواهیم در این حالت کدی اجرا کنیم. + -There’s more about patterns and matching that we’ll cover in [Chapter -19][ch19-00-patterns]. For now, we’re going to move on to the -`if let` syntax, which can be useful in situations where the `match` expression -is a bit wordy. +درباره الگوها و تطبیق آن‌ها مطالب بیشتری در [فصل 19][ch19-00-patterns] پوشش خواهیم داد. فعلاً به سینتکس `if let` می‌پردازیم که می‌تواند در مواقعی که عبارت `match` کمی طولانی به نظر می‌رسد، مفید باشد. [tuples]: ch03-02-data-types.html#the-tuple-type [ch19-00-patterns]: ch19-00-patterns.html diff --git a/src/ch06-03-if-let.md b/src/ch06-03-if-let.md index b22d7b894e..1696c10417 100644 --- a/src/ch06-03-if-let.md +++ b/src/ch06-03-if-let.md @@ -1,12 +1,8 @@ -## Concise Control Flow with `if let` and `let else` +## جریان کنترلی مختصر با `if let` و `let else` -The `if let` syntax lets you combine `if` and `let` into a less verbose way to -handle values that match one pattern while ignoring the rest. Consider the -program in Listing 6-6 that matches on an `Option` value in the -`config_max` variable but only wants to execute code if the value is the `Some` -variant. +دستور `if let` به شما اجازه می‌دهد که `if` و `let` را ترکیب کنید و به شکلی کمتر پرحجم، مقادیر مطابق با یک الگو را مدیریت کنید و سایر مقادیر را نادیده بگیرید. برنامه‌ای که در لیستینگ 6-6 نشان داده شده است، بر روی یک مقدار `Option` در متغیر `config_max` مطابقت دارد، اما تنها زمانی که مقدار `Some` باشد کد را اجرا می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-06/src/main.rs:here}} @@ -14,70 +10,50 @@ variant. -If the value is `Some`, we print out the value in the `Some` variant by binding -the value to the variable `max` in the pattern. We don’t want to do anything -with the `None` value. To satisfy the `match` expression, we have to add `_ => -()` after processing just one variant, which is annoying boilerplate code to -add. +اگر مقدار `Some` باشد، مقدار موجود در متغیر `Some` را با اتصال به متغیر `max` در الگو چاپ می‌کنیم. ما نمی‌خواهیم با مقدار `None` کاری انجام دهیم. برای برآورده کردن دستور `match`، باید `_ => ()` را بعد از پردازش تنها یک مورد اضافه کنیم، که کد اضافی آزاردهنده‌ای است. -Instead, we could write this in a shorter way using `if let`. The following -code behaves the same as the `match` in Listing 6-6: +در عوض، می‌توانیم این کد را به شکلی کوتاه‌تر با استفاده از `if let` بنویسیم. کد زیر به همان شکل `match` در لیستینگ 6-6 عمل می‌کند: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-12-if-let/src/main.rs:here}} ``` -The syntax `if let` takes a pattern and an expression separated by an equal -sign. It works the same way as a `match`, where the expression is given to the -`match` and the pattern is its first arm. In this case, the pattern is -`Some(max)`, and the `max` binds to the value inside the `Some`. We can then -use `max` in the body of the `if let` block in the same way we used `max` in -the corresponding `match` arm. The code in the `if let` block only runs if the -value matches the pattern. - -Using `if let` means less typing, less indentation, and less boilerplate code. -However, you lose the exhaustive checking `match` enforces that ensures you -aren’t forgetting to handle any cases. Choosing between `match` and `if let` -depends on what you’re doing in your particular situation and whether gaining -conciseness is an appropriate trade-off for losing exhaustive checking. - -In other words, you can think of `if let` as syntax sugar for a `match` that -runs code when the value matches one pattern and then ignores all other values. - -We can include an `else` with an `if let`. The block of code that goes with the -`else` is the same as the block of code that would go with the `_` case in the -`match` expression that is equivalent to the `if let` and `else`. Recall the -`Coin` enum definition in Listing 6-4, where the `Quarter` variant also held a -`UsState` value. If we wanted to count all non-quarter coins we see while also -announcing the state of the quarters, we could do that with a `match` -expression, like this: +دستور `if let` یک الگو و یک عبارت را می‌گیرد که با یک علامت مساوی جدا شده‌اند. این دستور همانند `match` عمل می‌کند، جایی که عبارت به `match` داده می‌شود و الگو بازوی اول آن است. در این مورد، الگو `Some(max)` است و متغیر `max` مقدار داخل `Some` را می‌گیرد. سپس می‌توانیم از `max` در بدنه بلوک `if let` همان‌طور که در بازوی متناظر `match` استفاده کردیم، استفاده کنیم. کد در بلوک `if let` تنها در صورتی اجرا می‌شود که مقدار با الگو مطابقت داشته باشد. + +استفاده از `if let` به معنای تایپ کمتر، تورفتگی کمتر، و کدنویسی قالبی (boilerplate) کمتر است. +با این حال، بررسی جامع‌ای که `match` تحمیل می‌کند را از دست می‌دهید؛ این بررسی تضمین می‌کند +که هیچ حالتی را فراموش نکرده باشید. انتخاب بین `match` و `if let` بستگی به کاری دارد که +در موقعیت خاص خود انجام می‌دهید و این‌که آیا دستیابی به اختصار، مبادله‌ای مناسب در برابر از دست دادن بررسی جامع است یا خیر. + +به عبارت دیگر، می‌توانید `if let` را به عنوان یک قند سینتکس برای `match` تصور کنید که کد را زمانی که مقدار با یک الگو مطابقت دارد اجرا می‌کند و سپس تمام مقادیر دیگر را نادیده می‌گیرد. + +ما می‌توانیم یک `else` با یک `if let` اضافه کنیم. بلوک کدی که با `else` همراه می‌شود همان بلوک کدی است که با مورد `_` در دستور `match` که معادل `if let` و `else` است همراه می‌شود. دستور `Coin` را در لیستینگ 6-4 به یاد بیاورید، جایی که نوع `Quarter` یک مقدار `UsState` را نیز در خود جای داده بود. اگر می‌خواستیم تمام سکه‌های غیر `Quarter` را که می‌بینیم بشماریم، هم‌زمان ایالت‌های سکه‌های `Quarter` را اعلام کنیم، می‌توانستیم این کار را با یک دستور `match` انجام دهیم، مانند این: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-13-count-and-announce-match/src/main.rs:here}} ``` -Or we could use an `if let` and `else` expression, like this: +یا می‌توانستیم از یک عبارت `if let` و `else` استفاده کنیم، مانند این: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-14-count-and-announce-if-let-else/src/main.rs:here}} ``` -## Staying on the “Happy Path” with `let...else` +## ماندن در «مسیر خوشحال» با `let...else` -The common pattern is to perform some computation when a value is present and -return a default value otherwise. Continuing on with our example of coins with a -`UsState` value, if we wanted to say something funny depending on how old the -state on the quarter was, we might introduce a method on `UsState` to check the -age of a state, like so: +الگوی رایج این است که وقتی یک مقدار موجود است، عملیاتی را انجام دهیم و در غیر این صورت، +یک مقدار پیش‌فرض را بازگردانیم. با ادامه دادن مثال سکه‌ها با یک مقدار `UsState`، +اگر بخواهیم بر اساس قدمت ایالتی که روی سکه‌ی ۲۵ سنتی (quarter) است، +چیزی خنده‌دار بگوییم، می‌توانیم یک متد روی `UsState` تعریف کنیم تا سن ایالت را بررسی کند، +مانند مثال زیر: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-07/src/main.rs:state}} ``` -Then we might use `if let` to match on the type of coin, introducing a `state` -variable within the body of the condition, as in Listing 6-7. +سپس ممکن است از `if let` برای مطابقت با نوع سکه استفاده کنیم، متغیری به نام `state` را در بدنه شرط معرفی کنیم، همان‌طور که در لیستینگ 6-7 نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-07/src/main.rs:describe}} @@ -85,14 +61,13 @@ variable within the body of the condition, as in Listing 6-7. -That gets the job done, but it has pushed the work into the body of the `if -let` statement, and if the work to be done is more complicated, it might be -hard to follow exactly how the top-level branches relate. We could also take -advantage of the fact that expressions produce a value either to produce the -`state` from the `if let` or to return early, as in Listing 6-8. (You could do -similar with a `match`, too.) +این کار انجام می‌شود، اما منطق اجرا را به درون بدنه‌ی عبارت `if let` منتقل کرده‌ایم، +و اگر کار مورد نظر پیچیده‌تر باشد، ممکن است دنبال کردن ارتباط شاخه‌های سطح بالا دشوار شود. +همچنین می‌توانیم از این واقعیت بهره ببریم که عبارات یک مقدار تولید می‌کنند— +یا برای تولید `state` از `if let` استفاده کنیم یا زودتر بازگردیم، همان‌طور که در لیستینگ 6-8 نشان داده شده است. +(می‌توانید کار مشابهی را با `match` نیز انجام دهید.) -+ ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-08/src/main.rs:describe}} @@ -100,20 +75,19 @@ similar with a `match`, too.) -This is a bit annoying to follow in its own way, though! One branch of the `if -let` produces a value, and the other one returns from the function entirely. +این تا حدی آزاردهنده است! یک شاخه `if let` یک مقدار تولید می‌کند و دیگری کاملاً از تابع بازمی‌گردد. -To make this common pattern nicer to express, Rust has `let...else`. The -`let...else` syntax takes a pattern on the left side and an expression on the -right, very similar to `if let`, but it does not have an `if` branch, only an -`else` branch. If the pattern matches, it will bind the value from the pattern -in the outer scope. If the pattern does _not_ match, the program will flow into -the `else` arm, which must return from the function. +برای بیان ساده‌تر این الگوی رایج، Rust ساختار `let...else` را ارائه داده است. +سینتکس `let...else` یک الگو در سمت چپ و یک عبارت در سمت راست می‌گیرد، +که بسیار شبیه به `if let` است، اما شاخه‌ی `if` ندارد و تنها یک شاخه‌ی `else` دارد. +اگر الگو با مقدار مطابقت داشته باشد، مقدار از درون الگو در حوزه‌ی بیرونی (outer scope) بایند خواهد شد. +اگر الگو *مطابقت نداشته باشد*، برنامه وارد شاخه‌ی `else` خواهد شد، +که باید از تابع بازگردد. -In Listing 6-9, you can see how Listing 6-8 looks when using `let...else` in -place of `if let`. +در لیستینگ 6-9 می‌توانید ببینید که چگونه لیستینگ 6-8 با استفاده از `let...else` +به جای `if let` بازنویسی شده است. -+ ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-09/src/main.rs:describe}} @@ -121,27 +95,16 @@ place of `if let`. -Notice that it stays “on the happy path” in the main body of the function this -way, without having significantly different control flow for two branches the -way the `if let` did. +توجه داشته باشید که با این روش، بدنه‌ی اصلی تابع در «مسیر خوشحال» باقی می‌ماند، +بدون آن‌که مانند `if let` جریان کنترل متفاوت و قابل‌توجهی بین دو شاخه ایجاد کند. -If you have a situation in which your program has logic that is too verbose to -express using a `match`, remember that `if let` and `let...else` are in your -Rust toolbox as well. +اگر در موقعیتی هستید که منطق برنامه‌تان برای بیان با استفاده از `match` بیش از حد مفصل است، +به یاد داشته باشید که `if let` و `let...else` نیز در جعبه‌ابزار Rust شما قرار دارند. -## Summary +## خلاصه -We’ve now covered how to use enums to create custom types that can be one of a -set of enumerated values. We’ve shown how the standard library’s `Option` -type helps you use the type system to prevent errors. When enum values have -data inside them, you can use `match` or `if let` to extract and use those -values, depending on how many cases you need to handle. +ما اکنون پوشش داده‌ایم که چگونه از enumها برای ایجاد انواع سفارشی که می‌توانند یکی از مجموعه مقادیر شمارش‌شده باشند استفاده کنید. ما نشان داده‌ایم که چگونه نوع `Option` از کتابخانه استاندارد به شما کمک می‌کند از سیستم نوع برای جلوگیری از خطاها استفاده کنید. وقتی مقادیر enum داده‌هایی درون خود دارند، می‌توانید از `match` یا `if let` برای استخراج و استفاده از آن مقادیر استفاده کنید، بسته به تعداد مواردی که باید مدیریت کنید. -Your Rust programs can now express concepts in your domain using structs and -enums. Creating custom types to use in your API ensures type safety: the -compiler will make certain your functions only get values of the type each -function expects. +برنامه‌های Rust شما اکنون می‌توانند مفاهیمی را در حوزه خود بیان کنند و از ساختارها و enumها استفاده کنند. ایجاد انواع سفارشی برای استفاده در API شما ایمنی نوع را تضمین می‌کند: کامپایلر مطمئن می‌شود که توابع شما فقط مقادیری از نوعی که هر تابع انتظار دارد دریافت می‌کنند. -In order to provide a well-organized API to your users that is straightforward -to use and only exposes exactly what your users will need, let’s now turn to -Rust’s modules. +برای ارائه یک API سازمان‌یافته به کاربران خود که استفاده از آن ساده باشد و فقط دقیقاً آنچه کاربران شما نیاز دارند را آشکار کند، حالا به ماژول‌های Rust می‌پردازیم. diff --git a/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md b/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md index 22afd6000f..fa8a12c222 100644 --- a/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md +++ b/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md @@ -1,48 +1,27 @@ -# Managing Growing Projects with Packages, Crates, and Modules - -As you write large programs, organizing your code will become increasingly -important. By grouping related functionality and separating code with distinct -features, you’ll clarify where to find code that implements a particular -feature and where to go to change how a feature works. - -The programs we’ve written so far have been in one module in one file. As a -project grows, you should organize code by splitting it into multiple modules -and then multiple files. A package can contain multiple binary crates and -optionally one library crate. As a package grows, you can extract parts into -separate crates that become external dependencies. This chapter covers all -these techniques. For very large projects comprising a set of interrelated -packages that evolve together, Cargo provides _workspaces_, which we’ll cover -in [“Cargo Workspaces”][workspaces] in Chapter 14. - -We’ll also discuss encapsulating implementation details, which lets you reuse -code at a higher level: once you’ve implemented an operation, other code can -call your code via its public interface without having to know how the -implementation works. The way you write code defines which parts are public for -other code to use and which parts are private implementation details that you -reserve the right to change. This is another way to limit the amount of detail -you have to keep in your head. - -A related concept is scope: the nested context in which code is written has a -set of names that are defined as “in scope.” When reading, writing, and -compiling code, programmers and compilers need to know whether a particular -name at a particular spot refers to a variable, function, struct, enum, module, -constant, or other item and what that item means. You can create scopes and -change which names are in or out of scope. You can’t have two items with the -same name in the same scope; tools are available to resolve name conflicts. - -Rust has a number of features that allow you to manage your code’s -organization, including which details are exposed, which details are private, -and what names are in each scope in your programs. These features, sometimes -collectively referred to as the _module system_, include: - -* **Packages**: A Cargo feature that lets you build, test, and share crates -* **Crates**: A tree of modules that produces a library or executable -* **Modules and use**: Let you control the organization, scope, and privacy of -paths -* **Paths**: A way of naming an item, such as a struct, function, or module - -In this chapter, we’ll cover all these features, discuss how they interact, and -explain how to use them to manage scope. By the end, you should have a solid -understanding of the module system and be able to work with scopes like a pro! +# مدیریت پروژه‌های بزرگ با بسته‌ها، جعبه‌ها (crates) و ماژول‌ها + +با نوشتن برنامه‌های بزرگ‌تر، سازماندهی کد شما اهمیت بیشتری پیدا می‌کند. با گروه‌بندی قابلیت‌های مرتبط و جدا کردن کدی که ویژگی‌های متمایزی دارد، می‌توانید مشخص کنید که کد یک ویژگی خاص در کجا پیاده‌سازی شده و کجا می‌توان آن را تغییر داد. + +برنامه‌هایی که تا این‌جا نوشته‌ایم، همگی در یک ماژول و در یک فایل بوده‌اند. +با رشد یک پروژه، باید کد را با تقسیم آن به چند ماژول و سپس چند فایل، سازمان‌دهی کنید. +یک پکیج می‌تواند شامل چندین crate دودویی باشد و به‌صورت اختیاری یک crate کتابخانه‌ای نیز داشته باشد. +با گسترش یک پکیج، می‌توانید بخش‌هایی از آن را به crateهای جداگانه استخراج کنید +که به وابستگی‌های خارجی تبدیل می‌شوند. این فصل تمام این تکنیک‌ها را پوشش می‌دهد. +برای پروژه‌های بسیار بزرگی که از مجموعه‌ای از پکیج‌های مرتبط به‌هم تشکیل شده‌اند +و با هم رشد می‌کنند، Cargo قابلیتی به نام *workspaces* ارائه می‌دهد +که آن را در فصل ۱۴ با عنوان [“Cargo Workspaces”][workspaces] بررسی خواهیم کرد. + +همچنین درباره جزئیات پیاده‌سازی که به شما امکان می‌دهد کد را در سطح بالاتری بازاستفاده کنید صحبت خواهیم کرد: وقتی یک عملیات را پیاده‌سازی کرده‌اید، سایر کدها می‌توانند از طریق رابط عمومی کد شما آن را فراخوانی کنند بدون این که لازم باشد بدانند چگونه پیاده‌سازی شده است. نحوه نوشتن کد شما مشخص می‌کند که کدام بخش‌ها برای سایر کدها عمومی و قابل استفاده هستند و کدام بخش‌ها جزئیات پیاده‌سازی خصوصی هستند که می‌توانید هر زمان بخواهید تغییر دهید. این رویکرد یکی دیگر از روش‌هایی است که مقدار جزئیاتی که باید به خاطر بسپارید را محدود می‌کند. + +یک مفهوم مرتبط، محدوده (scope) است: زمینه‌ای که در آن کد نوشته شده است و مجموعه‌ای از نام‌ها که به عنوان «در محدوده» تعریف می‌شوند. هنگام خواندن، نوشتن و کامپایل کد، برنامه‌نویسان و کامپایلرها باید بدانند که آیا یک نام خاص در یک مکان خاص به متغیر، تابع، ساختار، enum، ماژول، ثابت یا مورد دیگری اشاره دارد و معنای آن مورد چیست. شما می‌توانید محدوده‌ها ایجاد کنید و مشخص کنید که کدام نام‌ها در محدوده هستند یا خارج از آن. نمی‌توانید دو مورد با نام یکسان در یک محدوده داشته باشید؛ ابزارهایی برای رفع تعارض نام‌ها در دسترس هستند. + +Rust مجموعه‌ای از ویژگی‌ها دارد که به شما امکان می‌دهد سازماندهی کد خود را مدیریت کنید، از جمله جزئیاتی که آشکار می‌شوند، جزئیاتی که خصوصی هستند، و نام‌هایی که در هر محدوده در برنامه‌های شما قرار دارند. این ویژگی‌ها که گاهی به صورت جمعی _سیستم ماژول_ نامیده می‌شوند شامل موارد زیر هستند: + +* **Packages**: یکی از قابلیت‌های Cargo که به شما اجازه می‌دهد crateها را بسازید، تست کنید و به اشتراک بگذارید +* **Crates**: یک درخت از ماژول‌ها که یک کتابخانه یا فایل اجرایی تولید می‌کند +* **Modules و use**: به شما امکان می‌دهد سازمان‌دهی، حوزه (scope)، و سطح دسترسی مسیرها را کنترل کنید +* **Paths**: روشی برای نام‌گذاری یک آیتم، مانند یک `struct`، تابع، یا ماژول + +در این فصل، تمام این ویژگی‌ها را پوشش خواهیم داد، نحوه تعامل آن‌ها را توضیح می‌دهیم و نحوه استفاده از آن‌ها برای مدیریت محدوده را بررسی می‌کنیم. تا پایان، باید درک جامعی از سیستم ماژول داشته باشید و بتوانید با محدوده‌ها مانند یک حرفه‌ای کار کنید! [workspaces]: ch14-03-cargo-workspaces.html diff --git a/src/ch07-01-packages-and-crates.md b/src/ch07-01-packages-and-crates.md index da9ea55651..e933ebea39 100644 --- a/src/ch07-01-packages-and-crates.md +++ b/src/ch07-01-packages-and-crates.md @@ -1,45 +1,42 @@ -## Packages and Crates - -The first parts of the module system we’ll cover are packages and crates. - -A _crate_ is the smallest amount of code that the Rust compiler considers at a -time. Even if you run `rustc` rather than `cargo` and pass a single source code -file (as we did all the way back in “Writing and Running a Rust Program” in -Chapter 1), the compiler considers that file to be a crate. Crates can contain -modules, and the modules may be defined in other files that get compiled with -the crate, as we’ll see in the coming sections. - -A crate can come in one of two forms: a binary crate or a library crate. -_Binary crates_ are programs you can compile to an executable that you can run, -such as a command line program or a server. Each must have a function called -`main` that defines what happens when the executable runs. All the crates we’ve -created so far have been binary crates. - -_Library crates_ don’t have a `main` function, and they don’t compile to an -executable. Instead, they define functionality intended to be shared with -multiple projects. For example, the `rand` crate we used in [Chapter -2][rand] provides functionality that generates random numbers. -Most of the time when Rustaceans say “crate,” they mean library crate, and they -use “crate” interchangeably with the general programming concept of a “library.” - -The _crate root_ is a source file that the Rust compiler starts from and makes -up the root module of your crate (we’ll explain modules in depth in [“Defining -Modules to Control Scope and Privacy”][modules]). - -A _package_ is a bundle of one or more crates that provides a set of -functionality. A package contains a _Cargo.toml_ file that describes how to -build those crates. Cargo is actually a package that contains the binary crate -for the command line tool you’ve been using to build your code. The Cargo -package also contains a library crate that the binary crate depends on. Other -projects can depend on the Cargo library crate to use the same logic the Cargo -command line tool uses. - -A package can contain as many binary crates as you like, but at most only one -library crate. A package must contain at least one crate, whether that’s a -library or binary crate. - -Let’s walk through what happens when we create a package. First we enter the -command `cargo new my-project`: +## بسته‌ها و جعبه‌ها (crates) + +اولین بخش‌هایی که در سیستم ماژول بررسی خواهیم کرد، بسته‌ها و جعبه‌ها (crates) هستند. + +یک *crate* کوچک‌ترین واحدی از کد است که کامپایلر Rust در هر لحظه به آن توجه می‌کند. +حتی اگر به جای استفاده از `cargo`، مستقیماً `rustc` را اجرا کنید و تنها یک فایل کد منبع را (همان‌طور که در فصل اول در بخش «نوشتن و اجرای یک برنامه Rust» انجام دادیم) به آن بدهید، +کامپایلر آن فایل را به عنوان یک crate در نظر می‌گیرد. +crateها می‌توانند شامل ماژول‌هایی باشند، و این ماژول‌ها ممکن است در فایل‌های دیگری تعریف شده باشند +که هنگام کامپایل، همراه با crate پردازش می‌شوند، همان‌طور که در بخش‌های بعدی خواهیم دید. + +یک crate می‌تواند یکی از دو نوع زیر باشد: crate دودویی (binary) یا crate کتابخانه‌ای (library). +*crateهای دودویی* برنامه‌هایی هستند که می‌توانید آن‌ها را به فایل اجرایی کامپایل کرده و اجرا کنید، +مانند یک برنامه‌ی خط فرمان یا یک سرور. هر crate دودویی باید تابعی به نام `main` داشته باشد +که مشخص می‌کند هنگام اجرای فایل اجرایی، چه اتفاقی می‌افتد. +تمام crateهایی که تا این‌جا ایجاد کرده‌ایم، crateهای دودویی بوده‌اند. + +*crateهای کتابخانه‌ای* تابع `main` ندارند و به فایل اجرایی کامپایل نمی‌شوند. +در عوض، آن‌ها قابلیت‌هایی را تعریف می‌کنند که برای اشتراک‌گذاری میان پروژه‌های مختلف در نظر گرفته شده‌اند. +برای مثال، crate `rand` که در [فصل ۲][rand] از آن استفاده کردیم، قابلیت‌هایی برای تولید اعداد تصادفی فراهم می‌کند. +در اغلب موارد، زمانی که Rustaceanها از واژه‌ی “crate” استفاده می‌کنند، منظورشان crate کتابخانه‌ای است +و این واژه را به‌طور معادل با مفهوم عمومی «کتابخانه» در برنامه‌نویسی به کار می‌برند. + +*ریشه‌ی crate* (crate root) فایلی از کد منبع است که کامپایلر Rust از آن شروع می‌کند +و ماژول ریشه‌ی crate را تشکیل می‌دهد (ماژول‌ها را در بخش [“تعریف ماژول‌ها برای کنترل حوزه و سطح دسترسی”][modules] با جزئیات توضیح خواهیم داد). + +یک *package* مجموعه‌ای از یک یا چند crate است که مجموعه‌ای از قابلیت‌ها را ارائه می‌دهد. +یک package شامل یک فایل *Cargo.toml* است که مشخص می‌کند چگونه crateها باید ساخته شوند. +خود Cargo در واقع یک package است که شامل یک crate دودویی برای ابزار خط فرمانی است +که تاکنون از آن برای ساخت کد خود استفاده کرده‌اید. +پکیج Cargo همچنین شامل یک crate کتابخانه‌ای است که crate دودویی به آن وابسته است. +سایر پروژه‌ها می‌توانند به crate کتابخانه‌ای Cargo وابسته شوند تا از همان منطق استفاده کنند +که ابزار خط فرمان Cargo از آن بهره می‌برد. + +یک package می‌تواند هر تعداد crate دودویی داشته باشد، +اما در بیشترین حالت، تنها یک crate کتابخانه‌ای می‌تواند داشته باشد. +هر package باید دست‌کم شامل یک crate باشد، +چه crate کتابخانه‌ای و چه crate دودویی. + +بیایید ببینیم وقتی یک بسته ایجاد می‌کنیم چه اتفاقی می‌افتد. ابتدا دستور `cargo new my-project` را وارد می‌کنیم: ```console $ cargo new my-project @@ -51,21 +48,9 @@ $ ls my-project/src main.rs ``` -After we run `cargo new my-project`, we use `ls` to see what Cargo creates. In -the project directory, there’s a _Cargo.toml_ file, giving us a package. -There’s also a _src_ directory that contains _main.rs_. Open _Cargo.toml_ in -your text editor, and note there’s no mention of _src/main.rs_. Cargo follows a -convention that _src/main.rs_ is the crate root of a binary crate with the same -name as the package. Likewise, Cargo knows that if the package directory -contains _src/lib.rs_, the package contains a library crate with the same name -as the package, and _src/lib.rs_ is its crate root. Cargo passes the crate root -files to `rustc` to build the library or binary. +بعد از اجرای `cargo new my-project`، از دستور `ls` استفاده می‌کنیم تا ببینیم Cargo چه چیزی ایجاد کرده است. در دایرکتوری پروژه، یک فایل _Cargo.toml_ وجود دارد که به ما یک بسته می‌دهد. همچنین یک دایرکتوری _src_ وجود دارد که شامل فایل _main.rs_ است. فایل _Cargo.toml_ را در ویرایشگر متن خود باز کنید و توجه کنید که هیچ اشاره‌ای به _src/main.rs_ نشده است. Cargo از یک قرارداد پیروی می‌کند که _src/main.rs_ ریشه جعبه (crate) یک جعبه (crate) باینری با همان نام بسته است. به همین ترتیب، Cargo می‌داند که اگر دایرکتوری بسته شامل _src/lib.rs_ باشد، بسته شامل یک جعبه (crate) کتابخانه‌ای با همان نام بسته است و _src/lib.rs_ ریشه جعبه (crate) آن است. Cargo فایل‌های ریشه جعبه (crate) را به `rustc` ارسال می‌کند تا کتابخانه یا فایل اجرایی ساخته شود. -Here, we have a package that only contains _src/main.rs_, meaning it only -contains a binary crate named `my-project`. If a package contains _src/main.rs_ -and _src/lib.rs_, it has two crates: a binary and a library, both with the same -name as the package. A package can have multiple binary crates by placing files -in the _src/bin_ directory: each file will be a separate binary crate. +در اینجا، ما یک بسته داریم که تنها شامل _src/main.rs_ است، به این معنی که تنها یک جعبه (crate) باینری به نام `my-project` دارد. اگر یک بسته شامل _src/main.rs_ و _src/lib.rs_ باشد، آن بسته دو جعبه (crate) خواهد داشت: یک جعبه (crate) باینری و یک کتابخانه، هر دو با همان نام بسته. یک بسته می‌تواند چندین جعبه (crate) باینری داشته باشد با قرار دادن فایل‌ها در دایرکتوری _src/bin_: هر فایل یک جعبه (crate) باینری جداگانه خواهد بود. [modules]: ch07-02-defining-modules-to-control-scope-and-privacy.html [rand]: ch02-00-guessing-game-tutorial.html#generating-a-random-number diff --git a/src/ch07-02-defining-modules-to-control-scope-and-privacy.md b/src/ch07-02-defining-modules-to-control-scope-and-privacy.md index 37a41e3c2c..207f805a28 100644 --- a/src/ch07-02-defining-modules-to-control-scope-and-privacy.md +++ b/src/ch07-02-defining-modules-to-control-scope-and-privacy.md @@ -1,54 +1,26 @@ -## Defining Modules to Control Scope and Privacy - -In this section, we’ll talk about modules and other parts of the module system, -namely _paths_, which allow you to name items; the `use` keyword that brings a -path into scope; and the `pub` keyword to make items public. We’ll also discuss -the `as` keyword, external packages, and the glob operator. - -### Modules Cheat Sheet - -Before we get to the details of modules and paths, here we provide a quick -reference on how modules, paths, the `use` keyword, and the `pub` keyword work -in the compiler, and how most developers organize their code. We’ll be going -through examples of each of these rules throughout this chapter, but this is a -great place to refer to as a reminder of how modules work. - -- **Start from the crate root**: When compiling a crate, the compiler first - looks in the crate root file (usually _src/lib.rs_ for a library crate or - _src/main.rs_ for a binary crate) for code to compile. -- **Declaring modules**: In the crate root file, you can declare new modules; - say you declare a “garden” module with `mod garden;`. The compiler will look - for the module’s code in these places: - - Inline, within curly brackets that replace the semicolon following `mod - garden` - - In the file _src/garden.rs_ - - In the file _src/garden/mod.rs_ -- **Declaring submodules**: In any file other than the crate root, you can - declare submodules. For example, you might declare `mod vegetables;` in - _src/garden.rs_. The compiler will look for the submodule’s code within the - directory named for the parent module in these places: - - Inline, directly following `mod vegetables`, within curly brackets instead - of the semicolon - - In the file _src/garden/vegetables.rs_ - - In the file _src/garden/vegetables/mod.rs_ -- **Paths to code in modules**: Once a module is part of your crate, you can - refer to code in that module from anywhere else in that same crate, as long - as the privacy rules allow, using the path to the code. For example, an - `Asparagus` type in the garden vegetables module would be found at +## تعریف ماژول‌ها برای کنترل محدوده و حریم خصوصی + +در این بخش، ما درباره ماژول‌ها و سایر بخش‌های سیستم ماژول صحبت خواهیم کرد، یعنی _مسیرها_ که به شما امکان می‌دهند آیتم‌ها را نام‌گذاری کنید؛ کلمه کلیدی `use` که مسیر را به محدوده وارد می‌کند؛ و کلمه کلیدی `pub` برای عمومی کردن آیتم‌ها. همچنین درباره کلمه کلیدی `as`، بسته‌های خارجی، و عملگر `glob` صحبت خواهیم کرد. + +### خلاصه‌ای از ماژول‌ها + +قبل از اینکه به جزئیات ماژول‌ها و مسیرها بپردازیم، اینجا یک مرجع سریع در مورد نحوه عملکرد ماژول‌ها، مسیرها، کلمه کلیدی `use` و کلمه کلیدی `pub` در کامپایلر ارائه می‌دهیم و همچنین نحوه سازماندهی کد توسط اکثر توسعه‌دهندگان را نشان می‌دهیم. ما در طول این فصل به مثال‌هایی از هر یک از این قواعد خواهیم پرداخت، اما این یک مکان عالی برای یادآوری نحوه عملکرد ماژول‌ها است. + +- **شروع از ریشه جعبه (crate):** هنگام کامپایل یک جعبه (crate)، کامپایلر ابتدا در فایل ریشه جعبه (crate) (معمولاً _src/lib.rs_ برای یک جعبه (crate) کتابخانه‌ای یا _src/main.rs_ برای یک جعبه (crate) باینری) به دنبال کد برای کامپایل می‌گردد. +- **تعریف ماژول‌ها:** در فایل ریشه جعبه (crate)، می‌توانید ماژول‌های جدید تعریف کنید؛ مثلاً می‌توانید یک ماژول "garden" با `mod garden;` تعریف کنید. کامپایلر کد ماژول را در مکان‌های زیر جستجو می‌کند: + - به صورت درون‌خطی، داخل براکت‌های موج‌دار که به جای علامت نقطه‌ویرگول بعد از `mod garden` قرار می‌گیرند. + - در فایل _src/garden.rs_ + - در فایل _src/garden/mod.rs_ +- **تعریف زیرماژول‌ها:** در هر فایلی به جز فایل ریشه جعبه (crate)، می‌توانید زیرماژول‌ها تعریف کنید. برای مثال، ممکن است `mod vegetables;` را در فایل _src/garden.rs_ تعریف کنید. کامپایلر کد زیرماژول را در دایرکتوری‌ای که به نام ماژول والد است، در مکان‌های زیر جستجو می‌کند: + - به صورت درون‌خطی، مستقیماً بعد از `mod vegetables`، داخل براکت‌های موج‌دار به جای نقطه‌ویرگول + - در فایل _src/garden/vegetables.rs_ + - در فایل _src/garden/vegetables/mod.rs_ +- **مسیرها به کد در ماژول‌ها:** وقتی یک ماژول بخشی از جعبه (crate) شما باشد، می‌توانید از هر جای دیگر در همان جعبه (crate) (تا زمانی که قواعد حریم خصوصی اجازه دهند) با استفاده از مسیر به کد آن ارجاع دهید. برای مثال، یک نوع `Asparagus` در ماژول vegetables در garden به این صورت پیدا می‌شود: `crate::garden::vegetables::Asparagus`. -- **Private vs. public**: Code within a module is private from its parent - modules by default. To make a module public, declare it with `pub mod` - instead of `mod`. To make items within a public module public as well, use - `pub` before their declarations. -- **The `use` keyword**: Within a scope, the `use` keyword creates shortcuts to - items to reduce repetition of long paths. In any scope that can refer to - `crate::garden::vegetables::Asparagus`, you can create a shortcut with `use - crate::garden::vegetables::Asparagus;` and from then on you only need to - write `Asparagus` to make use of that type in the scope. - -Here, we create a binary crate named `backyard` that illustrates these rules. -The crate’s directory, also named `backyard`, contains these files and -directories: +- **خصوصی در مقابل عمومی:** کد درون یک ماژول به صورت پیش‌فرض برای ماژول‌های والد خصوصی است. برای عمومی کردن یک ماژول، آن را با `pub mod` به جای `mod` تعریف کنید. برای عمومی کردن آیتم‌های داخل یک ماژول عمومی، از `pub` قبل از اعلان آن‌ها استفاده کنید. +- **کلمه کلیدی `use`:** در یک محدوده، کلمه کلیدی `use` میانبری به آیتم‌ها ایجاد می‌کند تا تکرار مسیرهای طولانی کاهش یابد. در هر محدوده‌ای که می‌تواند به `crate::garden::vegetables::Asparagus` ارجاع دهد، می‌توانید یک میانبر با `use crate::garden::vegetables::Asparagus;` ایجاد کنید و از آن به بعد فقط کافی است `Asparagus` را در آن محدوده استفاده کنید. + +اینجا، ما یک جعبه (crate) باینری به نام `backyard` ایجاد می‌کنیم که این قواعد را نشان می‌دهد. دایرکتوری جعبه (crate) که آن هم `backyard` نامیده می‌شود شامل این فایل‌ها و دایرکتوری‌ها است: ```text backyard @@ -61,7 +33,7 @@ backyard └── main.rs ``` -The crate root file in this case is _src/main.rs_, and it contains: +فایل ریشه جعبه (crate) در اینجا _src/main.rs_ است و حاوی موارد زیر است: @@ -71,8 +43,7 @@ The crate root file in this case is _src/main.rs_, and it contains: -The `pub mod garden;` line tells the compiler to include the code it finds in -_src/garden.rs_, which is: +خط `pub mod garden;` به کامپایلر می‌گوید که کدی را که در _src/garden.rs_ پیدا می‌کند وارد کند، که شامل موارد زیر است: @@ -82,43 +53,25 @@ _src/garden.rs_, which is: -Here, `pub mod vegetables;` means the code in _src/garden/vegetables.rs_ is -included too. That code is: +اینجا، `pub mod vegetables;` به این معنا است که کد موجود در _src/garden/vegetables.rs_ نیز وارد می‌شود. آن کد به صورت زیر است: ```rust,noplayground,ignore {{#rustdoc_include ../listings/ch07-managing-growing-projects/quick-reference-example/src/garden/vegetables.rs}} ``` -Now let’s get into the details of these rules and demonstrate them in action! +حالا بیایید به جزئیات این قواعد بپردازیم و آن‌ها را در عمل نشان دهیم! -### Grouping Related Code in Modules +### گروه‌بندی کدهای مرتبط در ماژول‌ها -_Modules_ let us organize code within a crate for readability and easy reuse. -Modules also allow us to control the _privacy_ of items because code within a -module is private by default. Private items are internal implementation details -not available for outside use. We can choose to make modules and the items -within them public, which exposes them to allow external code to use and depend -on them. +_ماژول‌ها_ به ما امکان می‌دهند کد را در یک جعبه (crate) برای خوانایی و بازاستفاده آسان سازماندهی کنیم. ماژول‌ها همچنین به ما امکان کنترل _حریم خصوصی_ آیتم‌ها را می‌دهند زیرا کد درون یک ماژول به صورت پیش‌فرض خصوصی است. آیتم‌های خصوصی جزئیات پیاده‌سازی داخلی هستند که برای استفاده خارجی در دسترس نیستند. ما می‌توانیم انتخاب کنیم که ماژول‌ها و آیتم‌های درون آن‌ها عمومی باشند، که این موارد را برای استفاده خارجی آشکار می‌کند. -As an example, let’s write a library crate that provides the functionality of a -restaurant. We’ll define the signatures of functions but leave their bodies -empty to concentrate on the organization of the code rather than the -implementation of a restaurant. +برای مثال، بیایید یک جعبه (crate) کتابخانه‌ای بنویسیم که عملکرد یک رستوران را ارائه دهد. امضای توابع را تعریف می‌کنیم اما بدنه آن‌ها را خالی می‌گذاریم تا بیشتر بر سازماندهی کد تمرکز کنیم تا پیاده‌سازی عملکرد یک رستوران. -In the restaurant industry, some parts of a restaurant are referred to as -_front of house_ and others as _back of house_. Front of house is where -customers are; this encompasses where the hosts seat customers, servers take -orders and payment, and bartenders make drinks. Back of house is where the -chefs and cooks work in the kitchen, dishwashers clean up, and managers do -administrative work. +در صنعت رستوران، برخی قسمت‌های یک رستوران به عنوان _جلوی خانه_ و دیگر قسمت‌ها به عنوان _پشت خانه_ شناخته می‌شوند. جلوی خانه جایی است که مشتریان هستند؛ این شامل جایی است که میزبان‌ها مشتریان را می‌نشانند، گارسون‌ها سفارش می‌گیرند و پرداخت‌ها را انجام می‌دهند، و بارتندرها نوشیدنی درست می‌کنند. پشت خانه جایی است که سرآشپزها و آشپزها در آشپزخانه کار می‌کنند، ظرف‌شورها ظروف را تمیز می‌کنند، و مدیران کارهای اداری انجام می‌دهند. -To structure our crate in this way, we can organize its functions into nested -modules. Create a new library named `restaurant` by running `cargo new -restaurant --lib`. Then enter the code in Listing 7-1 into _src/lib.rs_ to -define some modules and function signatures; this code is the front of house -section. +برای ساختاردهی جعبه (crate) خود به این روش، می‌توانیم عملکردها را در ماژول‌های تو در تو سازماندهی کنیم. یک کتابخانه جدید به نام `restaurant` با اجرای دستور `cargo new restaurant --lib` ایجاد کنید. سپس کد لیستینگ 7-1 را در _src/lib.rs_ وارد کنید تا برخی ماژول‌ها و امضای توابع تعریف شود. این کد بخش جلوی خانه را تعریف می‌کند. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-01/src/lib.rs}} @@ -126,27 +79,23 @@ section. -We define a module with the `mod` keyword followed by the name of the module -(in this case, `front_of_house`). The body of the module then goes inside curly -brackets. Inside modules, we can place other modules, as in this case with the -modules `hosting` and `serving`. Modules can also hold definitions for other -items, such as structs, enums, constants, traits, and as in Listing 7-1, -functions. +ماژولی را با استفاده از کلیدواژه‌ی `mod` و به‌دنبال آن نام ماژول تعریف می‌کنیم +(در این مثال، `front_of_house`). بدنه‌ی ماژول درون آکولاد قرار می‌گیرد. +درون ماژول‌ها می‌توان ماژول‌های دیگری نیز قرار داد، همان‌طور که در این مثال، +ماژول‌های `hosting` و `serving` درون `front_of_house` قرار گرفته‌اند. +ماژول‌ها همچنین می‌توانند شامل تعریف آیتم‌های دیگر نیز باشند، +مانند `struct`ها، `enum`ها، ثابت‌ها (constants)، `trait`ها، +و همان‌طور که در لیستینگ 7-1 می‌بینید، توابع. + +با استفاده از ماژول‌ها، می‌توانیم تعاریف مرتبط را با هم گروه‌بندی کنیم و دلیل ارتباط آن‌ها را نام‌گذاری کنیم. برنامه‌نویسانی که از این کد استفاده می‌کنند می‌توانند بر اساس گروه‌ها کد را مرور کنند، به جای اینکه مجبور باشند تمام تعاریف را بخوانند. این کار پیدا کردن تعاریف مرتبط با آن‌ها را آسان‌تر می‌کند. برنامه‌نویسانی که عملکرد جدیدی به این کد اضافه می‌کنند می‌دانند که کد را کجا قرار دهند تا برنامه سازماندهی شده باقی بماند. -By using modules, we can group related definitions together and name why -they’re related. Programmers using this code can navigate the code based on the -groups rather than having to read through all the definitions, making it easier -to find the definitions relevant to them. Programmers adding new functionality -to this code would know where to place the code to keep the program organized. +### درخت ماژول -Earlier, we mentioned that _src/main.rs_ and _src/lib.rs_ are called crate -roots. The reason for their name is that the contents of either of these two -files form a module named `crate` at the root of the crate’s module structure, -known as the _module tree_. +قبلاً اشاره کردیم که _src/main.rs_ و _src/lib.rs_ به نام ریشه جعبه (crate) شناخته می‌شوند. دلیل نام‌گذاری آن‌ها این است که محتوای هر یک از این دو فایل یک ماژول به نام `crate` را در ریشه ساختار ماژول جعبه (crate) تشکیل می‌دهند، که به عنوان _درخت ماژول_ شناخته می‌شود. -Listing 7-2 shows the module tree for the structure in Listing 7-1. +لیستینگ 7-2 درخت ماژول را برای ساختار موجود در لیستینگ 7-1 نشان می‌دهد. -+ ```text crate @@ -162,15 +111,6 @@ crate -This tree shows how some of the modules nest inside other modules; for example, -`hosting` nests inside `front_of_house`. The tree also shows that some modules -are _siblings_, meaning they’re defined in the same module; `hosting` and -`serving` are siblings defined within `front_of_house`. If module A is -contained inside module B, we say that module A is the _child_ of module B and -that module B is the _parent_ of module A. Notice that the entire module tree -is rooted under the implicit module named `crate`. - -The module tree might remind you of the filesystem’s directory tree on your -computer; this is a very apt comparison! Just like directories in a filesystem, -you use modules to organize your code. And just like files in a directory, we -need a way to find our modules. +این درخت نشان می‌دهد که برخی از ماژول‌ها در داخل ماژول‌های دیگر قرار دارند؛ برای مثال، `hosting` در داخل `front_of_house` قرار دارد. درخت همچنین نشان می‌دهد که برخی از ماژول‌ها _هم‌سطح_ هستند، به این معنی که در همان ماژول تعریف شده‌اند؛ `hosting` و `serving` هم‌سطح هستند و درون `front_of_house` تعریف شده‌اند. اگر ماژول A درون ماژول B قرار گیرد، می‌گوییم ماژول A _فرزند_ ماژول B است و ماژول B _والد_ ماژول A است. توجه کنید که کل درخت ماژول در زیر ماژول ضمنی به نام `crate` ریشه دارد. + +درخت ماژول ممکن است شما را به یاد درخت دایرکتوری‌های فایل‌سیستم کامپیوتر بیندازد؛ این مقایسه بسیار مناسبی است! درست همان‌طور که دایرکتوری‌ها در فایل‌سیستم کد را سازماندهی می‌کنند، شما می‌توانید از ماژول‌ها برای سازماندهی کد خود استفاده کنید. و درست مانند فایل‌ها در یک دایرکتوری، ما نیاز به روشی برای پیدا کردن ماژول‌ها داریم. diff --git a/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md b/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md index 63e49420e9..ca5c932352 100644 --- a/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md +++ b/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md @@ -1,35 +1,21 @@ -## Paths for Referring to an Item in the Module Tree +## مسیرها برای اشاره به یک آیتم در درخت ماژول -To show Rust where to find an item in a module tree, we use a path in the same -way we use a path when navigating a filesystem. To call a function, we need to -know its path. +برای نشان دادن به Rust که یک آیتم را در درخت ماژول کجا پیدا کند، از یک مسیر استفاده می‌کنیم، مشابه استفاده از مسیر هنگام پیمایش در یک فایل‌سیستم. برای فراخوانی یک تابع، باید مسیر آن را بدانیم. -A path can take two forms: +یک مسیر می‌تواند به دو شکل باشد: -- An _absolute path_ is the full path starting from a crate root; for code - from an external crate, the absolute path begins with the crate name, and for - code from the current crate, it starts with the literal `crate`. -- A _relative path_ starts from the current module and uses `self`, `super`, or - an identifier in the current module. +- یک _مسیر مطلق_ مسیری کامل است که از ریشه جعبه (crate) شروع می‌شود؛ برای کدی که از یک جعبه (crate) خارجی می‌آید، مسیر مطلق با نام جعبه (crate) شروع می‌شود، و برای کدی که از جعبه (crate) فعلی می‌آید، با کلمه کلیدی `crate` شروع می‌شود. +- یک _مسیر نسبی_ از ماژول فعلی شروع می‌شود و از `self`، `super` یا یک شناسه در ماژول فعلی استفاده می‌کند. -Both absolute and relative paths are followed by one or more identifiers -separated by double colons (`::`). +هر دو مسیر مطلق و نسبی با یک یا چند شناسه که با دو نقطه دوبل (`::`) جدا شده‌اند دنبال می‌شوند. -Returning to Listing 7-1, say we want to call the `add_to_waitlist` function. -This is the same as asking: what’s the path of the `add_to_waitlist` function? -Listing 7-3 contains Listing 7-1 with some of the modules and functions -removed. +با بازگشت به لیستینگ 7-1، فرض کنید که می‌خواهیم تابع `add_to_waitlist` را فراخوانی کنیم. این کار مشابه پرسیدن این است: مسیر تابع `add_to_waitlist` چیست؟ لیستینگ 7-3 شامل لیستینگ 7-1 با حذف برخی از ماژول‌ها و توابع است. -We’ll show two ways to call the `add_to_waitlist` function from a new function, -`eat_at_restaurant`, defined in the crate root. These paths are correct, but -there’s another problem remaining that will prevent this example from compiling -as is. We’ll explain why in a bit. +ما دو روش برای فراخوانی تابع `add_to_waitlist` از یک تابع جدید، `eat_at_restaurant`، که در ریشه جعبه (crate) تعریف شده است، نشان خواهیم داد. این مسیرها درست هستند، اما یک مشکل دیگر وجود دارد که مانع کامپایل این مثال به شکل فعلی می‌شود. بعداً توضیح خواهیم داد که چرا. -The `eat_at_restaurant` function is part of our library crate’s public API, so -we mark it with the `pub` keyword. In the [“Exposing Paths with the `pub` -Keyword”][pub] section, we’ll go into more detail about `pub`. +تابع `eat_at_restaurant` بخشی از API عمومی جعبه (crate) کتابخانه‌ای ما است، بنابراین آن را با کلمه کلیدی `pub` علامت می‌زنیم. در بخش [«آشکار کردن مسیرها با کلمه کلیدی `pub`»][pub]، به جزئیات بیشتری درباره `pub` خواهیم پرداخت. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-03/src/lib.rs}} @@ -37,39 +23,17 @@ Keyword”][pub] section, we’ll go into more detail about `pub` -The first time we call the `add_to_waitlist` function in `eat_at_restaurant`, -we use an absolute path. The `add_to_waitlist` function is defined in the same -crate as `eat_at_restaurant`, which means we can use the `crate` keyword to -start an absolute path. We then include each of the successive modules until we -make our way to `add_to_waitlist`. You can imagine a filesystem with the same -structure: we’d specify the path `/front_of_house/hosting/add_to_waitlist` to -run the `add_to_waitlist` program; using the `crate` name to start from the -crate root is like using `/` to start from the filesystem root in your shell. - -The second time we call `add_to_waitlist` in `eat_at_restaurant`, we use a -relative path. The path starts with `front_of_house`, the name of the module -defined at the same level of the module tree as `eat_at_restaurant`. Here the -filesystem equivalent would be using the path -`front_of_house/hosting/add_to_waitlist`. Starting with a module name means -that the path is relative. - -Choosing whether to use a relative or absolute path is a decision you’ll make -based on your project, and it depends on whether you’re more likely to move -item definition code separately from or together with the code that uses the -item. For example, if we moved the `front_of_house` module and the -`eat_at_restaurant` function into a module named `customer_experience`, we’d -need to update the absolute path to `add_to_waitlist`, but the relative path -would still be valid. However, if we moved the `eat_at_restaurant` function -separately into a module named `dining`, the absolute path to the -`add_to_waitlist` call would stay the same, but the relative path would need to -be updated. Our preference in general is to specify absolute paths because it’s -more likely we’ll want to move code definitions and item calls independently of -each other. - -Let’s try to compile Listing 7-3 and find out why it won’t compile yet! The -errors we get are shown in Listing 7-4. - -+بار اولی که تابع `add_to_waitlist` را در `eat_at_restaurant` فراخوانی می‌کنیم، از یک مسیر مطلق استفاده می‌کنیم. تابع `add_to_waitlist` در همان جعبه (crate) تعریف شده است که `eat_at_restaurant` در آن قرار دارد، که به این معنی است که می‌توانیم از کلمه کلیدی `crate` برای شروع مسیر مطلق استفاده کنیم. سپس هر یک از ماژول‌های متوالی را شامل می‌کنیم تا به `add_to_waitlist` برسیم. می‌توانید یک فایل‌سیستم با ساختار مشابه تصور کنید: ما مسیر `/front_of_house/hosting/add_to_waitlist` را برای اجرای برنامه `add_to_waitlist` مشخص می‌کنیم؛ استفاده از نام `crate` برای شروع از ریشه جعبه (crate) مانند استفاده از `/` برای شروع از ریشه فایل‌سیستم در شل است. + +بار دوم که تابع `add_to_waitlist` را در `eat_at_restaurant` فراخوانی می‌کنیم، از یک مسیر نسبی استفاده می‌کنیم. مسیر با `front_of_house` شروع می‌شود، که نام ماژولی است که در همان سطح از درخت ماژول به عنوان `eat_at_restaurant` تعریف شده است. اینجا معادل فایل‌سیستم استفاده از مسیر `front_of_house/hosting/add_to_waitlist` است. شروع با نام ماژول به این معنی است که مسیر نسبی است. + +### انتخاب بین مسیرهای مطلق و نسبی + +انتخاب بین استفاده از مسیر نسبی یا مطلق یک تصمیم است که بر اساس پروژه شما گرفته می‌شود، و به این بستگی دارد که آیا احتمال بیشتری دارد کد تعریف آیتم را به طور مستقل از یا همراه با کدی که از آیتم استفاده می‌کند جابجا کنید. برای مثال، اگر ماژول `front_of_house` و تابع `eat_at_restaurant` را به یک ماژول به نام `customer_experience` منتقل کنیم، باید مسیر مطلق به `add_to_waitlist` را به‌روزرسانی کنیم، اما مسیر نسبی همچنان معتبر خواهد بود. با این حال، اگر تابع `eat_at_restaurant` را به طور مستقل به یک ماژول به نام `dining` منتقل کنیم، مسیر مطلق به فراخوانی `add_to_waitlist` تغییر نمی‌کند، اما مسیر نسبی باید به‌روزرسانی شود. ترجیح ما به طور کلی این است که مسیرهای مطلق را مشخص کنیم زیرا احتمال بیشتری دارد که بخواهیم تعریف کد و فراخوانی آیتم‌ها را مستقل از یکدیگر جابجا کنیم. + +بیایید سعی کنیم کد لیستینگ 7-3 را کامپایل کنیم و ببینیم چرا هنوز کامپایل نمی‌شود! خطاهایی که دریافت می‌کنیم در لیستینگ 7-4 نشان داده شده‌اند. + + ```console {{#include ../listings/ch07-managing-growing-projects/listing-07-03/output.txt}} @@ -77,35 +41,17 @@ errors we get are shown in Listing 7-4. -The error messages say that module `hosting` is private. In other words, we -have the correct paths for the `hosting` module and the `add_to_waitlist` -function, but Rust won’t let us use them because it doesn’t have access to the -private sections. In Rust, all items (functions, methods, structs, enums, -modules, and constants) are private to parent modules by default. If you want -to make an item like a function or struct private, you put it in a module. +پیام‌های خطا می‌گویند که ماژول `hosting` خصوصی است. به عبارت دیگر، ما مسیرهای صحیح برای ماژول `hosting` و تابع `add_to_waitlist` داریم، اما Rust به ما اجازه نمی‌دهد از آن‌ها استفاده کنیم زیرا به بخش‌های خصوصی دسترسی ندارد. در Rust، تمام آیتم‌ها (توابع، متدها، ساختارها، enumها، ماژول‌ها و ثابت‌ها) به صورت پیش‌فرض برای ماژول‌های والد خصوصی هستند. اگر بخواهید آیتمی مانند یک تابع یا ساختار را خصوصی کنید، آن را در یک ماژول قرار می‌دهید. -Items in a parent module can’t use the private items inside child modules, but -items in child modules can use the items in their ancestor modules. This is -because child modules wrap and hide their implementation details, but the child -modules can see the context in which they’re defined. To continue with our -metaphor, think of the privacy rules as being like the back office of a -restaurant: what goes on in there is private to restaurant customers, but -office managers can see and do everything in the restaurant they operate. +آیتم‌های موجود در یک ماژول والد نمی‌توانند از آیتم‌های خصوصی درون ماژول‌های فرزند استفاده کنند، اما آیتم‌های درون ماژول‌های فرزند می‌توانند از آیتم‌های ماژول‌های اجداد خود استفاده کنند. این به این دلیل است که ماژول‌های فرزند جزئیات پیاده‌سازی خود را بسته‌بندی و پنهان می‌کنند، اما ماژول‌های فرزند می‌توانند زمینه‌ای که در آن تعریف شده‌اند را ببینند. برای ادامه مثال، قواعد حریم خصوصی را مانند دفتر پشتی یک رستوران تصور کنید: آنچه در آنجا می‌گذرد برای مشتریان رستوران خصوصی است، اما مدیران دفتر می‌توانند همه چیز را در رستوران ببینند و انجام دهند. -Rust chose to have the module system function this way so that hiding inner -implementation details is the default. That way, you know which parts of the -inner code you can change without breaking outer code. However, Rust does give -you the option to expose inner parts of child modules’ code to outer ancestor -modules by using the `pub` keyword to make an item public. +Rust تصمیم گرفته است که سیستم ماژول به این صورت کار کند تا پنهان کردن جزئیات پیاده‌سازی داخلی به صورت پیش‌فرض باشد. به این ترتیب، می‌دانید کدام بخش‌های کد داخلی را می‌توانید تغییر دهید بدون اینکه کد بیرونی را خراب کنید. با این حال، Rust به شما این امکان را می‌دهد که بخش‌های داخلی کد ماژول‌های فرزند را به ماژول‌های اجداد بیرونی با استفاده از کلمه کلیدی `pub` عمومی کنید. -### Exposing Paths with the `pub` Keyword +### آشکار کردن مسیرها با کلمه کلیدی `pub` -Let’s return to the error in Listing 7-4 that told us the `hosting` module is -private. We want the `eat_at_restaurant` function in the parent module to have -access to the `add_to_waitlist` function in the child module, so we mark the -`hosting` module with the `pub` keyword, as shown in Listing 7-5. +بیایید به خطای لیستینگ 7-4 برگردیم که به ما گفت ماژول `hosting` خصوصی است. ما می‌خواهیم تابع `eat_at_restaurant` در ماژول والد به تابع `add_to_waitlist` در ماژول فرزند دسترسی داشته باشد، بنابراین ماژول `hosting` را با کلمه کلیدی `pub` علامت می‌زنیم، همان‌طور که در لیستینگ 7-5 نشان داده شده است. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-05/src/lib.rs:here}} @@ -113,10 +59,9 @@ access to the `add_to_waitlist` function in the child module, so we mark the -Unfortunately, the code in Listing 7-5 still results in compiler errors, as -shown in Listing 7-6. +متأسفانه، کد در لیستینگ 7-5 همچنان به خطاهای کامپایلر منجر می‌شود، همان‌طور که در لیستینگ 7-6 نشان داده شده است. -+ ```console {{#include ../listings/ch07-managing-growing-projects/listing-07-05/output.txt}} @@ -124,23 +69,13 @@ shown in Listing 7-6. -What happened? Adding the `pub` keyword in front of `mod hosting` makes the -module public. With this change, if we can access `front_of_house`, we can -access `hosting`. But the _contents_ of `hosting` are still private; making the -module public doesn’t make its contents public. The `pub` keyword on a module -only lets code in its ancestor modules refer to it, not access its inner code. -Because modules are containers, there’s not much we can do by only making the -module public; we need to go further and choose to make one or more of the -items within the module public as well. +چه اتفاقی افتاد؟ اضافه کردن کلمه کلیدی `pub` در جلوی `mod hosting` ماژول را عمومی می‌کند. با این تغییر، اگر به `front_of_house` دسترسی داشته باشیم، می‌توانیم به `hosting` نیز دسترسی داشته باشیم. اما _محتویات_ `hosting` همچنان خصوصی است؛ عمومی کردن ماژول به معنای عمومی کردن محتوای آن نیست. کلمه کلیدی `pub` روی یک ماژول فقط به کدهای موجود در ماژول‌های اجداد اجازه می‌دهد به آن ارجاع دهند، نه اینکه به کد داخلی آن دسترسی داشته باشند. از آنجایی که ماژول‌ها به عنوان ظرف عمل می‌کنند، تنها عمومی کردن ماژول کافی نیست؛ باید فراتر رفته و یک یا چند مورد از آیتم‌های درون ماژول را نیز عمومی کنیم. -The errors in Listing 7-6 say that the `add_to_waitlist` function is private. -The privacy rules apply to structs, enums, functions, and methods as well as -modules. +خطاهای موجود در لیستینگ 7-6 نشان می‌دهند که تابع `add_to_waitlist` خصوصی است. قواعد حریم خصوصی برای ساختارها، enumها، توابع، متدها و همچنین ماژول‌ها اعمال می‌شوند. -Let’s also make the `add_to_waitlist` function public by adding the `pub` -keyword before its definition, as in Listing 7-7. +بیایید تابع `add_to_waitlist` را نیز با اضافه کردن کلمه کلیدی `pub` قبل از تعریف آن عمومی کنیم، همان‌طور که در لیستینگ 7-7 نشان داده شده است. -+ ```rust,noplayground,test_harness {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-07/src/lib.rs:here}} @@ -174,39 +109,34 @@ If you plan on sharing your library crate so other projects can use your code, your public API is your contract with users of your crate that determines how they can interact with your code. There are many considerations around managing changes to your public API to make it easier for people to depend on your -crate. These considerations are beyond the scope of this book; if you’re +crate. These considerations are out of the scope of this book; if you’re interested in this topic, see [The Rust API Guidelines][api-guidelines]. -> #### Best Practices for Packages with a Binary and a Library +> #### بهترین شیوه‌ها برای بسته‌هایی که یک جعبه (crate) باینری و یک جعبه (crate) کتابخانه‌ای دارند > > We mentioned that a package can contain both a _src/main.rs_ binary crate > root as well as a _src/lib.rs_ library crate root, and both crates will have > the package name by default. Typically, packages with this pattern of > containing both a library and a binary crate will have just enough code in the -> binary crate to start an executable that calls code defined in the library -> crate. This lets other projects benefit from the most functionality that the +> binary crate to start an executable that calls code within the library crate. +> This lets other projects benefit from most of the functionality that the > package provides because the library crate’s code can be shared. > -> The module tree should be defined in _src/lib.rs_. Then, any public items can -> be used in the binary crate by starting paths with the name of the package. -> The binary crate becomes a user of the library crate just like a completely -> external crate would use the library crate: it can only use the public API. -> This helps you design a good API; not only are you the author, you’re also a -> client! +> درخت ماژول باید در _src/lib.rs_ تعریف شود. سپس، هر آیتم عمومی را می‌توان در جعبه (crate) باینری با شروع مسیرها با نام بسته استفاده کرد. جعبه (crate) باینری به یک کاربر از جعبه (crate) کتابخانه‌ای تبدیل می‌شود، درست مثل اینکه یک جعبه (crate) کاملاً خارجی از جعبه (crate) کتابخانه‌ای استفاده می‌کند: تنها می‌تواند از API عمومی استفاده کند. این کار به شما کمک می‌کند یک API خوب طراحی کنید؛ نه تنها نویسنده آن هستید، بلکه یک کاربر نیز هستید! > > In [Chapter 12][ch12], we’ll demonstrate this organizational -> practice with a command line program that will contain both a binary crate +> practice with a command-line program that will contain both a binary crate > and a library crate. ### Starting Relative Paths with `super` We can construct relative paths that begin in the parent module, rather than the current module or the crate root, by using `super` at the start of the -path. This is like starting a filesystem path with the `..` syntax that means -to go to the parent directory. Using `super` allows us to reference an item -that we know is in the parent module, which can make rearranging the module -tree easier when the module is closely related to the parent but the parent -might be moved elsewhere in the module tree someday. +path. This is like starting a filesystem path with the `..` syntax. Using +`super` allows us to reference an item that we know is in the parent module, +which can make rearranging the module tree easier when the module is closely +related to the parent but the parent might be moved elsewhere in the module +tree someday. Consider the code in Listing 7-8 that models the situation in which a chef fixes an incorrect order and personally brings it out to the customer. The @@ -214,7 +144,7 @@ function `fix_incorrect_order` defined in the `back_of_house` module calls the function `deliver_order` defined in the parent module by specifying the path to `deliver_order`, starting with `super`. -+ ```rust,noplayground,test_harness {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-08/src/lib.rs}} @@ -222,29 +152,13 @@ function `deliver_order` defined in the parent module by specifying the path to -The `fix_incorrect_order` function is in the `back_of_house` module, so we can -use `super` to go to the parent module of `back_of_house`, which in this case -is `crate`, the root. From there, we look for `deliver_order` and find it. -Success! We think the `back_of_house` module and the `deliver_order` function -are likely to stay in the same relationship to each other and get moved -together should we decide to reorganize the crate’s module tree. Therefore, we -used `super` so we’ll have fewer places to update code in the future if this -code gets moved to a different module. - -### Making Structs and Enums Public - -We can also use `pub` to designate structs and enums as public, but there are a -few extra details to the usage of `pub` with structs and enums. If we use `pub` -before a struct definition, we make the struct public, but the struct’s fields -will still be private. We can make each field public or not on a case-by-case -basis. In Listing 7-9, we’ve defined a public `back_of_house::Breakfast` struct -with a public `toast` field but a private `seasonal_fruit` field. This models -the case in a restaurant where the customer can pick the type of bread that -comes with a meal, but the chef decides which fruit accompanies the meal based -on what’s in season and in stock. The available fruit changes quickly, so -customers can’t choose the fruit or even see which fruit they’ll get. - -+تابع `fix_incorrect_order` در ماژول `back_of_house` است، بنابراین می‌توانیم از `super` برای رفتن به ماژول والد `back_of_house` استفاده کنیم، که در این مورد `crate`، یعنی ریشه است. از آنجا به دنبال `deliver_order` می‌گردیم و آن را پیدا می‌کنیم. موفقیت! ما فکر می‌کنیم که ماژول `back_of_house` و تابع `deliver_order` احتمالاً در همان رابطه با یکدیگر باقی می‌مانند و اگر بخواهیم درخت ماژول جعبه (crate) را سازماندهی مجدد کنیم، با هم جابجا می‌شوند. بنابراین، از `super` استفاده کردیم تا در آینده، اگر این کد به ماژول دیگری منتقل شد، تغییرات کمتری در کد لازم باشد. + +### عمومی کردن ساختارها و enumها + +ما همچنین می‌توانیم از `pub` برای مشخص کردن ساختارها و enumها به عنوان عمومی استفاده کنیم، اما چند جزئیات اضافی در مورد استفاده از `pub` با ساختارها و enumها وجود دارد. اگر از `pub` قبل از تعریف یک ساختار استفاده کنیم، ساختار عمومی می‌شود، اما فیلدهای ساختار همچنان خصوصی خواهند بود. ما می‌توانیم هر فیلد را به صورت موردی عمومی یا خصوصی کنیم. در لیستینگ 7-9، یک ساختار عمومی به نام `back_of_house::Breakfast` تعریف کرده‌ایم که یک فیلد عمومی به نام `toast` دارد اما فیلد `seasonal_fruit` خصوصی است. این مدل‌سازی حالتی است که در آن مشتری می‌تواند نوع نان همراه با وعده غذایی را انتخاب کند، اما سرآشپز تصمیم می‌گیرد که کدام میوه همراه وعده غذایی باشد بر اساس آنچه در فصل و موجودی است. میوه‌های موجود به سرعت تغییر می‌کنند، بنابراین مشتریان نمی‌توانند میوه را انتخاب کنند یا حتی ببینند که چه میوه‌ای دریافت خواهند کرد. + + ```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-09/src/lib.rs}} @@ -252,23 +166,14 @@ customers can’t choose the fruit or even see which fruit they’ll get. -Because the `toast` field in the `back_of_house::Breakfast` struct is public, -in `eat_at_restaurant` we can write and read to the `toast` field using dot -notation. Notice that we can’t use the `seasonal_fruit` field in -`eat_at_restaurant`, because `seasonal_fruit` is private. Try uncommenting the -line modifying the `seasonal_fruit` field value to see what error you get! +از آنجا که فیلد `toast` در ساختار `back_of_house::Breakfast` عمومی است، می‌توانیم در `eat_at_restaurant` به این فیلد با استفاده از نقطه‌گذاری مقدار بدهیم یا مقدار آن را بخوانیم. توجه کنید که نمی‌توانیم از فیلد `seasonal_fruit` در `eat_at_restaurant` استفاده کنیم، زیرا `seasonal_fruit` خصوصی است. خطی که مقدار فیلد `seasonal_fruit` را تغییر می‌دهد را لغو کامنت کنید تا ببینید چه خطایی دریافت می‌کنید! + +همچنین توجه کنید که چون `back_of_house::Breakfast` یک فیلد خصوصی دارد، ساختار باید یک تابع وابسته عمومی ارائه دهد که یک نمونه از `Breakfast` بسازد (ما آن را اینجا `summer` نامیده‌ایم). اگر `Breakfast` چنین تابعی نداشت، نمی‌توانستیم یک نمونه از `Breakfast` را در `eat_at_restaurant` ایجاد کنیم، زیرا نمی‌توانستیم مقدار فیلد خصوصی `seasonal_fruit` را در `eat_at_restaurant` تنظیم کنیم. -Also, note that because `back_of_house::Breakfast` has a private field, the -struct needs to provide a public associated function that constructs an -instance of `Breakfast` (we’ve named it `summer` here). If `Breakfast` didn’t -have such a function, we couldn’t create an instance of `Breakfast` in -`eat_at_restaurant` because we couldn’t set the value of the private -`seasonal_fruit` field in `eat_at_restaurant`. +در مقابل، اگر یک enum را عمومی کنیم، تمام متغیرهای آن نیز عمومی می‌شوند. ما فقط به `pub` قبل از کلمه کلیدی `enum` نیاز داریم، همان‌طور که در لیستینگ 7-10 نشان داده شده است. -In contrast, if we make an enum public, all of its variants are then public. We -only need the `pub` before the `enum` keyword, as shown in Listing 7-10. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-10/src/lib.rs}} @@ -276,18 +181,11 @@ only need the `pub` before the `enum` keyword, as shown in Listing 7-10. -Because we made the `Appetizer` enum public, we can use the `Soup` and `Salad` -variants in `eat_at_restaurant`. +از آنجایی که enum `Appetizer` را عمومی کردیم، می‌توانیم از متغیرهای `Soup` و `Salad` در `eat_at_restaurant` استفاده کنیم. -Enums aren’t very useful unless their variants are public; it would be annoying -to have to annotate all enum variants with `pub` in every case, so the default -for enum variants is to be public. Structs are often useful without their -fields being public, so struct fields follow the general rule of everything -being private by default unless annotated with `pub`. +Enums خیلی مفید نیستند مگر اینکه متغیرهای آن‌ها عمومی باشند؛ اضافه کردن `pub` به تمام متغیرهای enum در هر مورد کار خسته‌کننده‌ای خواهد بود، بنابراین به طور پیش‌فرض متغیرهای enum عمومی هستند. ساختارها اغلب بدون عمومی بودن فیلدهایشان مفید هستند، بنابراین فیلدهای ساختار از قانون کلی پیروی می‌کنند که همه چیز به صورت پیش‌فرض خصوصی است مگر اینکه با `pub` مشخص شود. -There’s one more situation involving `pub` that we haven’t covered, and that is -our last module system feature: the `use` keyword. We’ll cover `use` by itself -first, and then we’ll show how to combine `pub` and `use`. +یک وضعیت دیگر مرتبط با `pub` وجود دارد که هنوز آن را پوشش نداده‌ایم، و آن آخرین ویژگی سیستم ماژول ما است: کلمه کلیدی `use`. ابتدا `use` را به تنهایی بررسی خواهیم کرد، و سپس نشان خواهیم داد چگونه `pub` و `use` را ترکیب کنیم. [pub]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#exposing-paths-with-the-pub-keyword [api-guidelines]: https://rust-lang.github.io/api-guidelines/ diff --git a/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md b/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md index 52a85415b3..3c12f8587a 100644 --- a/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md +++ b/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md @@ -1,18 +1,10 @@ -## Bringing Paths into Scope with the `use` Keyword +## وارد کردن مسیرها به محدوده با کلمه کلیدی `use` -Having to write out the paths to call functions can feel inconvenient and -repetitive. In Listing 7-7, whether we chose the absolute or relative path to -the `add_to_waitlist` function, every time we wanted to call `add_to_waitlist` -we had to specify `front_of_house` and `hosting` too. Fortunately, there’s a -way to simplify this process: we can create a shortcut to a path with the `use` -keyword once, and then use the shorter name everywhere else in the scope. +نوشتن مسیرهای کامل برای فراخوانی توابع می‌تواند خسته‌کننده و تکراری باشد. در لیستینگ 7-7، چه مسیر مطلق یا نسبی را برای تابع `add_to_waitlist` انتخاب کنیم، هر بار که بخواهیم این تابع را فراخوانی کنیم باید `front_of_house` و `hosting` را نیز مشخص کنیم. خوشبختانه، راهی برای ساده‌تر کردن این فرآیند وجود دارد: می‌توانیم یک میانبر به یک مسیر با استفاده از کلمه کلیدی `use` ایجاد کنیم و سپس در هر جای دیگر محدوده، از نام کوتاه‌تر استفاده کنیم. -In Listing 7-11, we bring the `crate::front_of_house::hosting` module into the -scope of the `eat_at_restaurant` function so we only have to specify -`hosting::add_to_waitlist` to call the `add_to_waitlist` function in -`eat_at_restaurant`. +در لیستینگ 7-11، ماژول `crate::front_of_house::hosting` را به محدوده تابع `eat_at_restaurant` می‌آوریم تا فقط نیاز به مشخص کردن `hosting::add_to_waitlist` برای فراخوانی تابع `add_to_waitlist` در `eat_at_restaurant` داشته باشیم. -+ ```rust,noplayground,test_harness {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-11/src/lib.rs}} @@ -20,18 +12,11 @@ scope of the `eat_at_restaurant` function so we only have to specify -Adding `use` and a path in a scope is similar to creating a symbolic link in -the filesystem. By adding `use crate::front_of_house::hosting` in the crate -root, `hosting` is now a valid name in that scope, just as though the `hosting` -module had been defined in the crate root. Paths brought into scope with `use` -also check privacy, like any other paths. +اضافه کردن `use` و یک مسیر در یک محدوده مشابه ایجاد یک لینک نمادین در فایل‌سیستم است. با اضافه کردن `use crate::front_of_house::hosting` در ریشه جعبه (crate)، `hosting` اکنون یک نام معتبر در آن محدوده است، درست مانند اینکه ماژول `hosting` در ریشه جعبه (crate) تعریف شده باشد. مسیرهایی که با `use` به محدوده آورده می‌شوند مانند هر مسیر دیگری حریم خصوصی را بررسی می‌کنند. -Note that `use` only creates the shortcut for the particular scope in which the -`use` occurs. Listing 7-12 moves the `eat_at_restaurant` function into a new -child module named `customer`, which is then a different scope than the `use` -statement, so the function body won’t compile. +توجه کنید که `use` فقط میانبر را برای محدوده خاصی که در آن `use` استفاده شده ایجاد می‌کند. لیستینگ 7-12 تابع `eat_at_restaurant` را به یک زیرماژول جدید به نام `customer` منتقل می‌کند که سپس یک محدوده متفاوت از دستور `use` است، بنابراین بدنه تابع کامپایل نمی‌شود. -+ ```rust,noplayground,test_harness,does_not_compile,ignore {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-12/src/lib.rs}} @@ -39,26 +24,19 @@ statement, so the function body won’t compile. -The compiler error shows that the shortcut no longer applies within the -`customer` module: +خطای کامپایلر نشان می‌دهد که میانبر دیگر در ماژول `customer` اعمال نمی‌شود: ```console {{#include ../listings/ch07-managing-growing-projects/listing-07-12/output.txt}} ``` -Notice there’s also a warning that the `use` is no longer used in its scope! To -fix this problem, move the `use` within the `customer` module too, or reference -the shortcut in the parent module with `super::hosting` within the child -`customer` module. +توجه کنید که همچنین یک هشدار وجود دارد که `use` دیگر در محدوده خود استفاده نمی‌شود! برای رفع این مشکل، دستور `use` را نیز به داخل ماژول `customer` منتقل کنید، یا میانبر را در ماژول والد با `super::hosting` در داخل ماژول `customer` ارجاع دهید. -### Creating Idiomatic `use` Paths +### ایجاد مسیرهای `use` به صورت ایدیوماتیک -In Listing 7-11, you might have wondered why we specified `use -crate::front_of_house::hosting` and then called `hosting::add_to_waitlist` in -`eat_at_restaurant`, rather than specifying the `use` path all the way out to -the `add_to_waitlist` function to achieve the same result, as in Listing 7-13. +در لیستینگ 7-11، ممکن است این سوال پیش بیاید که چرا ما `use crate::front_of_house::hosting` را مشخص کرده‌ایم و سپس `hosting::add_to_waitlist` را در `eat_at_restaurant` فراخوانی کرده‌ایم، به جای اینکه مسیر `use` را تا تابع `add_to_waitlist` مشخص کنیم تا همان نتیجه را به دست آوریم، همان‌طور که در لیستینگ 7-13 نشان داده شده است. -+ ```rust,noplayground,test_harness {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-13/src/lib.rs}} @@ -66,20 +44,11 @@ the `add_to_waitlist` function to achieve the same result, as in Listing 7-13. -Although both Listing 7-11 and Listing 7-13 accomplish the same task, Listing -7-11 is the idiomatic way to bring a function into scope with `use`. Bringing -the function’s parent module into scope with `use` means we have to specify the -parent module when calling the function. Specifying the parent module when -calling the function makes it clear that the function isn’t locally defined -while still minimizing repetition of the full path. The code in Listing 7-13 is -unclear as to where `add_to_waitlist` is defined. +اگرچه هم لیستینگ 7-11 و هم لیستینگ 7-13 کار مشابهی انجام می‌دهند، لیستینگ 7-11 روش ایدیوماتیک برای وارد کردن یک تابع به محدوده با `use` است. وارد کردن ماژول والد تابع با `use` به این معنا است که باید ماژول والد را هنگام فراخوانی تابع مشخص کنیم. مشخص کردن ماژول والد هنگام فراخوانی تابع نشان می‌دهد که تابع به صورت محلی تعریف نشده است، در حالی که همچنان تکرار مسیر کامل را به حداقل می‌رساند. کد موجود در لیستینگ 7-13 مشخص نمی‌کند که `add_to_waitlist` کجا تعریف شده است. -On the other hand, when bringing in structs, enums, and other items with `use`, -it’s idiomatic to specify the full path. Listing 7-14 shows the idiomatic way -to bring the standard library’s `HashMap` struct into the scope of a binary -crate. +از طرف دیگر، وقتی ساختارها، enumها، و سایر آیتم‌ها را با `use` وارد می‌کنیم، ایدیوماتیک است که مسیر کامل را مشخص کنیم. لیستینگ 7-14 روش ایدیوماتیک برای وارد کردن ساختار `HashMap` از کتابخانه استاندارد به محدوده جعبه (crate) باینری را نشان می‌دهد. -+ ```rust {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-14/src/main.rs}} @@ -87,15 +56,11 @@ crate. -There’s no strong reason behind this idiom: it’s just the convention that has -emerged, and folks have gotten used to reading and writing Rust code this way. +هیچ دلیل قوی پشت این عرف نیست: این فقط کنوانسیونی است که در جامعه Rust به وجود آمده و افراد به خواندن و نوشتن کد Rust به این روش عادت کرده‌اند. -The exception to this idiom is if we’re bringing two items with the same name -into scope with `use` statements, because Rust doesn’t allow that. Listing 7-15 -shows how to bring two `Result` types into scope that have the same name but -different parent modules, and how to refer to them. +استثنای این عرف زمانی است که دو آیتم با نام یکسان را با دستورات `use` وارد محدوده می‌کنیم، زیرا Rust این اجازه را نمی‌دهد. لیستینگ 7-15 نشان می‌دهد که چگونه دو نوع `Result` را که نام یکسانی دارند اما از ماژول‌های والد متفاوتی می‌آیند وارد محدوده کنیم و چگونه به آن‌ها ارجاع دهیم. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-15/src/lib.rs:here}} @@ -103,19 +68,13 @@ different parent modules, and how to refer to them. -As you can see, using the parent modules distinguishes the two `Result` types. -If instead we specified `use std::fmt::Result` and `use std::io::Result`, we’d -have two `Result` types in the same scope, and Rust wouldn’t know which one we -meant when we used `Result`. +همان‌طور که می‌بینید، استفاده از ماژول‌های والد دو نوع `Result` را از هم متمایز می‌کند. اگر به جای آن `use std::fmt::Result` و `use std::io::Result` مشخص کنیم، دو نوع `Result` در یک محدوده خواهیم داشت و Rust نمی‌تواند بفهمد منظور ما از `Result` کدام است. -### Providing New Names with the `as` Keyword +### ارائه نام‌های جدید با کلمه کلیدی `as` -There’s another solution to the problem of bringing two types of the same name -into the same scope with `use`: after the path, we can specify `as` and a new -local name, or _alias_, for the type. Listing 7-16 shows another way to write -the code in Listing 7-15 by renaming one of the two `Result` types using `as`. +یک راه‌حل دیگر برای مشکل وارد کردن دو نوع با نام یکسان به یک محدوده با `use` این است که پس از مسیر، با استفاده از `as` یک نام محلی جدید یا _نام مستعار_ برای نوع مشخص کنیم. لیستینگ 7-16 راه دیگری برای نوشتن کد در لیستینگ 7-15 را نشان می‌دهد که در آن یکی از دو نوع `Result` را با استفاده از `as` تغییر نام داده‌ایم. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-16/src/lib.rs:here}} @@ -123,24 +82,20 @@ the code in Listing 7-15 by renaming one of the two `Result` types using `as`. -In the second `use` statement, we chose the new name `IoResult` for the -`std::io::Result` type, which won’t conflict with the `Result` from `std::fmt` -that we’ve also brought into scope. Listing 7-15 and Listing 7-16 are -considered idiomatic, so the choice is up to you! +در دستور دوم `use`، ما نام جدید `IoResult` را برای نوع `std::io::Result` انتخاب کردیم، که با نوع `Result` از `std::fmt` که آن را نیز وارد محدوده کرده‌ایم، تضاد نخواهد داشت. هر دو لیستینگ 7-15 و 7-16 ایدیوماتیک در نظر گرفته می‌شوند، بنابراین انتخاب با شماست! -### Re-exporting Names with `pub use` +### دوباره صادر کردن نام‌ها با `pub use` -When we bring a name into scope with the `use` keyword, the name is private to -the scope into which we imported it. To enable code outside that scope to refer -to that name as if it had been defined in that scope, we can combine `pub` and -`use`. This technique is called _re-exporting_ because we’re bringing an item -into scope but also making that item available for others to bring into their -scope. +وقتی با استفاده از کلیدواژه‌ی `use` یک نام را وارد حوزه‌ای می‌کنیم، +آن نام تنها در همان حوزه خصوصی است که در آن وارد شده است. +برای این‌که کد خارج از آن حوزه نیز بتواند به آن نام دسترسی داشته باشد، +انگار که در همان حوزه تعریف شده است، می‌توانیم `pub` و `use` را با هم ترکیب کنیم. +این تکنیک _re-exporting_ نامیده می‌شود، زیرا در حالی که یک آیتم را وارد حوزه می‌کنیم، +همزمان آن را برای دیگران نیز قابل دسترس می‌کنیم تا بتوانند آن را وارد حوزه‌ی خود کنند. -Listing 7-17 shows the code in Listing 7-11 with `use` in the root module -changed to `pub use`. +لیستینگ 7-17 کد موجود در لیستینگ 7-11 را با تغییر دستور `use` در ماژول ریشه به `pub use` نشان می‌دهد. -+ ```rust,noplayground,test_harness {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-17/src/lib.rs}} @@ -148,35 +103,19 @@ changed to `pub use`. -Before this change, external code would have to call the `add_to_waitlist` -function by using the path -`restaurant::front_of_house::hosting::add_to_waitlist()`, which also would have -required the `front_of_house` module to be marked as `pub`. Now that this `pub -use` has re-exported the `hosting` module from the root module, external code -can use the path `restaurant::hosting::add_to_waitlist()` instead. - -Re-exporting is useful when the internal structure of your code is different -from how programmers calling your code would think about the domain. For -example, in this restaurant metaphor, the people running the restaurant think -about “front of house” and “back of house.” But customers visiting a restaurant -probably won’t think about the parts of the restaurant in those terms. With `pub -use`, we can write our code with one structure but expose a different structure. -Doing so makes our library well organized for programmers working on the library -and programmers calling the library. We’ll look at another example of `pub use` -and how it affects your crate’s documentation in [“Exporting a Convenient Public -API with `pub use`”][ch14-pub-use] in Chapter 14. - -### Using External Packages - -In Chapter 2, we programmed a guessing game project that used an external -package called `rand` to get random numbers. To use `rand` in our project, we -added this line to _Cargo.toml_: - - +قبل از این تغییر، کد خارجی باید تابع `add_to_waitlist` را با استفاده از مسیر `restaurant::front_of_house::hosting::add_to_waitlist()` فراخوانی می‌کرد، که همچنین نیاز داشت ماژول `front_of_house` به عنوان `pub` علامت‌گذاری شود. حالا که این `pub use` ماژول `hosting` را از ماژول ریشه دوباره صادر کرده است، کد خارجی می‌تواند از مسیر `restaurant::hosting::add_to_waitlist()` استفاده کند. + +Re-exporting زمانی مفید است که ساختار داخلی کد شما با نحوه‌ی تفکر برنامه‌نویسانی که از کد شما استفاده می‌کنند درباره‌ی دامنه، متفاوت باشد. +برای مثال، در این تمثیل رستوران، کسانی که رستوران را اداره می‌کنند درباره‌ی «بخش جلویی» (front of house) و «بخش پشتی» (back of house) فکر می‌کنند. +اما مشتریانی که به رستوران می‌آیند احتمالاً درباره‌ی قسمت‌های رستوران با چنین اصطلاحاتی فکر نمی‌کنند. +با استفاده از `pub use` می‌توانیم کد خود را با یک ساختار بنویسیم ولی ساختاری متفاوت را در معرض استفاده قرار دهیم. +این کار باعث می‌شود کتابخانه‌ی ما هم برای برنامه‌نویسانی که روی کتابخانه کار می‌کنند و هم برای برنامه‌نویسانی که از آن استفاده می‌کنند، به‌خوبی سازمان‌دهی شده باشد. +در فصل ۱۴، در بخش [“صادرات یک API عمومی راحت با استفاده از `pub use`”][ch14-pub-use]، +مثال دیگری از `pub use` و تأثیر آن بر مستندات crate شما را بررسی خواهیم کرد. + +### استفاده از بسته‌های خارجی + +در فصل ۲، ما یک پروژه بازی حدس‌زنی برنامه‌ریزی کردیم که از یک بسته خارجی به نام `rand` برای تولید اعداد تصادفی استفاده می‌کرد. برای استفاده از `rand` در پروژه خود، این خط را به _Cargo.toml_ اضافه کردیم: @@ -186,44 +125,31 @@ added this line to _Cargo.toml_: -Adding `rand` as a dependency in _Cargo.toml_ tells Cargo to download the -`rand` package and any dependencies from [crates.io](https://crates.io/) and -make `rand` available to our project. +اضافه کردن `rand` به عنوان یک وابستگی در _Cargo.toml_ به Cargo می‌گوید که بسته `rand` و هرگونه وابستگی را از [crates.io](https://crates.io/) دانلود کرده و `rand` را در پروژه ما در دسترس قرار دهد. -Then, to bring `rand` definitions into the scope of our package, we added a -`use` line starting with the name of the crate, `rand`, and listed the items we -wanted to bring into scope. Recall that in [“Generating a Random -Number”][rand] in Chapter 2, we brought the `Rng` trait into -scope and called the `rand::thread_rng` function: +سپس، برای وارد کردن تعاریف crate `rand` به حوزه‌ی پکیج خود، +یک خط `use` اضافه کردیم که با نام crate، یعنی `rand`، آغاز شد +و آیتم‌هایی را که می‌خواستیم وارد حوزه کنیم، فهرست کردیم. +به یاد داشته باشید که در بخش [“تولید یک عدد تصادفی”][rand] در فصل ۲، +`trait` مربوط به `Rng` را وارد حوزه کردیم و تابع `rand::thread_rng` را فراخوانی نمودیم: ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-03/src/main.rs:ch07-04}} ``` -Members of the Rust community have made many packages available at -[crates.io](https://crates.io/), and pulling any of them into your package -involves these same steps: listing them in your package’s _Cargo.toml_ file and -using `use` to bring items from their crates into scope. +اعضای جامعه Rust بسیاری از بسته‌ها را در [crates.io](https://crates.io/) به اشتراک گذاشته‌اند، و وارد کردن هر یک از آن‌ها به بسته شما شامل این مراحل است: فهرست کردن آن‌ها در فایل _Cargo.toml_ بسته شما و استفاده از `use` برای وارد کردن آیتم‌ها از جعبه (crate) آن‌ها به محدوده. -Note that the standard `std` library is also a crate that’s external to our -package. Because the standard library is shipped with the Rust language, we -don’t need to change _Cargo.toml_ to include `std`. But we do need to refer to -it with `use` to bring items from there into our package’s scope. For example, -with `HashMap` we would use this line: +توجه داشته باشید که کتابخانه استاندارد `std` نیز یک جعبه (crate) خارجی برای بسته ما است. از آنجا که کتابخانه استاندارد همراه با زبان Rust ارائه می‌شود، نیازی به تغییر _Cargo.toml_ برای گنجاندن `std` نداریم. اما برای وارد کردن آیتم‌ها از آن به محدوده بسته خود، باید به آن با `use` ارجاع دهیم. برای مثال، با `HashMap` از این خط استفاده می‌کردیم: ```rust use std::collections::HashMap; ``` -This is an absolute path starting with `std`, the name of the standard library -crate. +این یک مسیر مطلق است که با `std`، نام جعبه (crate) کتابخانه استاندارد، شروع می‌شود. -### Using Nested Paths to Clean Up Large `use` Lists +### استفاده از مسیرهای تو در تو برای ساده‌سازی لیست‌های بزرگ `use` -If we’re using multiple items defined in the same crate or same module, listing -each item on its own line can take up a lot of vertical space in our files. For -example, these two `use` statements we had in the guessing game in Listing 2-4 -bring items from `std` into scope: +اگر از چندین آیتم تعریف‌شده در یک جعبه (crate) یا ماژول استفاده کنیم، فهرست کردن هر آیتم در خط خود می‌تواند فضای عمودی زیادی در فایل‌های ما اشغال کند. برای مثال، این دو دستور `use` که در بازی حدس‌زنی در لیستینگ ۲-۴ استفاده کردیم آیتم‌هایی از `std` را به محدوده می‌آورند: @@ -233,12 +159,9 @@ bring items from `std` into scope: -Instead, we can use nested paths to bring the same items into scope in one -line. We do this by specifying the common part of the path, followed by two -colons, and then curly brackets around a list of the parts of the paths that -differ, as shown in Listing 7-18. +در عوض، می‌توانیم از مسیرهای تو در تو استفاده کنیم تا همان آیتم‌ها را در یک خط به محدوده بیاوریم. این کار را با مشخص کردن بخش مشترک مسیر، به دنبال آن دو نقطه دوبل و سپس یک لیست از بخش‌های متفاوت مسیرها در داخل آکولاد انجام می‌دهیم، همان‌طور که در لیستینگ 7-18 نشان داده شده است. -+ ```rust,ignore {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-18/src/main.rs:here}} @@ -246,16 +169,11 @@ differ, as shown in Listing 7-18. -In bigger programs, bringing many items into scope from the same crate or -module using nested paths can reduce the number of separate `use` statements -needed by a lot! +در برنامه‌های بزرگ‌تر، وارد کردن بسیاری از آیتم‌ها از یک جعبه (crate) یا ماژول مشابه با استفاده از مسیرهای تو در تو می‌تواند تعداد دستورات `use` جداگانه مورد نیاز را به طور قابل‌توجهی کاهش دهد. -We can use a nested path at any level in a path, which is useful when combining -two `use` statements that share a subpath. For example, Listing 7-19 shows two -`use` statements: one that brings `std::io` into scope and one that brings -`std::io::Write` into scope. +ما می‌توانیم در هر سطحی از یک مسیر، از یک مسیر تو در تو استفاده کنیم، که این کار در مواقعی که دو دستور `use` دارای یک زیرمسیر مشترک هستند، مفید است. برای مثال، لیستینگ 7-19 دو دستور `use` را نشان می‌دهد: یکی که `std::io` را به محدوده وارد می‌کند و دیگری که `std::io::Write` را به محدوده وارد می‌کند. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-19/src/lib.rs}} @@ -263,11 +181,9 @@ two `use` statements that share a subpath. For example, Listing 7-19 shows two -The common part of these two paths is `std::io`, and that’s the complete first -path. To merge these two paths into one `use` statement, we can use `self` in -the nested path, as shown in Listing 7-20. +بخش مشترک این دو مسیر، `std::io` است که مسیر کامل اولین دستور `use` را تشکیل می‌دهد. برای ترکیب این دو مسیر به یک دستور `use`، می‌توانیم از `self` در مسیر تو در تو استفاده کنیم، همان‌طور که در لیستینگ 7-20 نشان داده شده است. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-20/src/lib.rs}} @@ -275,32 +191,30 @@ the nested path, as shown in Listing 7-20. -This line brings `std::io` and `std::io::Write` into scope. +این خط، `std::io` و `std::io::Write` را به محدوده وارد می‌کند. -### The Glob Operator +### عملگر Glob -If we want to bring _all_ public items defined in a path into scope, we can -specify that path followed by the `*` glob operator: +اگر بخواهیم _تمام_ آیتم‌های عمومی تعریف‌شده در یک مسیر را به محدوده وارد کنیم، می‌توانیم آن مسیر را به همراه عملگر `*` مشخص کنیم: ```rust use std::collections::*; ``` -This `use` statement brings all public items defined in `std::collections` into -the current scope. Be careful when using the glob operator! Glob can make it -harder to tell what names are in scope and where a name used in your program -was defined. Additionally, if the dependency changes its definitions, what -you’ve imported changes as well, which may lead to compiler errors when you -upgrade the dependency if the dependency adds a definition with the same name -as a definition of yours in the same scope, for example. - -The glob operator is often used when testing to bring everything under test into -the `tests` module; we’ll talk about that in [“How to Write -Tests”][writing-tests] in Chapter 11. The glob operator is also -sometimes used as part of the prelude pattern: see [the standard library -documentation](../std/prelude/index.html#other-preludes) for more -information on that pattern. +این دستور `use` تمام آیتم‌های عمومی تعریف‌شده در `std::collections` را وارد حوزه‌ی فعلی می‌کند. +در استفاده از عملگر glob دقت کنید! +استفاده از glob می‌تواند باعث شود تشخیص این‌که چه نام‌هایی در حوزه هستند +و یک نام استفاده‌شده در برنامه از کجا آمده، دشوارتر شود. +علاوه بر این، اگر وابستگی تغییراتی در تعاریف خود ایجاد کند، آن‌چه شما وارد کرده‌اید نیز تغییر می‌کند، +که ممکن است هنگام به‌روزرسانی وابستگی، باعث بروز خطای کامپایلر شود— +برای مثال، اگر وابستگی تعریفی با همان نامی اضافه کند که شما نیز در همان حوزه تعریف کرده‌اید. + +عملگر glob اغلب هنگام تست برای وارد کردن تمام آیتم‌های تحت تست به ماژول `tests` استفاده می‌شود؛ +در فصل ۱۱ در بخش [“چگونه تست بنویسیم”][writing-tests] درباره‌ی آن صحبت خواهیم کرد. +همچنین، عملگر glob گاهی در قالب الگوی prelude نیز به‌کار می‌رود؛ +برای اطلاعات بیشتر درباره‌ی این الگو، به [مستندات کتابخانه‌ی استاندارد](https://doc.rust-lang.org/std/prelude/index.html#other-preludes) مراجعه کنید. [ch14-pub-use]: ch14-02-publishing-to-crates-io.html#exporting-a-convenient-public-api-with-pub-use [rand]: ch02-00-guessing-game-tutorial.html#generating-a-random-number [writing-tests]: ch11-01-writing-tests.html#how-to-write-tests + diff --git a/src/ch07-05-separating-modules-into-different-files.md b/src/ch07-05-separating-modules-into-different-files.md index 4de9b6908f..f20e72ccb8 100644 --- a/src/ch07-05-separating-modules-into-different-files.md +++ b/src/ch07-05-separating-modules-into-different-files.md @@ -1,22 +1,12 @@ -## Separating Modules into Different Files +## جدا کردن ماژول‌ها به فایل‌های مختلف -So far, all the examples in this chapter defined multiple modules in one file. -When modules get large, you might want to move their definitions to a separate -file to make the code easier to navigate. +تا به اینجا، تمام مثال‌های این فصل چندین ماژول را در یک فایل تعریف کرده‌اند. هنگامی که ماژول‌ها بزرگ می‌شوند، ممکن است بخواهید تعریف‌های آن‌ها را به یک فایل جداگانه منتقل کنید تا کد آسان‌تر خوانده و مدیریت شود. -For example, let’s start from the code in Listing 7-17 that had multiple -restaurant modules. We’ll extract modules into files instead of having all the -modules defined in the crate root file. In this case, the crate root file is -_src/lib.rs_, but this procedure also works with binary crates whose crate root -file is _src/main.rs_. +برای مثال، بیایید از کد موجود در لیستینگ 7-17 شروع کنیم که شامل چندین ماژول مرتبط با رستوران بود. ما این ماژول‌ها را به جای تعریف در فایل ریشه جعبه (crate)، به فایل‌های جداگانه منتقل می‌کنیم. در این مثال، فایل ریشه جعبه (crate) _src/lib.rs_ است، اما این روش برای جعبه‌ها (crates)ی باینری که فایل ریشه آن‌ها _src/main.rs_ است نیز کار می‌کند. -First we’ll extract the `front_of_house` module to its own file. Remove the -code inside the curly brackets for the `front_of_house` module, leaving only -the `mod front_of_house;` declaration, so that _src/lib.rs_ contains the code -shown in Listing 7-21. Note that this won’t compile until we create the -_src/front_of_house.rs_ file in Listing 7-22. +ابتدا ماژول `front_of_house` را به فایل خودش منتقل می‌کنیم. کدی که داخل آکولادهای ماژول `front_of_house` است را حذف کرده و فقط اعلان `mod front_of_house;` را باقی می‌گذاریم. نتیجه کد در _src/lib.rs_ مانند لیستینگ 7-21 خواهد بود. توجه داشته باشید که این کد تا زمانی که فایل _src/front_of_house.rs_ مطابق لیستینگ 7-22 ایجاد نشود کامپایل نخواهد شد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-21-and-22/src/lib.rs}} @@ -24,12 +14,9 @@ _src/front_of_house.rs_ file in Listing 7-22. -Next, place the code that was in the curly brackets into a new file named -_src/front_of_house.rs_, as shown in Listing 7-22. The compiler knows to look -in this file because it came across the module declaration in the crate root -with the name `front_of_house`. +سپس، کدی که داخل آکولادهای ماژول `front_of_house` بود را به یک فایل جدید به نام _src/front_of_house.rs_ منتقل می‌کنیم، همان‌طور که در لیستینگ 7-22 نشان داده شده است. کامپایلر می‌داند که باید این فایل را بررسی کند زیرا در فایل ریشه جعبه (crate) با نام `front_of_house` اعلان ماژول را دیده است. -+ ```rust,ignore {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-21-and-22/src/front_of_house.rs}} @@ -37,22 +24,11 @@ with the name `front_of_house`. -Note that you only need to load a file using a `mod` declaration _once_ in your -module tree. Once the compiler knows the file is part of the project (and knows -where in the module tree the code resides because of where you’ve put the `mod` -statement), other files in your project should refer to the loaded file’s code -using a path to where it was declared, as covered in the [“Paths for Referring -to an Item in the Module Tree”][paths] section. In other words, -`mod` is _not_ an “include” operation that you may have seen in other -programming languages. +توجه داشته باشید که شما فقط یک بار نیاز دارید تا یک فایل را با استفاده از دستور `mod` در درخت ماژول خود بارگذاری کنید. وقتی کامپایلر می‌فهمد که فایل بخشی از پروژه است (و می‌فهمد که کد در کجای درخت ماژول قرار دارد به خاطر جایی که دستور `mod` را قرار داده‌اید)، سایر فایل‌های پروژه شما باید با استفاده از مسیری که به محل اعلان فایل اشاره می‌کند به کد بارگذاری شده ارجاع دهند، همان‌طور که در بخش [«مسیرها برای اشاره به یک آیتم در درخت ماژول»][paths] توضیح داده شد. به عبارت دیگر، `mod` یک عملیات "شامل کردن" (include) نیست که ممکن است در زبان‌های برنامه‌نویسی دیگر دیده باشید. -Next, we’ll extract the `hosting` module to its own file. The process is a bit -different because `hosting` is a child module of `front_of_house`, not of the -root module. We’ll place the file for `hosting` in a new directory that will be -named for its ancestors in the module tree, in this case _src/front_of_house_. +در مرحله بعد، ماژول `hosting` را به فایل خودش منتقل می‌کنیم. این فرآیند کمی متفاوت است زیرا `hosting` یک زیرماژول از `front_of_house` است، نه از ماژول ریشه. فایل مربوط به `hosting` را در یک دایرکتوری جدید قرار می‌دهیم که به نام والدین آن در درخت ماژول نام‌گذاری شده است، که در اینجا _src/front_of_house_ است. -To start moving `hosting`, we change _src/front_of_house.rs_ to contain only -the declaration of the `hosting` module: +برای شروع انتقال `hosting`، فایل _src/front_of_house.rs_ را تغییر می‌دهیم تا فقط شامل اعلان ماژول `hosting` باشد: @@ -62,8 +38,7 @@ the declaration of the `hosting` module: -Then we create a _src/front_of_house_ directory and a _hosting.rs_ file to -contain the definitions made in the `hosting` module: +سپس یک دایرکتوری به نام _src/front_of_house_ و یک فایل _hosting.rs_ ایجاد می‌کنیم تا تعریف‌هایی که در ماژول `hosting` انجام شده‌اند را در آن قرار دهیم: @@ -73,57 +48,32 @@ contain the definitions made in the `hosting` module: -If we instead put _hosting.rs_ in the _src_ directory, the compiler would -expect the _hosting.rs_ code to be in a `hosting` module declared in the crate -root, and not declared as a child of the `front_of_house` module. The -compiler’s rules for which files to check for which modules’ code mean the -directories and files more closely match the module tree. +اگر به جای آن فایل _hosting.rs_ را در دایرکتوری _src_ قرار دهیم، کامپایلر انتظار خواهد داشت که کد _hosting.rs_ در یک ماژول `hosting` که در ریشه جعبه (crate) اعلان شده باشد قرار داشته باشد، نه به عنوان یک زیرماژول از ماژول `front_of_house`. قوانین کامپایلر برای مشخص کردن این که کدام فایل‌ها برای کدام ماژول‌ها بررسی شوند، به این معناست که دایرکتوری‌ها و فایل‌ها با درخت ماژول مطابقت بیشتری دارند. -> ### Alternate File Paths +> ### مسیرهای فایل جایگزین > -> So far we’ve covered the most idiomatic file paths the Rust compiler uses, -> but Rust also supports an older style of file path. For a module named -> `front_of_house` declared in the crate root, the compiler will look for the -> module’s code in: +> تاکنون مسیرهای فایل ایدیوماتیک را که کامپایلر Rust استفاده می‌کند پوشش داده‌ایم، اما Rust از یک سبک قدیمی‌تر از مسیر فایل نیز پشتیبانی می‌کند. برای یک ماژول به نام `front_of_house` که در ریشه جعبه (crate) اعلان شده است، کامپایلر کد ماژول را در مکان‌های زیر جستجو می‌کند: > -> - _src/front_of_house.rs_ (what we covered) -> - _src/front_of_house/mod.rs_ (older style, still supported path) +> - _src/front_of_house.rs_ (روشی که پوشش داده شد) +> - _src/front_of_house/mod.rs_ (مسیر قدیمی‌تر، همچنان پشتیبانی‌شده) > -> For a module named `hosting` that is a submodule of `front_of_house`, the -> compiler will look for the module’s code in: +> برای یک ماژول به نام `hosting` که زیرماژولی از `front_of_house` است، کامپایلر کد ماژول را در مکان‌های زیر جستجو می‌کند: > -> - _src/front_of_house/hosting.rs_ (what we covered) -> - _src/front_of_house/hosting/mod.rs_ (older style, still supported path) +> - _src/front_of_house/hosting.rs_ (روشی که پوشش داده شد) +> - _src/front_of_house/hosting/mod.rs_ (مسیر قدیمی‌تر، همچنان پشتیبانی‌شده) > -> If you use both styles for the same module, you’ll get a compiler error. -> Using a mix of both styles for different modules in the same project is -> allowed, but might be confusing for people navigating your project. +> اگر هر دو سبک را برای یک ماژول استفاده کنید، یک خطای کامپایلر دریافت خواهید کرد. استفاده از ترکیبی از هر دو سبک برای ماژول‌های مختلف در یک پروژه مجاز است، اما ممکن است برای کسانی که پروژه شما را مرور می‌کنند گیج‌کننده باشد. > -> The main downside to the style that uses files named _mod.rs_ is that your -> project can end up with many files named _mod.rs_, which can get confusing -> when you have them open in your editor at the same time. - -We’ve moved each module’s code to a separate file, and the module tree remains -the same. The function calls in `eat_at_restaurant` will work without any -modification, even though the definitions live in different files. This -technique lets you move modules to new files as they grow in size. - -Note that the `pub use crate::front_of_house::hosting` statement in -_src/lib.rs_ also hasn’t changed, nor does `use` have any impact on what files -are compiled as part of the crate. The `mod` keyword declares modules, and Rust -looks in a file with the same name as the module for the code that goes into -that module. - -## Summary - -Rust lets you split a package into multiple crates and a crate into modules so -you can refer to items defined in one module from another module. You can do -this by specifying absolute or relative paths. These paths can be brought into -scope with a `use` statement so you can use a shorter path for multiple uses of -the item in that scope. Module code is private by default, but you can make -definitions public by adding the `pub` keyword. - -In the next chapter, we’ll look at some collection data structures in the -standard library that you can use in your neatly organized code. +> نکته منفی اصلی سبک استفاده از فایل‌هایی با نام _mod.rs_ این است که پروژه شما ممکن است تعداد زیادی فایل با نام _mod.rs_ داشته باشد، که می‌تواند هنگام باز بودن همزمان این فایل‌ها در ویرایشگر شما گیج‌کننده باشد. + +ما کد هر ماژول را به یک فایل جداگانه منتقل کرده‌ایم و درخت ماژول به همان شکل باقی مانده است. فراخوانی توابع در `eat_at_restaurant` بدون هیچ تغییری کار خواهد کرد، حتی اگر تعریف‌ها در فایل‌های مختلف قرار داشته باشند. این تکنیک به شما امکان می‌دهد ماژول‌ها را به فایل‌های جدید منتقل کنید زیرا اندازه آن‌ها افزایش می‌یابد. + +توجه داشته باشید که دستور `pub use crate::front_of_house::hosting` در _src/lib.rs_ نیز تغییری نکرده است، و همچنین `use` هیچ تأثیری بر اینکه چه فایل‌هایی به عنوان بخشی از جعبه (crate) کامپایل شوند ندارد. کلمه کلیدی `mod` ماژول‌ها را اعلان می‌کند و Rust در فایلی با همان نام ماژول به دنبال کدی می‌گردد که وارد آن ماژول شود. + +## خلاصه + +Rust به شما اجازه می‌دهد یک بسته را به چندین جعبه (crate) و یک جعبه (crate) را به ماژول‌ها تقسیم کنید تا بتوانید به آیتم‌هایی که در یک ماژول تعریف شده‌اند از ماژول دیگری ارجاع دهید. می‌توانید این کار را با مشخص کردن مسیرهای مطلق یا نسبی انجام دهید. این مسیرها می‌توانند با یک دستور `use` به محدوده وارد شوند تا بتوانید از یک مسیر کوتاه‌تر برای استفاده‌های متعدد از آن آیتم در آن محدوده استفاده کنید. کد ماژول به صورت پیش‌فرض خصوصی است، اما می‌توانید با افزودن کلمه کلیدی `pub` تعریف‌ها را عمومی کنید. + +در فصل بعدی، به برخی از ساختارهای داده‌ای مجموعه در کتابخانه استاندارد خواهیم پرداخت که می‌توانید در کد مرتب و سازماندهی‌شده خود از آن‌ها استفاده کنید. [paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html diff --git a/src/ch08-00-common-collections.md b/src/ch08-00-common-collections.md index adf600c942..ab0646b0fc 100644 --- a/src/ch08-00-common-collections.md +++ b/src/ch08-00-common-collections.md @@ -1,25 +1,25 @@ -# Common Collections +# مجموعه‌های معمول -Rust’s standard library includes a number of very useful data structures called -_collections_. Most other data types represent one specific value, but -collections can contain multiple values. Unlike the built-in array and tuple -types, the data that these collections point to is stored on the heap, which -means the amount of data does not need to be known at compile time and can grow -or shrink as the program runs. Each kind of collection has different -capabilities and costs, and choosing an appropriate one for your current -situation is a skill you’ll develop over time. In this chapter, we’ll discuss -three collections that are used very often in Rust programs: +کتابخانه‌ی استاندارد Rust شامل تعدادی ساختار داده‌ی بسیار مفید به نام *collections* (مجموعه‌ها) است. +اکثر انواع داده‌ی دیگر نمایانگر یک مقدار خاص هستند، اما مجموعه‌ها می‌توانند چندین مقدار را در خود نگه دارند. +بر خلاف انواع داخلی مانند `array` و `tuple`، داده‌هایی که این مجموعه‌ها به آن‌ها اشاره می‌کنند، +روی heap ذخیره می‌شوند؛ به این معنا که نیازی نیست اندازه‌ی داده‌ها در زمان کامپایل مشخص باشد، +و این اندازه می‌تواند در طول اجرای برنامه رشد یا کاهش یابد. +هر نوع مجموعه قابلیت‌ها و هزینه‌های متفاوتی دارد، +و انتخاب مجموعه‌ی مناسب برای موقعیت فعلی، مهارتی است که با گذشت زمان به دست خواهید آورد. +در این فصل، درباره‌ی سه نوع مجموعه که بسیار در برنامه‌های Rust استفاده می‌شوند صحبت خواهیم کرد: -- A _vector_ allows you to store a variable number of values next to each other. -- A _string_ is a collection of characters. We’ve mentioned the `String` type - previously, but in this chapter we’ll talk about it in depth. -- A _hash map_ allows you to associate a value with a specific key. It’s a - particular implementation of the more general data structure called a _map_. +- یک _بردار_ به شما اجازه می‌دهد که تعداد متغیری از مقادیر را در کنار + یکدیگر ذخیره کنید. +- یک _رشته_ یک مجموعه از کاراکترها است. ما قبلاً نوع `String` را ذکر کرده‌ایم، + اما در این فصل به طور عمیق‌تر درباره آن صحبت خواهیم کرد. +- یک _هش مپ_ به شما اجازه می‌دهد که یک مقدار را با یک کلید مشخص مرتبط کنید. + این یک پیاده‌سازی خاص از ساختار داده کلی‌تر به نام _نقشه_ است. -To learn about the other kinds of collections provided by the standard library, -see [the documentation][collections]. +برای یادگیری درباره انواع دیگر مجموعه‌هایی که توسط کتابخانه استاندارد ارائه شده‌اند، +[مستندات][collections] را مشاهده کنید. -We’ll discuss how to create and update vectors, strings, and hash maps, as well -as what makes each special. +ما درباره نحوه ایجاد و به‌روزرسانی بردارها، رشته‌ها و هش مپ‌ها، همچنین ویژگی‌هایی +که هر کدام را خاص می‌کند، صحبت خواهیم کرد. -[collections]: ../std/collections/index.html +[collections]: https://doc.rust-lang.org/std/collections/index.html diff --git a/src/ch08-01-vectors.md b/src/ch08-01-vectors.md index d62b7f65d8..6a2e13fd7b 100644 --- a/src/ch08-01-vectors.md +++ b/src/ch08-01-vectors.md @@ -1,17 +1,17 @@ -## Storing Lists of Values with Vectors +## ذخیره لیست‌هایی از مقادیر با بردارها -The first collection type we’ll look at is `Vec`, also known as a _vector_. -Vectors allow you to store more than one value in a single data structure that -puts all the values next to each other in memory. Vectors can only store values -of the same type. They are useful when you have a list of items, such as the -lines of text in a file or the prices of items in a shopping cart. +اولین نوع مجموعه‌ای که به آن خواهیم پرداخت، `Vec` یا همان _بردار_ است. +بردارها به شما اجازه می‌دهند که بیش از یک مقدار را در یک ساختار داده ذخیره کنید +که تمامی مقادیر را در کنار یکدیگر در حافظه قرار می‌دهد. بردارها فقط می‌توانند +مقادیر از یک نوع را ذخیره کنند. این ابزار زمانی مفید است که لیستی از آیتم‌ها +مانند خطوط متنی در یک فایل یا قیمت آیتم‌ها در یک سبد خرید داشته باشید. -### Creating a New Vector +### ایجاد یک بردار جدید -To create a new empty vector, we call the `Vec::new` function, as shown in -Listing 8-1. +برای ایجاد یک بردار خالی جدید، از تابع `Vec::new` استفاده می‌کنیم، همانطور که +در لیست ۸-۱ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-01/src/main.rs:here}} @@ -19,24 +19,24 @@ Listing 8-1. -Note that we added a type annotation here. Because we aren’t inserting any -values into this vector, Rust doesn’t know what kind of elements we intend to -store. This is an important point. Vectors are implemented using generics; -we’ll cover how to use generics with your own types in Chapter 10. For now, -know that the `Vec` type provided by the standard library can hold any type. -When we create a vector to hold a specific type, we can specify the type within -angle brackets. In Listing 8-1, we’ve told Rust that the `Vec` in `v` will -hold elements of the `i32` type. - -More often, you’ll create a `Vec` with initial values and Rust will infer -the type of value you want to store, so you rarely need to do this type -annotation. Rust conveniently provides the `vec!` macro, which will create a -new vector that holds the values you give it. Listing 8-2 creates a new -`Vec` that holds the values `1`, `2`, and `3`. The integer type is `i32` -because that’s the default integer type, as we discussed in the [“Data -Types”][data-types] section of Chapter 3. - -+توجه داشته باشید که ما یک توضیح نوع اضافه کرده‌ایم. چون ما هیچ مقداری به این بردار +اضافه نکرده‌ایم، Rust نمی‌داند چه نوع عناصری را قصد داریم ذخیره کنیم. این نکته +مهمی است. بردارها با استفاده از جنریک‌ها پیاده‌سازی شده‌اند؛ در فصل ۱۰ خواهیم دید +که چگونه می‌توان جنریک‌ها را در انواع خودتان استفاده کرد. در حال حاضر بدانید که +نوع `Vec` ارائه شده توسط کتابخانه استاندارد می‌تواند هر نوعی را نگهداری کند. +وقتی یک بردار برای نگهداری نوع خاصی ایجاد می‌کنیم، می‌توانیم نوع موردنظر را داخل +براکت‌های زاویه‌ای مشخص کنیم. در لیست ۸-۱، ما به Rust اعلام کرده‌ایم که بردار `Vec` +در `v` عناصر نوع `i32` را نگهداری خواهد کرد. + +بیشتر اوقات، شما یک `Vec` با مقادیر اولیه ایجاد خواهید کرد و Rust نوع مقادیر +را از روی آنها استنتاج خواهد کرد، بنابراین به ندرت نیاز به توضیح نوع خواهید داشت. +Rust به راحتی ماکروی `vec!` را فراهم می‌کند که یک بردار جدید ایجاد کرده و مقادیر +مورد نظر شما را در آن قرار می‌دهد. لیست ۸-۲ یک بردار جدید `Vec` را ایجاد می‌کند +که مقادیر `1`، `2` و `3` را نگهداری می‌کند. نوع عدد صحیح `i32` است چون این نوع +پیش‌فرض برای اعداد صحیح است، همانطور که در بخش [“انواع داده‌ها”][data-types] +فصل ۳ بحث کردیم. + + ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-02/src/main.rs:here}} @@ -44,16 +44,16 @@ Types”][data-types] section of Chapter 3. -Because we’ve given initial `i32` values, Rust can infer that the type of `v` -is `Vec`, and the type annotation isn’t necessary. Next, we’ll look at how -to modify a vector. +چون مقادیر اولیه `i32` داده‌ایم، Rust می‌تواند استنتاج کند که نوع `v` +`Vec` است و نیازی به توضیح نوع نیست. حالا به نحوه به‌روزرسانی یک بردار +خواهیم پرداخت. -### Updating a Vector +### به‌روزرسانی یک بردار -To create a vector and then add elements to it, we can use the `push` method, -as shown in Listing 8-3. +برای ایجاد یک بردار و سپس اضافه کردن عناصر به آن، می‌توانیم از متد `push` استفاده کنیم، +همانطور که در لیست ۸-۳ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-03/src/main.rs:here}} @@ -61,21 +61,21 @@ as shown in Listing 8-3. -As with any variable, if we want to be able to change its value, we need to -make it mutable using the `mut` keyword, as discussed in Chapter 3. The numbers -we place inside are all of type `i32`, and Rust infers this from the data, so -we don’t need the `Vec` annotation. +همانطور که با هر متغیری دیگر انجام می‌دهیم، اگر بخواهیم بتوانیم مقدار آن را تغییر دهیم، +باید آن را با استفاده از کلیدواژه `mut` قابل تغییر کنیم، همانطور که در فصل ۳ بحث شد. +اعدادی که در داخل بردار قرار می‌دهیم همه از نوع `i32` هستند و Rust این نوع را از داده‌ها +استنتاج می‌کند، بنابراین نیازی به توضیح نوع `Vec` نیست. -### Reading Elements of Vectors +### خواندن عناصر بردار -There are two ways to reference a value stored in a vector: via indexing or by -using the `get` method. In the following examples, we’ve annotated the types of -the values that are returned from these functions for extra clarity. +دو روش برای ارجاع به یک مقدار ذخیره شده در بردار وجود دارد: از طریق استفاده از اندیس (index)یا +با استفاده از متد `get`. در مثال‌های زیر، انواع مقادیر بازگشتی از این توابع برای وضوح بیشتر +مشخص شده‌اند. -Listing 8-4 shows both methods of accessing a value in a vector, with indexing -syntax and the `get` method. +لیست ۸-۴ هر دو روش دسترسی به یک مقدار در بردار، با استفاده از سینتکس اندیس (index)و متد `get` +را نشان می‌دهد. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-04/src/main.rs:here}} @@ -83,19 +83,17 @@ syntax and the `get` method. -Note a few details here. We use the index value of `2` to get the third element -because vectors are indexed by number, starting at zero. Using `&` and `[]` -gives us a reference to the element at the index value. When we use the `get` -method with the index passed as an argument, we get an `Option<&T>` that we can -use with `match`. +به چند جزئیات اینجا توجه کنید. ما از مقدار اندیس (index)`2` برای دسترسی به عنصر سوم استفاده می‌کنیم +زیرا بردارها با شماره از صفر اندیس‌گذاری می‌شوند. استفاده از `&` و `[]` یک مرجع به عنصر +در مقدار اندیس (index)را به ما می‌دهد. وقتی از متد `get` با اندیسی که به عنوان آرگومان داده می‌شود +استفاده می‌کنیم، یک `Option<&T>` دریافت می‌کنیم که می‌توانیم با `match` از آن استفاده کنیم. -Rust provides these two ways to reference an element so you can choose how the -program behaves when you try to use an index value outside the range of -existing elements. As an example, let’s see what happens when we have a vector -of five elements and then we try to access an element at index 100 with each -technique, as shown in Listing 8-5. +Rust این دو روش ارجاع به یک عنصر را ارائه می‌دهد تا بتوانید انتخاب کنید که برنامه شما چگونه رفتار کند +وقتی تلاش می‌کنید از یک مقدار اندیس (index)خارج از محدوده عناصر موجود استفاده کنید. به عنوان یک مثال، +بیایید ببینیم چه اتفاقی می‌افتد وقتی یک بردار با پنج عنصر داشته باشیم و سپس تلاش کنیم به یک عنصر +در اندیس (index)۱۰۰ با هر دو تکنیک دسترسی پیدا کنیم، همانطور که در لیست ۸-۵ نشان داده شده است. -+ ```rust,should_panic,panics {{#rustdoc_include ../listings/ch08-common-collections/listing-08-05/src/main.rs:here}} @@ -103,32 +101,27 @@ technique, as shown in Listing 8-5. -When we run this code, the first `[]` method will cause the program to panic -because it references a nonexistent element. This method is best used when you -want your program to crash if there’s an attempt to access an element past the -end of the vector. - -When the `get` method is passed an index that is outside the vector, it returns -`None` without panicking. You would use this method if accessing an element -beyond the range of the vector may happen occasionally under normal -circumstances. Your code will then have logic to handle having either -`Some(&element)` or `None`, as discussed in Chapter 6. For example, the index -could be coming from a person entering a number. If they accidentally enter a -number that’s too large and the program gets a `None` value, you could tell the -user how many items are in the current vector and give them another chance to -enter a valid value. That would be more user-friendly than crashing the program -due to a typo! - -When the program has a valid reference, the borrow checker enforces the -ownership and borrowing rules (covered in Chapter 4) to ensure this reference -and any other references to the contents of the vector remain valid. Recall the -rule that states you can’t have mutable and immutable references in the same -scope. That rule applies in Listing 8-6, where we hold an immutable reference -to the first element in a vector and try to add an element to the end. This -program won’t work if we also try to refer to that element later in the -function. - -+وقتی این کد را اجرا می‌کنیم، روش اول `[]` باعث می‌شود برنامه متوقف شود زیرا به یک +عنصر غیرموجود اشاره می‌کند. این روش زمانی بهترین استفاده را دارد که بخواهید برنامه‌تان +در صورت تلاش برای دسترسی به عنصری خارج از انتهای بردار، متوقف شود. + +وقتی متد `get` یک اندیس (index)خارج از بردار دریافت می‌کند، مقدار `None` را بدون متوقف کردن +برنامه بازمی‌گرداند. شما از این روش استفاده می‌کنید اگر دسترسی به عنصری خارج از محدوده بردار +ممکن است گاه‌به‌گاه در شرایط عادی رخ دهد. کد شما سپس منطق لازم برای مدیریت داشتن +`Some(&element)` یا `None` را خواهد داشت، همانطور که در فصل ۶ بحث شد. برای مثال، +اندیس (index)ممکن است از یک عدد ورودی توسط کاربر بیاید. اگر کاربر تصادفاً عددی وارد کند که بیش از حد +بزرگ باشد و برنامه مقدار `None` دریافت کند، شما می‌توانید به کاربر اطلاع دهید که چند آیتم +در بردار موجود است و به او فرصت دیگری برای وارد کردن یک مقدار معتبر بدهید. این راهکار برای +کاربر پسندتر است تا این که برنامه به دلیل یک اشتباه تایپی متوقف شود! + +وقتی برنامه یک مرجع معتبر دارد، بررسی‌کننده قرض قوانین مالکیت و قرض‌گیری (که در فصل ۴ پوشش داده شد) +را اعمال می‌کند تا اطمینان حاصل کند که این مرجع و هر مرجع دیگری به محتوای بردار معتبر باقی می‌مانند. +به یاد بیاورید که قانون بیان می‌کند نمی‌توانید مرجع‌های قابل تغییر و غیرقابل تغییر را در یک حوزه +داشته باشید. این قانون در لیست ۸-۶ اعمال می‌شود، جایی که یک مرجع غیرقابل تغییر به اولین عنصر +در یک بردار نگه داشته شده است و سعی داریم یک عنصر به انتها اضافه کنیم. این برنامه زمانی کار نخواهد کرد +اگر همچنین بخواهیم بعداً در تابع به آن عنصر ارجاع دهیم. + + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch08-common-collections/listing-08-06/src/main.rs:here}} @@ -136,33 +129,31 @@ function. -Compiling this code will result in this error: +کامپایل کردن این کد به این خطا منجر می‌شود: ```console {{#include ../listings/ch08-common-collections/listing-08-06/output.txt}} ``` -The code in Listing 8-6 might look like it should work: why should a reference -to the first element care about changes at the end of the vector? This error is -due to the way vectors work: because vectors put the values next to each other -in memory, adding a new element onto the end of the vector might require -allocating new memory and copying the old elements to the new space, if there -isn’t enough room to put all the elements next to each other where the vector -is currently stored. In that case, the reference to the first element would be -pointing to deallocated memory. The borrowing rules prevent programs from -ending up in that situation. +کد در لیست ۸-۶ ممکن است به نظر بیاید که باید کار کند: چرا یک مرجع به اولین عنصر باید +به تغییرات انتهای بردار اهمیت دهد؟ این خطا به نحوه کار بردارها مربوط است: چون بردارها مقادیر +را در کنار یکدیگر در حافظه قرار می‌دهند، اضافه کردن یک عنصر جدید به انتهای بردار ممکن است نیازمند +اختصاص حافظه جدید و کپی کردن عناصر قدیمی به مکان جدید باشد، اگر فضای کافی برای قرار دادن +همه عناصر در کنار یکدیگر در محل کنونی بردار وجود نداشته باشد. در این حالت، مرجع به اولین +عنصر به حافظه‌ای اشاره می‌کند که آزاد شده است. قوانین قرض‌گیری از به وجود آمدن این شرایط +در برنامه‌ها جلوگیری می‌کنند. -> Note: For more on the implementation details of the `Vec` type, see [“The -> Rustonomicon”][nomicon]. +> نکته: برای اطلاعات بیشتر درباره جزئیات پیاده‌سازی نوع `Vec`، به [“The Rustonomicon”][nomicon] مراجعه کنید. -### Iterating Over the Values in a Vector -To access each element in a vector in turn, we would iterate through all of the -elements rather than use indices to access one at a time. Listing 8-7 shows how -to use a `for` loop to get immutable references to each element in a vector of -`i32` values and print them. +### پیمایش بر روی مقادیر در یک بردار -+برای دسترسی به هر عنصر در یک بردار به ترتیب، می‌توانیم به جای استفاده از اندیس‌ها +برای دسترسی به یک عنصر در هر بار، بر روی تمامی عناصر پیمایش کنیم. لیست ۸-۷ نشان می‌دهد +چگونه می‌توان از یک حلقه `for` برای گرفتن مرجع‌های غیرقابل تغییر به هر عنصر در یک بردار +از مقادیر `i32` استفاده کرد و آنها را چاپ کرد. + + ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-07/src/main.rs:here}} @@ -170,11 +161,10 @@ to use a `for` loop to get immutable references to each element in a vector of -We can also iterate over mutable references to each element in a mutable vector -in order to make changes to all the elements. The `for` loop in Listing 8-8 -will add `50` to each element. +همچنین می‌توانیم بر روی مرجع‌های قابل تغییر به هر عنصر در یک بردار قابل تغییر پیمایش کنیم +تا تغییراتی روی تمام عناصر اعمال کنیم. حلقه `for` در لیست ۸-۸ مقدار `50` را به هر عنصر اضافه می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-08/src/main.rs:here}} @@ -182,34 +172,36 @@ will add `50` to each element. -To change the value that the mutable reference refers to, we have to use the -`*` dereference operator to get to the value in `i` before we can use the `+=` -operator. We’ll talk more about the dereference operator in the [“Following the -Reference to the Value”][deref] section of Chapter 15. +برای تغییر مقداری که رفرنس قابل‌تغییر به آن اشاره می‌کند، +باید از عملگر `*` برای dereference کردن استفاده کنیم تا به مقدار درون `i` دسترسی پیدا کنیم +و سپس بتوانیم از عملگر `+=` استفاده کنیم. +در بخش [“دنبال کردن رفرنس تا رسیدن به مقدار”][deref] در فصل ۱۵، +بیشتر درباره‌ی عملگر dereference صحبت خواهیم کرد. + +پیمایش بر روی یک بردار، چه به صورت غیرقابل تغییر و چه به صورت قابل تغییر، امن است +زیرا از قوانین بررسی‌کننده قرض پیروی می‌کند. اگر بخواهیم در بدنه حلقه‌های `for` در لیست ۸-۷ +و لیست ۸-۸ آیتم‌ها را درج یا حذف کنیم، با خطای کامپایل مشابهی با کدی که در لیست ۸-۶ دیدیم +روبرو خواهیم شد. مرجع به برداری که حلقه `for` نگه می‌دارد از تغییر همزمان کل بردار +جلوگیری می‌کند. -Iterating over a vector, whether immutably or mutably, is safe because of the -borrow checker’s rules. If we attempted to insert or remove items in the `for` -loop bodies in Listing 8-7 and Listing 8-8, we would get a compiler error -similar to the one we got with the code in Listing 8-6. The reference to the -vector that the `for` loop holds prevents simultaneous modification of the -whole vector. -### Using an Enum to Store Multiple Types +### استفاده از Enum برای ذخیره انواع مختلف -Vectors can only store values that are of the same type. This can be -inconvenient; there are definitely use cases for needing to store a list of -items of different types. Fortunately, the variants of an enum are defined -under the same enum type, so when we need one type to represent elements of -different types, we can define and use an enum! +بردارها فقط می‌توانند مقادیر از یک نوع را ذخیره کنند. این موضوع ممکن است گاهی +ناخوشایند باشد؛ مطمئناً موارد استفاده‌ای وجود دارند که نیاز به ذخیره یک لیست +از آیتم‌ها با انواع مختلف دارید. خوشبختانه، متغیرهای یک `enum` تحت یک نوع +`enum` تعریف شده‌اند، بنابراین وقتی نیاز به یک نوع برای نمایش عناصر از انواع +مختلف دارید، می‌توانید یک `enum` تعریف کرده و از آن استفاده کنید! -For example, say we want to get values from a row in a spreadsheet in which -some of the columns in the row contain integers, some floating-point numbers, -and some strings. We can define an enum whose variants will hold the different -value types, and all the enum variants will be considered the same type: that -of the enum. Then we can create a vector to hold that enum and so, ultimately, -hold different types. We’ve demonstrated this in Listing 8-9. +برای مثال، فرض کنید می‌خواهیم مقادیر یک ردیف از یک صفحه گسترده را که برخی از +ستون‌های آن شامل اعداد صحیح، برخی شامل اعداد اعشاری و برخی شامل رشته‌ها +می‌باشند، دریافت کنیم. می‌توانیم یک `enum` تعریف کنیم که متغیرهای آن انواع +مختلف مقادیر را نگهداری کنند، و تمام متغیرهای `enum` به عنوان یک نوع مشابه +(یعنی نوع `enum`) در نظر گرفته می‌شوند. سپس می‌توانیم یک بردار ایجاد کنیم +که این `enum` را نگهداری کند و در نتیجه انواع مختلف را ذخیره کند. این موضوع +در لیست ۸-۹ نمایش داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-09/src/main.rs:here}} @@ -217,29 +209,31 @@ hold different types. We’ve demonstrated this in Listing 8-9. -Rust needs to know what types will be in the vector at compile time so it knows -exactly how much memory on the heap will be needed to store each element. We -must also be explicit about what types are allowed in this vector. If Rust -allowed a vector to hold any type, there would be a chance that one or more of -the types would cause errors with the operations performed on the elements of -the vector. Using an enum plus a `match` expression means that Rust will ensure -at compile time that every possible case is handled, as discussed in Chapter 6. +Rust باید بداند چه انواعی در بردار خواهند بود تا بتواند در زمان کامپایل +دقیقاً مشخص کند چه مقدار حافظه در heap برای ذخیره هر عنصر نیاز است. +همچنین باید به طور صریح مشخص کنیم که چه انواعی در این بردار مجاز هستند. +اگر Rust اجازه می‌داد که بردار هر نوعی را نگهداری کند، احتمال داشت که +یک یا چند نوع باعث ایجاد خطا در عملیات انجام شده روی عناصر بردار شوند. +استفاده از یک `enum` به علاوه یک عبارت `match` به این معنی است که Rust +در زمان کامپایل اطمینان حاصل خواهد کرد که تمام حالت‌های ممکن مدیریت شده‌اند، +همانطور که در فصل ۶ بحث شد. -If you don’t know the exhaustive set of types a program will get at runtime to -store in a vector, the enum technique won’t work. Instead, you can use a trait -object, which we’ll cover in Chapter 18. +اگر مجموعه جامعی از انواعی که برنامه در زمان اجرا دریافت می‌کند و باید +در بردار ذخیره شود را نمی‌دانید، تکنیک `enum` کار نخواهد کرد. به جای آن، +می‌توانید از یک شیء ویژگی (`trait object`) استفاده کنید که در فصل ۱۸ +مورد بررسی قرار خواهد گرفت. -Now that we’ve discussed some of the most common ways to use vectors, be sure -to review [the API documentation][vec-api] for all of the many -useful methods defined on `Vec` by the standard library. For example, in -addition to `push`, a `pop` method removes and returns the last element. +اکنون که برخی از رایج‌ترین روش‌های استفاده از بردارها را بحث کردیم، مطمئن شوید +که [مستندات API][vec-api] را برای تمام متدهای مفیدی که کتابخانه +استاندارد روی `Vec` تعریف کرده است مرور کنید. برای مثال، علاوه بر `push`، +متد `pop` عنصر آخر را حذف کرده و بازمی‌گرداند. -### Dropping a Vector Drops Its Elements +### حذف یک بردار، عناصر آن را نیز حذف می‌کند -Like any other `struct`, a vector is freed when it goes out of scope, as -annotated in Listing 8-10. +مانند هر `struct` دیگری، یک بردار وقتی از محدوده خارج می‌شود آزاد می‌شود، +همانطور که در لیست ۸-۱۰ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-10/src/main.rs:here}} @@ -247,14 +241,13 @@ annotated in Listing 8-10. -When the vector gets dropped, all of its contents are also dropped, meaning the -integers it holds will be cleaned up. The borrow checker ensures that any -references to contents of a vector are only used while the vector itself is -valid. +وقتی بردار حذف می‌شود، تمام محتوای آن نیز حذف می‌شوند، به این معنی که اعداد +صحیحی که نگهداری می‌کند تمیزکاری می‌شوند. بررسی‌کننده قرض اطمینان حاصل می‌کند +که هر مرجع به محتوای یک بردار فقط تا زمانی که خود بردار معتبر است استفاده شود. -Let’s move on to the next collection type: `String`! +حال به نوع مجموعه بعدی می‌پردازیم: `String`! [data-types]: ch03-02-data-types.html#data-types [nomicon]: ../nomicon/vec/vec.html -[vec-api]: ../std/vec/struct.Vec.html +[vec-api]: https://doc.rust-lang.org/std/vec/struct.Vec.html [deref]: ch15-02-deref.html#following-the-pointer-to-the-value-with-the-dereference-operator diff --git a/src/ch08-02-strings.md b/src/ch08-02-strings.md index 6df7dd83cb..269fbeca51 100644 --- a/src/ch08-02-strings.md +++ b/src/ch08-02-strings.md @@ -1,47 +1,43 @@ -## Storing UTF-8 Encoded Text with Strings - -We talked about strings in Chapter 4, but we’ll look at them in more depth now. -New Rustaceans commonly get stuck on strings for a combination of three -reasons: Rust’s propensity for exposing possible errors, strings being a more -complicated data structure than many programmers give them credit for, and -UTF-8. These factors combine in a way that can seem difficult when you’re -coming from other programming languages. - -We discuss strings in the context of collections because strings are -implemented as a collection of bytes, plus some methods to provide useful -functionality when those bytes are interpreted as text. In this section, we’ll -talk about the operations on `String` that every collection type has, such as -creating, updating, and reading. We’ll also discuss the ways in which `String` -is different from the other collections, namely how indexing into a `String` is -complicated by the differences between how people and computers interpret -`String` data. - -### What Is a String? - -We’ll first define what we mean by the term _string_. Rust has only one string -type in the core language, which is the string slice `str` that is usually seen -in its borrowed form `&str`. In Chapter 4, we talked about _string slices_, -which are references to some UTF-8 encoded string data stored elsewhere. String -literals, for example, are stored in the program’s binary and are therefore -string slices. - -The `String` type, which is provided by Rust’s standard library rather than -coded into the core language, is a growable, mutable, owned, UTF-8 encoded -string type. When Rustaceans refer to “strings” in Rust, they might be -referring to either the `String` or the string slice `&str` types, not just one -of those types. Although this section is largely about `String`, both types are -used heavily in Rust’s standard library, and both `String` and string slices -are UTF-8 encoded. - -### Creating a New String - -Many of the same operations available with `Vec` are available with `String` -as well because `String` is actually implemented as a wrapper around a vector -of bytes with some extra guarantees, restrictions, and capabilities. An example -of a function that works the same way with `Vec` and `String` is the `new` -function to create an instance, shown in Listing 8-11. - -+## ذخیره متن‌های کدگذاری شده UTF-8 با رشته‌ها (strings) + +ما در فصل ۴ درباره رشته‌ها صحبت کردیم، اما اکنون به آن‌ها با عمق بیشتری نگاه خواهیم کرد. +Rustaceanهای تازه‌وارد معمولاً به دلیل ترکیبی از سه عامل در رشته‌ها دچار مشکل می‌شوند: +گرایش Rust به آشکارسازی خطاهای ممکن، رشته‌ها به عنوان یک ساختار داده پیچیده‌تر از آنچه +بسیاری از برنامه‌نویسان تصور می‌کنند، و UTF-8. این عوامل به نحوی ترکیب می‌شوند که می‌توانند +برای کسانی که از زبان‌های برنامه‌نویسی دیگر می‌آیند دشوار باشند. + +ما رشته‌ها را در زمینه مجموعه‌ها بررسی می‌کنیم، زیرا رشته‌ها به عنوان مجموعه‌ای از بایت‌ها +پیاده‌سازی شده‌اند، به علاوه تعدادی متد برای ارائه قابلیت‌های مفید زمانی که این بایت‌ها +به عنوان متن تفسیر می‌شوند. در این بخش، درباره عملیات‌هایی که روی `String` انجام می‌شود +و هر نوع مجموعه‌ای آن‌ها را دارد، مانند ایجاد، به‌روزرسانی، و خواندن صحبت خواهیم کرد. +همچنین تفاوت‌های `String` با سایر مجموعه‌ها را مورد بحث قرار می‌دهیم، به‌ویژه نحوه پیچیدگی +اندیس‌گذاری در یک `String` به دلیل تفاوت‌های بین تفسیر داده‌های `String` توسط انسان‌ها +و کامپیوترها. + +### رشته (string) چیست؟ + +ابتدا تعریف می‌کنیم که منظور ما از اصطلاح _رشته_ چیست. Rust فقط یک نوع رشته در زبان +هسته خود دارد که همان قطعه رشته `str` است که معمولاً به صورت قرض گرفته شده `&str` +دیده می‌شود. در فصل ۴ درباره _قطعه‌های رشته_ صحبت کردیم، که ارجاعاتی به داده‌های رشته‌ای +کدگذاری شده UTF-8 هستند که در جای دیگری ذخیره شده‌اند. به عنوان مثال، رشته‌های +لیترال در باینری برنامه ذخیره می‌شوند و بنابراین قطعه‌های رشته هستند. + +نوع `String`، که توسط کتابخانه استاندارد Rust ارائه شده است و نه مستقیماً در زبان هسته +کدگذاری شده، یک نوع رشته رشدپذیر، قابل تغییر، و مالک UTF-8 است. وقتی Rustaceanها +به "رشته‌ها" در Rust اشاره می‌کنند، ممکن است به نوع `String` یا قطعه رشته `&str` اشاره +کنند، نه فقط یکی از این دو نوع. اگرچه این بخش عمدتاً درباره `String` است، اما هر دو نوع +در کتابخانه استاندارد Rust به شدت مورد استفاده قرار می‌گیرند و هر دو `String` و قطعه‌های +رشته کدگذاری UTF-8 دارند. + +### ایجاد یک رشته (strings) جدید + +بسیاری از عملیات مشابه موجود در `Vec` برای `String` نیز در دسترس است، زیرا `String` +در واقع به عنوان یک پوششی بر روی یک بردار از بایت‌ها پیاده‌سازی شده است، با برخی +ضمانت‌ها، محدودیت‌ها، و قابلیت‌های اضافی. مثالی از یک تابع که به همان روش با +`Vec` و `String` کار می‌کند، تابع `new` برای ایجاد یک نمونه است، همانطور که در لیست +۸-۱۱ نشان داده شده است. + + ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-11/src/main.rs:here}} @@ -49,13 +45,12 @@ function to create an instance, shown in Listing 8-11. -This line creates a new, empty string called `s`, into which we can then load -data. Often, we’ll have some initial data with which we want to start the -string. For that, we use the `to_string` method, which is available on any type -that implements the `Display` trait, as string literals do. Listing 8-12 shows -two examples. +این خط یک رشته جدید و خالی به نام `s` ایجاد می‌کند که می‌توانیم داده‌ها را در آن بارگذاری کنیم. +اغلب، داده‌های اولیه‌ای خواهیم داشت که می‌خواهیم رشته را با آن‌ها شروع کنیم. برای این کار، +از متد `to_string` استفاده می‌کنیم که بر روی هر نوعی که ویژگی `Display` را پیاده‌سازی +می‌کند، همانند رشته‌های لیترال، در دسترس است. لیست ۸-۱۲ دو مثال را نشان می‌دهد. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-12/src/main.rs:here}} @@ -63,13 +58,12 @@ two examples. -This code creates a string containing `initial contents`. +این کد یک رشته حاوی `initial contents` ایجاد می‌کند. -We can also use the function `String::from` to create a `String` from a string -literal. The code in Listing 8-13 is equivalent to the code in Listing 8-12 -that uses `to_string`. +ما همچنین می‌توانیم از تابع `String::from` برای ایجاد یک `String` از یک رشته لیترال +استفاده کنیم. کد در لیست ۸-۱۳ معادل کدی است که در لیست ۸-۱۲ از `to_string` استفاده می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-13/src/main.rs:here}} @@ -77,16 +71,16 @@ that uses `to_string`. -Because strings are used for so many things, we can use many different generic -APIs for strings, providing us with a lot of options. Some of them can seem -redundant, but they all have their place! In this case, `String::from` and -`to_string` do the same thing, so which one you choose is a matter of style and -readability. +از آنجا که رشته‌ها برای موارد بسیاری استفاده می‌شوند، می‌توانیم از بسیاری از APIهای +جنریک مختلف برای رشته‌ها استفاده کنیم که گزینه‌های زیادی را در اختیار ما قرار می‌دهند. +برخی از این‌ها ممکن است به نظر اضافی بیایند، اما هرکدام جایگاه خاص خود را دارند! +در این مورد، `String::from` و `to_string` عملکرد یکسانی دارند، بنابراین انتخاب بین آن‌ها +مسئله سبک و خوانایی کد است. -Remember that strings are UTF-8 encoded, so we can include any properly encoded -data in them, as shown in Listing 8-14. +به یاد داشته باشید که رشته‌ها با کدگذاری UTF-8 هستند، بنابراین می‌توانیم هر داده‌ای که +به طور صحیح کدگذاری شده باشد را در آن‌ها قرار دهیم، همانطور که در لیست ۸-۱۴ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-14/src/main.rs:here}} @@ -94,20 +88,20 @@ data in them, as shown in Listing 8-14. -All of these are valid `String` values. +تمام این موارد مقادیر معتبر `String` هستند. -### Updating a String +### به‌روزرسانی یک رشته -A `String` can grow in size and its contents can change, just like the contents -of a `Vec`, if you push more data into it. In addition, you can conveniently -use the `+` operator or the `format!` macro to concatenate `String` values. +یک `String` می‌تواند از نظر اندازه رشد کند و محتوای آن تغییر کند، همانطور که محتوای +یک `Vec` تغییر می‌کند، اگر داده بیشتری به آن اضافه کنیم. علاوه بر این، می‌توانیم به راحتی +از عملگر `+` یا ماکروی `format!` برای الحاق مقادیر `String` استفاده کنیم. -#### Appending to a String with `push_str` and `push` +#### الحاق به یک رشته (string) با `push_str` و `push` -We can grow a `String` by using the `push_str` method to append a string slice, -as shown in Listing 8-15. +ما می‌توانیم یک `String` را با استفاده از متد `push_str` برای الحاق یک قطعه رشته رشد دهیم، +همانطور که در لیست ۸-۱۵ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-15/src/main.rs:here}} @@ -115,12 +109,11 @@ as shown in Listing 8-15. -After these two lines, `s` will contain `foobar`. The `push_str` method takes a -string slice because we don’t necessarily want to take ownership of the -parameter. For example, in the code in Listing 8-16, we want to be able to use -`s2` after appending its contents to `s1`. +بعد از این دو خط، مقدار `s` شامل `foobar` خواهد بود. متد `push_str` یک قطعه رشته را به عنوان آرگومان می‌گیرد +زیرا ما لزوماً نمی‌خواهیم مالکیت پارامتر را بگیریم. برای مثال، در کدی که در لیست ۸-۱۶ نشان داده شده است، +ما می‌خواهیم بتوانیم پس از الحاق محتوای `s2` به `s1` همچنان از `s2` استفاده کنیم. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-16/src/main.rs:here}} @@ -128,14 +121,13 @@ parameter. For example, in the code in Listing 8-16, we want to be able to use -If the `push_str` method took ownership of `s2`, we wouldn’t be able to print -its value on the last line. However, this code works as we’d expect! +اگر متد `push_str` مالکیت `s2` را می‌گرفت، نمی‌توانستیم مقدار آن را در خط آخر چاپ کنیم. با این حال، +این کد همانطور که انتظار می‌رود کار می‌کند! -The `push` method takes a single character as a parameter and adds it to the -`String`. Listing 8-17 adds the letter _l_ to a `String` using the `push` -method. +متد `push` یک کاراکتر را به عنوان پارامتر می‌گیرد و آن را به `String` اضافه می‌کند. لیست ۸-۱۷ +حرف _l_ را با استفاده از متد `push` به یک `String` اضافه می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-17/src/main.rs:here}} @@ -143,14 +135,14 @@ method. -As a result, `s` will contain `lol`. +در نتیجه، مقدار `s` شامل `lol` خواهد بود. -#### Concatenation with the `+` Operator or the `format!` Macro +#### الحاق با استفاده از عملگر `+` یا ماکروی `format!` -Often, you’ll want to combine two existing strings. One way to do so is to use -the `+` operator, as shown in Listing 8-18. +اغلب، ممکن است بخواهید دو رشته موجود را با هم ترکیب کنید. یکی از راه‌های انجام این کار +استفاده از عملگر `+` است، همانطور که در لیست ۸-۱۸ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-18/src/main.rs:here}} @@ -158,73 +150,64 @@ the `+` operator, as shown in Listing 8-18. -The string `s3` will contain `Hello, world!`. The reason `s1` is no longer -valid after the addition, and the reason we used a reference to `s2`, has to do -with the signature of the method that’s called when we use the `+` operator. -The `+` operator uses the `add` method, whose signature looks something like -this: +مقدار `s3` شامل `Hello, world!` خواهد بود. دلیل اینکه `s1` پس از این الحاق دیگر معتبر نیست +و دلیل اینکه ما از یک مرجع به `s2` استفاده کردیم، به امضای متدی که هنگام استفاده از +عملگر `+` فراخوانی می‌شود مربوط است. عملگر `+` از متد `add` استفاده می‌کند که امضای آن به شکل زیر است: ```rust,ignore fn add(self, s: &str) -> String { ``` -In the standard library, you’ll see `add` defined using generics and associated -types. Here, we’ve substituted in concrete types, which is what happens when we -call this method with `String` values. We’ll discuss generics in Chapter 10. -This signature gives us the clues we need in order to understand the tricky -bits of the `+` operator. - -First, `s2` has an `&`, meaning that we’re adding a _reference_ of the second -string to the first string. This is because of the `s` parameter in the `add` -function: we can only add a `&str` to a `String`; we can’t add two `String` -values together. But wait—the type of `&s2` is `&String`, not `&str`, as -specified in the second parameter to `add`. So why does Listing 8-18 compile? - -The reason we’re able to use `&s2` in the call to `add` is that the compiler -can _coerce_ the `&String` argument into a `&str`. When we call the `add` -method, Rust uses a _deref coercion_, which here turns `&s2` into `&s2[..]`. -We’ll discuss deref coercion in more depth in Chapter 15. Because `add` does -not take ownership of the `s` parameter, `s2` will still be a valid `String` -after this operation. - -Second, we can see in the signature that `add` takes ownership of `self` -because `self` does _not_ have an `&`. This means `s1` in Listing 8-18 will be -moved into the `add` call and will no longer be valid after that. So, although -`let s3 = s1 + &s2;` looks like it will copy both strings and create a new one, -this statement actually takes ownership of `s1`, appends a copy of the contents -of `s2`, and then returns ownership of the result. In other words, it looks -like it’s making a lot of copies, but it isn’t; the implementation is more -efficient than copying. - -If we need to concatenate multiple strings, the behavior of the `+` operator -gets unwieldy: +در کتابخانه استاندارد، شما `add` را خواهید دید که با استفاده از جنریک‌ها و انواع مرتبط تعریف شده است. +اینجا، ما انواع مشخصی را جایگزین کرده‌ایم، که این همان چیزی است که هنگام فراخوانی این متد با مقادیر +`String` اتفاق می‌افتد. درباره جنریک‌ها در فصل ۱۰ صحبت خواهیم کرد. این امضا به ما سرنخ‌هایی می‌دهد +تا بتوانیم بخش‌های چالش‌برانگیز عملگر `+` را درک کنیم. + +اول، `s2` یک `&` دارد، به این معنی که ما یک _مرجع_ از رشته دوم را به رشته اول اضافه می‌کنیم. +این به دلیل پارامتر `s` در تابع `add` است: ما فقط می‌توانیم یک `&str` را به یک `String` اضافه کنیم؛ +نمی‌توانیم دو مقدار `String` را با هم جمع کنیم. اما صبر کنید—نوع `&s2`، `&String` است، نه `&str` +همانطور که در پارامتر دوم `add` مشخص شده است. پس چرا کد در لیست ۸-۱۸ کامپایل می‌شود؟ + +دلیل اینکه می‌توانیم از `&s2` در فراخوانی `add` استفاده کنیم این است که کامپایلر می‌تواند آرگومان +`&String` را به `&str` تبدیل کند. هنگامی که ما متد `add` را فراخوانی می‌کنیم، Rust از یک +_coercion deref_ استفاده می‌کند که در اینجا `&s2` را به `&s2[..]` تبدیل می‌کند. ما این موضوع +را در فصل ۱۵ به طور عمیق‌تری بررسی خواهیم کرد. از آنجا که `add` مالکیت پارامتر `s` را نمی‌گیرد، +`s2` پس از این عملیات همچنان یک `String` معتبر باقی خواهد ماند. + +دوم، می‌توانیم در امضا ببینیم که `add` مالکیت `self` را می‌گیرد زیرا `self` یک `&` ندارد. +این بدان معناست که `s1` در لیست ۸-۱۸ به فراخوانی `add` منتقل می‌شود و پس از آن دیگر معتبر نخواهد بود. +بنابراین، اگرچه `let s3 = s1 + &s2;` به نظر می‌رسد که هر دو رشته را کپی می‌کند و یک رشته جدید ایجاد +می‌کند، این عبارت در واقع مالکیت `s1` را می‌گیرد، یک کپی از محتوای `s2` را اضافه می‌کند، و سپس مالکیت +نتیجه را بازمی‌گرداند. به عبارت دیگر، به نظر می‌رسد که کپی‌های زیادی انجام می‌دهد، اما اینطور نیست؛ +پیاده‌سازی کارآمدتر از کپی کردن است. + +اگر نیاز به الحاق چندین رشته داشته باشیم، رفتار عملگر `+` دست‌وپاگیر می‌شود: ```rust {{#rustdoc_include ../listings/ch08-common-collections/no-listing-01-concat-multiple-strings/src/main.rs:here}} ``` -At this point, `s` will be `tic-tac-toe`. With all of the `+` and `"` -characters, it’s difficult to see what’s going on. For combining strings in -more complicated ways, we can instead use the `format!` macro: +در این نقطه، مقدار `s` برابر با `tic-tac-toe` خواهد بود. با تمام این `+` و کاراکترهای `"`، +دیدن اینکه چه اتفاقی می‌افتد دشوار است. برای ترکیب رشته‌ها به روش‌های پیچیده‌تر، می‌توانیم +به جای آن از ماکروی `format!` استفاده کنیم: ```rust {{#rustdoc_include ../listings/ch08-common-collections/no-listing-02-format/src/main.rs:here}} ``` -This code also sets `s` to `tic-tac-toe`. The `format!` macro works like -`println!`, but instead of printing the output to the screen, it returns a -`String` with the contents. The version of the code using `format!` is much -easier to read, and the code generated by the `format!` macro uses references -so that this call doesn’t take ownership of any of its parameters. +این کد نیز مقدار `s` را به `tic-tac-toe` تنظیم می‌کند. ماکروی `format!` شبیه به `println!` کار می‌کند، +اما به جای چاپ خروجی روی صفحه، یک `String` با محتوای مورد نظر بازمی‌گرداند. نسخه کد با استفاده از +`format!` بسیار خواناتر است و کدی که توسط ماکروی `format!` تولید می‌شود از مراجع استفاده می‌کند، +بنابراین این فراخوانی مالکیت هیچ‌یک از پارامترهایش را نمی‌گیرد. -### Indexing into Strings +### اندیس‌گذاری در رشته‌ها -In many other programming languages, accessing individual characters in a -string by referencing them by index is a valid and common operation. However, -if you try to access parts of a `String` using indexing syntax in Rust, you’ll -get an error. Consider the invalid code in Listing 8-19. +در بسیاری از زبان‌های برنامه‌نویسی دیگر، دسترسی به کاراکترهای منفرد در یک رشته با اشاره به آن‌ها +توسط اندیس (index)یک عملیات معتبر و رایج است. با این حال، اگر تلاش کنید در Rust با استفاده از سینتکس +اندیس‌گذاری به بخش‌هایی از یک `String` دسترسی پیدا کنید، با خطا مواجه می‌شوید. کد نامعتبر +در لیست ۸-۱۹ را در نظر بگیرید. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch08-common-collections/listing-08-19/src/main.rs:here}} @@ -232,109 +215,102 @@ get an error. Consider the invalid code in Listing 8-19. -This code will result in the following error: +این کد به خطای زیر منجر خواهد شد: ```console {{#include ../listings/ch08-common-collections/listing-08-19/output.txt}} ``` -The error and the note tell the story: Rust strings don’t support indexing. But -why not? To answer that question, we need to discuss how Rust stores strings in -memory. +خطا و توضیحات آن گویای موضوع است: رشته‌های Rust از اندیس‌گذاری پشتیبانی نمی‌کنند. اما چرا؟ +برای پاسخ به این سؤال، باید درباره نحوه ذخیره‌سازی رشته‌ها در حافظه توسط Rust صحبت کنیم. -#### Internal Representation +#### نمایش داخلی -A `String` is a wrapper over a `Vec`. Let’s look at some of our properly -encoded UTF-8 example strings from Listing 8-14. First, this one: +یک `String` در واقع یک پوشش بر روی `Vec` است. بیایید به برخی از مثال‌های رشته‌های کدگذاری +شده UTF-8 در لیست ۸-۱۴ نگاه کنیم. ابتدا این مورد: ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-14/src/main.rs:spanish}} ``` -In this case, `len` will be `4`, which means the vector storing the string -`"Hola"` is 4 bytes long. Each of these letters takes one byte when encoded in -UTF-8. The following line, however, may surprise you (note that this string -begins with the capital Cyrillic letter _Ze_, not the number 3): +در این حالت، مقدار `len` برابر با `4` خواهد بود، به این معنی که برداری که رشته `"Hola"` را +ذخیره می‌کند ۴ بایت طول دارد. هر یک از این حروف هنگام کدگذاری در UTF-8 یک بایت می‌گیرد. +با این حال، خط زیر ممکن است شما را شگفت‌زده کند (توجه داشته باشید که این رشته با حرف بزرگ +سیریلیک _Ze_ آغاز می‌شود، نه عدد ۳): ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-14/src/main.rs:russian}} ``` -If you were asked how long the string is, you might say 12. In fact, Rust’s -answer is 24: that’s the number of bytes it takes to encode “Здравствуйте” in -UTF-8, because each Unicode scalar value in that string takes 2 bytes of -storage. Therefore, an index into the string’s bytes will not always correlate -to a valid Unicode scalar value. To demonstrate, consider this invalid Rust -code: +اگر از شما پرسیده شود طول این رشته چقدر است، ممکن است بگویید ۱۲. اما در واقع، پاسخ Rust +۲۴ است: این تعداد بایت‌هایی است که برای کدگذاری “Здравствуйте” در UTF-8 نیاز است، زیرا هر مقدار +اسکالر Unicode در این رشته ۲ بایت فضای ذخیره‌سازی می‌گیرد. بنابراین، یک اندیس (index)در بایت‌های رشته +همیشه با یک مقدار اسکالر Unicode معتبر مطابقت ندارد. برای نشان دادن این موضوع، کد نامعتبر +زیر در Rust را در نظر بگیرید: ```rust,ignore,does_not_compile let hello = "Здравствуйте"; let answer = &hello[0]; ``` -You already know that `answer` will not be `З`, the first letter. When encoded -in UTF-8, the first byte of `З` is `208` and the second is `151`, so it would -seem that `answer` should in fact be `208`, but `208` is not a valid character -on its own. Returning `208` is likely not what a user would want if they asked -for the first letter of this string; however, that’s the only data that Rust -has at byte index 0. Users generally don’t want the byte value returned, even -if the string contains only Latin letters: if `&"hi"[0]` were valid code that -returned the byte value, it would return `104`, not `h`. +شما قبلاً می‌دانید که مقدار `answer` برابر با `З`، اولین حرف، نخواهد بود. وقتی در UTF-8 کدگذاری +می‌شود، اولین بایت از `З` برابر با `208` و دومین بایت برابر با `151` است، بنابراین ممکن است به نظر +برسد که `answer` باید در واقع `208` باشد، اما `208` به تنهایی یک کاراکتر معتبر نیست. بازگرداندن +`208` احتمالاً چیزی نیست که یک کاربر بخواهد اگر درخواست اولین حرف این رشته را داشته باشد؛ +با این حال، این تنها داده‌ای است که Rust در اندیس (index)بایت ۰ دارد. کاربران به طور کلی نمی‌خواهند +مقدار بایت بازگردانده شود، حتی اگر رشته فقط حروف لاتین داشته باشد: اگر `&"hi"[0]` یک کد معتبر +بود که مقدار بایت را بازمی‌گرداند، مقدار `104` و نه `h` را بازمی‌گرداند. -The answer, then, is that to avoid returning an unexpected value and causing -bugs that might not be discovered immediately, Rust doesn’t compile this code -at all and prevents misunderstandings early in the development process. +پاسخ این است که برای جلوگیری از بازگرداندن یک مقدار غیرمنتظره و ایجاد باگ‌هایی که ممکن است فوراً +کشف نشوند، Rust این کد را اصلاً کامپایل نمی‌کند و از سوءتفاهم‌ها در اوایل فرآیند توسعه جلوگیری می‌کند. -#### Bytes and Scalar Values and Grapheme Clusters! Oh My! +#### بایت‌ها، مقادیر اسکالر و خوشه‌های گرافیمی! اوه خدای من! -Another point about UTF-8 is that there are actually three relevant ways to -look at strings from Rust’s perspective: as bytes, scalar values, and grapheme -clusters (the closest thing to what we would call _letters_). +نکته دیگری درباره UTF-8 این است که در واقع سه روش مرتبط برای مشاهده رشته‌ها از دیدگاه Rust وجود دارد: +به صورت بایت، مقادیر اسکالر، و خوشه‌های گرافیمی (نزدیک‌ترین چیز به چیزی که ما _حروف_ می‌نامیم). -If we look at the Hindi word “नमस्ते” written in the Devanagari script, it is -stored as a vector of `u8` values that looks like this: +اگر به کلمه هندی "नमस्ते" نوشته شده در اسکریپت Devanagari نگاه کنیم، این کلمه به صورت یک بردار +از مقادیر `u8` ذخیره می‌شود که به شکل زیر است: ```text [224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164, 224, 165, 135] ``` -That’s 18 bytes and is how computers ultimately store this data. If we look at -them as Unicode scalar values, which are what Rust’s `char` type is, those -bytes look like this: +این ۱۸ بایت است و این همان چیزی است که کامپیوترها در نهایت این داده را ذخیره می‌کنند. +اگر به آن‌ها به عنوان مقادیر اسکالر Unicode نگاه کنیم، که همان نوع `char` در Rust است، این بایت‌ها +به این صورت به نظر می‌رسند: ```text ['न', 'म', 'स', '्', 'त', 'े'] ``` -There are six `char` values here, but the fourth and sixth are not letters: -they’re diacritics that don’t make sense on their own. Finally, if we look at -them as grapheme clusters, we’d get what a person would call the four letters -that make up the Hindi word: +اینجا شش مقدار `char` وجود دارد، اما مقدار چهارم و ششم حروف نیستند: این‌ها دیاکریتیک‌هایی هستند که +به تنهایی معنایی ندارند. در نهایت، اگر به آن‌ها به عنوان خوشه‌های گرافیمی نگاه کنیم، همان چیزی +که یک فرد به عنوان حروف کلمه هندی تشخیص می‌دهد، اینطور خواهد بود: ```text ["न", "म", "स्", "ते"] ``` -Rust provides different ways of interpreting the raw string data that computers -store so that each program can choose the interpretation it needs, no matter -what human language the data is in. +Rust روش‌های مختلفی برای تفسیر داده خام رشته ارائه می‌دهد که کامپیوترها ذخیره می‌کنند، بنابراین هر +برنامه می‌تواند تفسیری را که نیاز دارد انتخاب کند، صرف نظر از اینکه داده به چه زبان انسانی است. + +یکی دیگر از دلایل اینکه Rust به ما اجازه نمی‌دهد در یک `String` اندیس‌گذاری کنیم تا یک کاراکتر را +دریافت کنیم این است که عملیات اندیس‌گذاری باید همیشه در زمان ثابت (O(1)) انجام شود. اما امکان +تضمین این عملکرد با یک `String` وجود ندارد، زیرا Rust باید محتویات را از ابتدا تا اندیس (index)مرور کند تا +تعیین کند که چند کاراکتر معتبر وجود دارد. -A final reason Rust doesn’t allow us to index into a `String` to get a -character is that indexing operations are expected to always take constant time -(O(1)). But it isn’t possible to guarantee that performance with a `String`, -because Rust would have to walk through the contents from the beginning to the -index to determine how many valid characters there were. -### Slicing Strings +### برش رشته‌ها -Indexing into a string is often a bad idea because it’s not clear what the -return type of the string-indexing operation should be: a byte value, a -character, a grapheme cluster, or a string slice. If you really need to use -indices to create string slices, therefore, Rust asks you to be more specific. +اندیس‌گذاری در یک رشته اغلب ایده خوبی نیست زیرا مشخص نیست که نوع بازگشتی عملیات اندیس‌گذاری +رشته چه باید باشد: یک مقدار بایت، یک کاراکتر، یک خوشه گرافیمی، یا یک قطعه رشته. بنابراین، +اگر واقعاً نیاز به استفاده از اندیس‌ها برای ایجاد قطعه‌های رشته دارید، Rust از شما می‌خواهد +بیشتر مشخص کنید. -Rather than indexing using `[]` with a single number, you can use `[]` with a -range to create a string slice containing particular bytes: +به جای اندیس‌گذاری با استفاده از `[]` و یک عدد، می‌توانید از `[]` با یک بازه استفاده کنید +تا یک قطعه رشته که شامل بایت‌های خاصی است ایجاد کنید: ```rust let hello = "Здравствуйте"; @@ -342,27 +318,26 @@ let hello = "Здравствуйте"; let s = &hello[0..4]; ``` -Here, `s` will be a `&str` that contains the first four bytes of the string. -Earlier, we mentioned that each of these characters was two bytes, which means -`s` will be `Зд`. +اینجا، `s` یک `&str` خواهد بود که شامل چهار بایت اول رشته است. پیش‌تر اشاره کردیم که هر +یک از این کاراکترها دو بایت طول دارند، که به این معنی است که مقدار `s` برابر با `Зд` خواهد بود. -If we were to try to slice only part of a character’s bytes with something like -`&hello[0..1]`, Rust would panic at runtime in the same way as if an invalid -index were accessed in a vector: +اگر سعی کنیم فقط بخشی از بایت‌های یک کاراکتر را با چیزی مثل `&hello[0..1]` برش دهیم، +Rust در زمان اجرا دچار خطا می‌شود، به همان شکلی که اگر یک اندیس (index)نامعتبر در یک بردار +دسترسی داده شود: ```console {{#include ../listings/ch08-common-collections/output-only-01-not-char-boundary/output.txt}} ``` -You should use caution when creating string slices with ranges, because doing -so can crash your program. +هنگام ایجاد قطعه‌های رشته با بازه‌ها باید احتیاط کنید، زیرا این کار ممکن است باعث خرابی +برنامه شما شود. -### Methods for Iterating Over Strings +### متدهایی برای پیمایش در رشته‌ها -The best way to operate on pieces of strings is to be explicit about whether -you want characters or bytes. For individual Unicode scalar values, use the -`chars` method. Calling `chars` on “Зд” separates out and returns two values of -type `char`, and you can iterate over the result to access each element: +بهترین راه برای کار با بخش‌هایی از رشته‌ها این است که به وضوح مشخص کنید که آیا می‌خواهید +روی کاراکترها یا بایت‌ها کار کنید. برای مقادیر اسکالر Unicode منفرد، از متد `chars` استفاده کنید. +فراخوانی `chars` روی `"Зд"` دو مقدار از نوع `char` را جدا کرده و بازمی‌گرداند، و می‌توانید +با استفاده از نتیجه پیمایش کنید تا به هر عنصر دسترسی پیدا کنید: ```rust for c in "Зд".chars() { @@ -370,15 +345,14 @@ for c in "Зд".chars() { } ``` -This code will print the following: +این کد خروجی زیر را چاپ خواهد کرد: ```text З д ``` -Alternatively, the `bytes` method returns each raw byte, which might be -appropriate for your domain: +به صورت جایگزین، متد `bytes` هر بایت خام را بازمی‌گرداند که ممکن است برای حوزه کاری شما مناسب باشد: ```rust for b in "Зд".bytes() { @@ -386,7 +360,7 @@ for b in "Зд".bytes() { } ``` -This code will print the four bytes that make up this string: +این کد چهار بایتی که این رشته را تشکیل می‌دهند چاپ خواهد کرد: ```text 208 @@ -395,29 +369,24 @@ This code will print the four bytes that make up this string: 180 ``` -But be sure to remember that valid Unicode scalar values may be made up of more -than one byte. +اما حتماً به یاد داشته باشید که مقادیر اسکالر Unicode معتبر ممکن است از بیش از یک بایت تشکیل شده باشند. -Getting grapheme clusters from strings, as with the Devanagari script, is -complex, so this functionality is not provided by the standard library. Crates -are available on [crates.io](https://crates.io/) if this is the -functionality you need. +دریافت خوشه‌های گرافیمی از رشته‌ها، همانند اسکریپت Devanagari، پیچیده است، بنابراین این قابلیت +توسط کتابخانه استاندارد ارائه نمی‌شود. اگر به این قابلیت نیاز دارید، کرایت‌هایی در +[crates.io](https://crates.io/) موجود هستند. -### Strings Are Not So Simple +### رشته‌ها اینقدر ساده نیستند -To summarize, strings are complicated. Different programming languages make -different choices about how to present this complexity to the programmer. Rust -has chosen to make the correct handling of `String` data the default behavior -for all Rust programs, which means programmers have to put more thought into -handling UTF-8 data up front. This trade-off exposes more of the complexity of -strings than is apparent in other programming languages, but it prevents you -from having to handle errors involving non-ASCII characters later in your -development life cycle. +به طور خلاصه، رشته‌ها پیچیده هستند. زبان‌های برنامه‌نویسی مختلف انتخاب‌های متفاوتی درباره نحوه +نمایش این پیچیدگی به برنامه‌نویس می‌کنند. Rust انتخاب کرده است که مدیریت صحیح داده‌های +`String` رفتار پیش‌فرض برای تمام برنامه‌های Rust باشد، که به این معنی است که برنامه‌نویسان +باید در ابتدا بیشتر درباره مدیریت داده‌های UTF-8 فکر کنند. این معامله پیچیدگی بیشتری از رشته‌ها +را نسبت به سایر زبان‌های برنامه‌نویسی نشان می‌دهد، اما از مواجهه با خطاهای مربوط به کاراکترهای +غیر-ASCII در مراحل بعدی چرخه توسعه جلوگیری می‌کند. -The good news is that the standard library offers a lot of functionality built -off the `String` and `&str` types to help handle these complex situations -correctly. Be sure to check out the documentation for useful methods like -`contains` for searching in a string and `replace` for substituting parts of a -string with another string. +خبر خوب این است که کتابخانه استاندارد عملکردهای زیادی را بر اساس انواع `String` و `&str` +برای کمک به مدیریت صحیح این شرایط پیچیده ارائه می‌دهد. حتماً مستندات را برای متدهای مفیدی مانند +`contains` برای جستجو در یک رشته و `replace` برای جایگزینی بخش‌هایی از یک رشته با رشته‌ای دیگر +بررسی کنید. -Let’s switch to something a bit less complex: hash maps! +بیایید به چیزی کمی کمتر پیچیده برویم: هش مپ‌ها! diff --git a/src/ch08-03-hash-maps.md b/src/ch08-03-hash-maps.md index 4d185c84c9..e9ae211859 100644 --- a/src/ch08-03-hash-maps.md +++ b/src/ch08-03-hash-maps.md @@ -1,30 +1,27 @@ -## Storing Keys with Associated Values in Hash Maps +## ذخیره کلیدها با مقادیر مرتبط در هش مپ‌ها -The last of our common collections is the _hash map_. The type `HashMap` -stores a mapping of keys of type `K` to values of type `V` using a _hashing -function_, which determines how it places these keys and values into memory. -Many programming languages support this kind of data structure, but they often -use a different name, such as _hash_, _map_, _object_, _hash table_, -_dictionary_, or _associative array_, just to name a few. +آخرین مورد از مجموعه‌های رایج ما، _هش مپ_ است. نوع `HashMap` یک نگاشت از کلیدهایی +با نوع `K` به مقادیری با نوع `V` را با استفاده از یک _تابع هش_ ذخیره می‌کند، که تعیین می‌کند +چگونه این کلیدها و مقادیر در حافظه قرار بگیرند. بسیاری از زبان‌های برنامه‌نویسی از این نوع +ساختار داده پشتیبانی می‌کنند، اما اغلب از نام‌های متفاوتی مانند _هش_، _مپ_، _شیء_، +_جدول هش_، _دایرکتوری_، یا _آرایه ارتباطی_ برای اشاره به آن استفاده می‌کنند. -Hash maps are useful when you want to look up data not by using an index, as -you can with vectors, but by using a key that can be of any type. For example, -in a game, you could keep track of each team’s score in a hash map in which -each key is a team’s name and the values are each team’s score. Given a team -name, you can retrieve its score. +هش مپ‌ها زمانی مفید هستند که بخواهید داده‌ها را نه با استفاده از یک اندیس، مانند بردارها، +بلکه با استفاده از یک کلید که می‌تواند هر نوعی باشد، جستجو کنید. برای مثال، در یک بازی، +می‌توانید امتیاز هر تیم را در یک هش مپ ذخیره کنید که هر کلید نام یک تیم و هر مقدار +امتیاز آن تیم باشد. با داشتن نام یک تیم، می‌توانید امتیاز آن را بازیابی کنید. -We’ll go over the basic API of hash maps in this section, but many more goodies -are hiding in the functions defined on `HashMap` by the standard library. -As always, check the standard library documentation for more information. +در این بخش، به API اصلی هش مپ‌ها می‌پردازیم، اما امکانات بیشتری در توابع تعریف شده +روی `HashMap` در کتابخانه استاندارد وجود دارد. مانند همیشه، مستندات کتابخانه +استاندارد را برای اطلاعات بیشتر بررسی کنید. -### Creating a New Hash Map +### ایجاد یک هش مپ جدید -One way to create an empty hash map is to use `new` and to add elements with -`insert`. In Listing 8-20, we’re keeping track of the scores of two teams whose -names are _Blue_ and _Yellow_. The Blue team starts with 10 points, and the -Yellow team starts with 50. +یکی از راه‌های ایجاد یک هش مپ خالی استفاده از `new` و افزودن عناصر با `insert` است. +در لیست ۸-۲۰، ما امتیازات دو تیم به نام‌های _Blue_ و _Yellow_ را پیگیری می‌کنیم. تیم +آبی با ۱۰ امتیاز و تیم زرد با ۵۰ امتیاز شروع می‌کنند. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-20/src/main.rs:here}} @@ -32,23 +29,21 @@ Yellow team starts with 50. -Note that we need to first `use` the `HashMap` from the collections portion of -the standard library. Of our three common collections, this one is the least -often used, so it’s not included in the features brought into scope -automatically in the prelude. Hash maps also have less support from the -standard library; there’s no built-in macro to construct them, for example. +توجه داشته باشید که ابتدا باید `HashMap` را از بخش مجموعه‌های کتابخانه استاندارد `use` +کنیم. از میان سه مجموعه رایج ما، این یکی کمتر مورد استفاده قرار می‌گیرد، بنابراین به طور +پیش‌فرض در محدوده وارد نمی‌شود. همچنین، هش مپ‌ها از حمایت کمتری از کتابخانه استاندارد +برخوردارند؛ برای مثال، هیچ ماکروی داخلی برای ساخت آن‌ها وجود ندارد. -Just like vectors, hash maps store their data on the heap. This `HashMap` has -keys of type `String` and values of type `i32`. Like vectors, hash maps are -homogeneous: all of the keys must have the same type, and all of the values -must have the same type. +همانند بردارها، هش مپ‌ها داده‌های خود را روی heap ذخیره می‌کنند. این `HashMap` دارای +کلیدهایی از نوع `String` و مقادیری از نوع `i32` است. مانند بردارها، هش مپ‌ها همگن هستند: +تمام کلیدها باید از یک نوع باشند و تمام مقادیر نیز باید از یک نوع باشند. -### Accessing Values in a Hash Map +### دسترسی به مقادیر در یک هش مپ -We can get a value out of the hash map by providing its key to the `get` -method, as shown in Listing 8-21. +می‌توانیم یک مقدار را با ارائه کلید آن به متد `get` از هش مپ دریافت کنیم، همانطور که +در لیست ۸-۲۱ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-21/src/main.rs:here}} @@ -56,34 +51,33 @@ method, as shown in Listing 8-21. -Here, `score` will have the value that’s associated with the Blue team, and the -result will be `10`. The `get` method returns an `Option<&V>`; if there’s no -value for that key in the hash map, `get` will return `None`. This program -handles the `Option` by calling `copied` to get an `Option` rather than an -`Option<&i32>`, then `unwrap_or` to set `score` to zero if `scores` doesn’t -have an entry for the key. +اینجا، مقدار `score` برابر با مقداری خواهد بود که به تیم Blue مرتبط است، و نتیجه `10` خواهد بود. +متد `get` یک `Option<&V>` را بازمی‌گرداند؛ اگر هیچ مقداری برای آن کلید در هش مپ وجود نداشته +باشد، `get` مقدار `None` را بازمی‌گرداند. این برنامه مقدار `Option` را با فراخوانی `copied` +برای دریافت یک `Option` به جای `Option<&i32>` مدیریت می‌کند، سپس با استفاده از `unwrap_or` +مقدار `score` را به صفر تنظیم می‌کند اگر `scores` یک ورودی برای کلید نداشته باشد. -We can iterate over each key-value pair in a hash map in a similar manner as we -do with vectors, using a `for` loop: +می‌توانیم روی هر جفت کلید-مقدار در یک `hash map` به‌روشی مشابه با `vector`ها پیمایش کنیم، +با استفاده از یک حلقه‌ی `for`: ```rust {{#rustdoc_include ../listings/ch08-common-collections/no-listing-03-iterate-over-hashmap/src/main.rs:here}} ``` -This code will print each pair in an arbitrary order: +این کد هر جفت را به ترتیب دلخواه چاپ خواهد کرد: ```text Yellow: 50 Blue: 10 ``` -### Hash Maps and Ownership +### هش مپ‌ها و مالکیت -For types that implement the `Copy` trait, like `i32`, the values are copied -into the hash map. For owned values like `String`, the values will be moved and -the hash map will be the owner of those values, as demonstrated in Listing 8-22. +برای انواعی که ویژگی `Copy` را پیاده‌سازی می‌کنند، مانند `i32`، مقادیر درون هش مپ کپی می‌شوند. +برای مقادیر مالک مانند `String`، مقادیر منتقل شده و هش مپ مالک آن‌ها خواهد شد، همانطور که در +لیست ۸-۲۲ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-22/src/main.rs:here}} @@ -91,38 +85,35 @@ the hash map will be the owner of those values, as demonstrated in Listing 8-22. -We aren’t able to use the variables `field_name` and `field_value` after -they’ve been moved into the hash map with the call to `insert`. +پس از انتقال متغیرهای `field_name` و `field_value` به هش مپ با فراخوانی `insert`، دیگر نمی‌توانیم +از آن‌ها استفاده کنیم. -If we insert references to values into the hash map, the values won’t be moved -into the hash map. The values that the references point to must be valid for at -least as long as the hash map is valid. We’ll talk more about these issues in -[“Validating References with -Lifetimes”][validating-references-with-lifetimes] in Chapter 10. +اگر رفرنس‌هایی به مقادیر را درون `hash map` قرار دهیم، آن مقادیر به درون `hash map` منتقل نخواهند شد (moved نمی‌شوند). +مقدارهایی که این رفرنس‌ها به آن‌ها اشاره می‌کنند، باید حداقل تا زمانی معتبر باشند که `hash map` معتبر است. +در فصل ۱۰، در بخش [“اعتبارسنجی رفرنس‌ها با استفاده از lifetime”][validating-references-with-lifetimes]، +بیشتر درباره‌ی این مسائل صحبت خواهیم کرد. -### Updating a Hash Map +### به‌روزرسانی یک هش مپ -Although the number of key and value pairs is growable, each unique key can -only have one value associated with it at a time (but not vice versa: for -example, both the Blue team and the Yellow team could have the value `10` -stored in the `scores` hash map). +اگرچه تعداد جفت‌های کلید و مقدار قابل افزایش است، هر کلید یکتا فقط می‌تواند یک مقدار +مرتبط داشته باشد (اما نه بالعکس: برای مثال، هر دو تیم Blue و Yellow می‌توانند مقدار `10` +را در هش مپ `scores` ذخیره کنند). -When you want to change the data in a hash map, you have to decide how to -handle the case when a key already has a value assigned. You could replace the -old value with the new value, completely disregarding the old value. You could -keep the old value and ignore the new value, only adding the new value if the -key _doesn’t_ already have a value. Or you could combine the old value and the -new value. Let’s look at how to do each of these! +وقتی می‌خواهید داده‌ها را در یک هش مپ تغییر دهید، باید تصمیم بگیرید چگونه با حالتی که +یک کلید قبلاً دارای مقدار است برخورد کنید. می‌توانید مقدار قدیمی را با مقدار جدید جایگزین کنید +و مقدار قدیمی را کاملاً نادیده بگیرید. می‌توانید مقدار قدیمی را نگه دارید و مقدار جدید را نادیده بگیرید، +فقط مقدار جدید را اضافه کنید اگر کلید _ندارد_ قبلاً یک مقدار. یا می‌توانید مقدار قدیمی و مقدار جدید +را با هم ترکیب کنید. بیایید ببینیم چگونه هر یک از این کارها را انجام دهیم! -#### Overwriting a Value +#### بازنویسی یک مقدار -If we insert a key and a value into a hash map and then insert that same key -with a different value, the value associated with that key will be replaced. -Even though the code in Listing 8-23 calls `insert` twice, the hash map will -only contain one key-value pair because we’re inserting the value for the Blue -team’s key both times. +اگر یک کلید و مقدار را درون یک `hash map` قرار دهیم و سپس همان کلید را با یک مقدار متفاوت دوباره وارد کنیم، +مقدار مرتبط با آن کلید جایگزین خواهد شد. +حتی با این‌که کد در لیستینگ 8-23 دوبار تابع `insert` را فراخوانی می‌کند، +`hash map` تنها شامل یک جفت کلید-مقدار خواهد بود، +زیرا هر دو بار مقدار مربوط به کلید تیم Blue را وارد می‌کنیم. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-23/src/main.rs:here}} @@ -130,28 +121,23 @@ team’s key both times. -This code will print `{"Blue": 25}`. The original value of `10` has been -overwritten. - - +این کد مقدار `{"Blue": 25}` را چاپ خواهد کرد. مقدار اصلی `10` بازنویسی شده است. -#### Adding a Key and Value Only If a Key Isn’t Present +#### اضافه کردن یک کلید و مقدار فقط اگر کلید وجود ندارد -It’s common to check whether a particular key already exists in the hash map -with a value and then to take the following actions: if the key does exist in -the hash map, the existing value should remain the way it is; if the key -doesn’t exist, insert it and a value for it. +بررسی اینکه آیا یک کلید خاص در هش مپ دارای مقدار است یا خیر و سپس انجام اقدامات زیر رایج است: +اگر کلید در هش مپ وجود دارد، مقدار موجود باید همانطور که هست باقی بماند؛ اگر کلید وجود ندارد، +آن را به همراه یک مقدار وارد کنید. -Hash maps have a special API for this called `entry` that takes the key you -want to check as a parameter. The return value of the `entry` method is an enum -called `Entry` that represents a value that might or might not exist. Let’s say -we want to check whether the key for the Yellow team has a value associated -with it. If it doesn’t, we want to insert the value `50`, and the same for the -Blue team. Using the `entry` API, the code looks like Listing 8-24. +هش مپ‌ها یک API خاص برای این کار دارند که به نام `entry` شناخته می‌شود و کلیدی که می‌خواهید بررسی کنید +را به عنوان پارامتر می‌گیرد. مقدار بازگشتی متد `entry` یک enum به نام `Entry` است که نشان‌دهنده مقداری +است که ممکن است وجود داشته باشد یا نداشته باشد. فرض کنید می‌خواهیم بررسی کنیم که آیا کلید تیم Yellow +دارای مقدار مرتبط است یا خیر. اگر ندارد، می‌خواهیم مقدار `50` را وارد کنیم، و همینطور برای تیم Blue. +با استفاده از API `entry`، کد به شکل لیست ۸-۲۴ خواهد بود. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-24/src/main.rs:here}} @@ -159,28 +145,25 @@ Blue team. Using the `entry` API, the code looks like Listing 8-24. -The `or_insert` method on `Entry` is defined to return a mutable reference to -the value for the corresponding `Entry` key if that key exists, and if not, it -inserts the parameter as the new value for this key and returns a mutable -reference to the new value. This technique is much cleaner than writing the -logic ourselves and, in addition, plays more nicely with the borrow checker. +متد `or_insert` روی `Entry` به گونه‌ای تعریف شده است که یک مرجع قابل تغییر به مقدار مرتبط با کلید +`Entry` برمی‌گرداند اگر آن کلید وجود داشته باشد، و اگر نه، پارامتر را به عنوان مقدار جدید برای +این کلید وارد کرده و یک مرجع قابل تغییر به مقدار جدید بازمی‌گرداند. این تکنیک بسیار تمیزتر از نوشتن +منطق به صورت دستی است و علاوه بر این، با بررسی‌کننده قرض بهتر کار می‌کند. + +اجرای کد در لیست ۸-۲۴ مقدار `{"Yellow": 50, "Blue": 10}` را چاپ خواهد کرد. اولین فراخوانی به `entry` +کلید تیم Yellow را با مقدار `50` وارد می‌کند زیرا تیم Yellow قبلاً مقداری ندارد. دومین فراخوانی +به `entry` هش مپ را تغییر نمی‌دهد زیرا تیم Blue قبلاً مقدار `10` را دارد. -Running the code in Listing 8-24 will print `{"Yellow": 50, "Blue": 10}`. The -first call to `entry` will insert the key for the Yellow team with the value -`50` because the Yellow team doesn’t have a value already. The second call to -`entry` will not change the hash map because the Blue team already has the -value `10`. -#### Updating a Value Based on the Old Value +#### به‌روزرسانی یک مقدار بر اساس مقدار قدیمی -Another common use case for hash maps is to look up a key’s value and then -update it based on the old value. For instance, Listing 8-25 shows code that -counts how many times each word appears in some text. We use a hash map with -the words as keys and increment the value to keep track of how many times we’ve -seen that word. If it’s the first time we’ve seen a word, we’ll first insert -the value `0`. +یکی دیگر از موارد استفاده رایج برای هش مپ‌ها این است که مقدار یک کلید را جستجو کرده و سپس بر اساس +مقدار قدیمی آن را به‌روزرسانی کنیم. برای مثال، لیست ۸-۲۵ کدی را نشان می‌دهد که تعداد دفعات ظاهر شدن +هر کلمه در یک متن را می‌شمارد. ما از یک هش مپ با کلمات به عنوان کلید و مقدار برای نگهداری تعداد دفعات +ظاهر شدن هر کلمه استفاده می‌کنیم. اگر این اولین بار باشد که یک کلمه را مشاهده می‌کنیم، ابتدا مقدار +`0` را وارد می‌کنیم. -+ ```rust {{#rustdoc_include ../listings/ch08-common-collections/listing-08-25/src/main.rs:here}} @@ -188,60 +171,57 @@ the value `0`. -This code will print `{"world": 2, "hello": 1, "wonderful": 1}`. You might see -the same key-value pairs printed in a different order: recall from [“Accessing -Values in a Hash Map”][access] that iterating over a hash map -happens in an arbitrary order. - -The `split_whitespace` method returns an iterator over subslices, separated by -whitespace, of the value in `text`. The `or_insert` method returns a mutable -reference (`&mut V`) to the value for the specified key. Here, we store that -mutable reference in the `count` variable, so in order to assign to that value, -we must first dereference `count` using the asterisk (`*`). The mutable -reference goes out of scope at the end of the `for` loop, so all of these -changes are safe and allowed by the borrowing rules. - -### Hashing Functions - -By default, `HashMap` uses a hashing function called _SipHash_ that can provide -resistance to denial-of-service (DoS) attacks involving hash -tables[^siphash]. This is not the fastest hashing algorithm -available, but the trade-off for better security that comes with the drop in -performance is worth it. If you profile your code and find that the default -hash function is too slow for your purposes, you can switch to another function -by specifying a different hasher. A _hasher_ is a type that implements the -`BuildHasher` trait. We’ll talk about traits and how to implement them in -[Chapter 10][traits]. You don’t necessarily have to implement -your own hasher from scratch; [crates.io](https://crates.io/) -has libraries shared by other Rust users that provide hashers implementing many -common hashing algorithms. +این کد مقدار `{"world": 2, "hello": 1, "wonderful": 1}` را چاپ خواهد کرد. +ممکن است همین جفت‌های کلید-مقدار را به ترتیب متفاوتی ببینید: +به یاد داشته باشید از بخش [“دسترسی به مقادیر در یک Hash Map”][access] +که پیمایش در یک `hash map` به‌صورت ترتیبی دلخواه (arbitrary order) انجام می‌شود. + +متد `split_whitespace` یک iterator بر روی زیررشته‌هایی که با فضای خالی جدا شده‌اند از مقدار موجود +در `text` بازمی‌گرداند. متد `or_insert` یک مرجع قابل تغییر (`&mut V`) به مقدار مرتبط با کلید مشخص +برمی‌گرداند. اگر آن کلید وجود داشته باشد، مقدار بازگشتی همان مقدار موجود است؛ و اگر نه، پارامتر +را به عنوان مقدار جدید برای این کلید وارد می‌کند و مرجع قابل تغییر به مقدار جدید را بازمی‌گرداند. +در اینجا، ما این مرجع قابل تغییر را در متغیر `count` ذخیره می‌کنیم، بنابراین برای تخصیص مقدار +به آن، باید ابتدا `count` را با استفاده از عملگر ستاره (`*`) dereference کنیم. مرجع قابل تغییر +در انتهای حلقه `for` از محدوده خارج می‌شود، بنابراین تمام این تغییرات ایمن هستند و قوانین +قرض‌گیری را نقض نمی‌کنند. + +### توابع هش + +به طور پیش‌فرض، `HashMap` از یک تابع هش به نام _SipHash_ استفاده می‌کند که مقاومت در برابر +حملات انکار سرویس (DoS) مربوط به جداول هش[^siphash] را فراهم می‌کند. این سریع‌ترین +الگوریتم هش موجود نیست، اما مبادله برای امنیت بهتر با کاهش عملکرد ارزشمند است. اگر کد خود را +پروفایل کنید و متوجه شوید که تابع هش پیش‌فرض برای اهداف شما بسیار کند است، می‌توانید با مشخص کردن +یک هش‌کننده دیگر آن را تغییر دهید. یک _هش‌کننده_ نوعی است که ویژگی `BuildHasher` را پیاده‌سازی +می‌کند. درباره ویژگی‌ها (traits) و نحوه پیاده‌سازی آن‌ها در [فصل ۱۰][traits] صحبت +خواهیم کرد. نیازی نیست حتماً هش‌کننده خود را از ابتدا پیاده‌سازی کنید؛ در +[crates.io](https://crates.io/) کتابخانه‌هایی موجود هستند که توسط کاربران Rust به +اشتراک گذاشته شده‌اند و هش‌کننده‌هایی با بسیاری از الگوریتم‌های هش رایج ارائه می‌دهند. [^siphash]: [https://en.wikipedia.org/wiki/SipHash](https://en.wikipedia.org/wiki/SipHash) -## Summary - -Vectors, strings, and hash maps will provide a large amount of functionality -necessary in programs when you need to store, access, and modify data. Here are -some exercises you should now be equipped to solve: - -1. Given a list of integers, use a vector and return the median (when sorted, - the value in the middle position) and mode (the value that occurs most - often; a hash map will be helpful here) of the list. -1. Convert strings to pig latin. The first consonant of each word is moved to - the end of the word and _ay_ is added, so _first_ becomes _irst-fay_. Words - that start with a vowel have _hay_ added to the end instead (_apple_ becomes - _apple-hay_). Keep in mind the details about UTF-8 encoding! -1. Using a hash map and vectors, create a text interface to allow a user to add - employee names to a department in a company; for example, “Add Sally to - Engineering” or “Add Amir to Sales.” Then let the user retrieve a list of all - people in a department or all people in the company by department, sorted - alphabetically. - -The standard library API documentation describes methods that vectors, strings, -and hash maps have that will be helpful for these exercises! - -We’re getting into more complex programs in which operations can fail, so it’s -a perfect time to discuss error handling. We’ll do that next! +## خلاصه + +بردارها، رشته‌ها، و هش مپ‌ها مقدار زیادی از قابلیت‌های مورد نیاز برای ذخیره، دسترسی، و +تغییر داده‌ها در برنامه‌ها را فراهم می‌کنند. در اینجا چند تمرین وجود دارد که اکنون باید +قادر به حل آن‌ها باشید: + +1. با داشتن یک لیست از اعداد صحیح، از یک بردار استفاده کرده و میانه (وقتی مرتب‌سازی شود، مقداری که + در موقعیت وسط قرار دارد) و مد (مقداری که بیشترین بار ظاهر می‌شود؛ یک هش مپ در اینجا مفید خواهد بود) + لیست را بازگردانید. +2. رشته‌ها را به زبان لاتین خوکی تبدیل کنید. اولین صامت هر کلمه به انتهای کلمه منتقل شده و _ay_ + به آن اضافه می‌شود، بنابراین _first_ به _irst-fay_ تبدیل می‌شود. کلماتی که با یک حرف صدادار + شروع می‌شوند، _hay_ به انتهای آن‌ها اضافه می‌شود (_apple_ به _apple-hay_ تبدیل می‌شود). جزئیات + مربوط به کدگذاری UTF-8 را در نظر داشته باشید! +3. با استفاده از یک هش مپ و بردارها، یک رابط متنی ایجاد کنید تا به کاربر امکان اضافه کردن نام کارمندان + به یک دپارتمان در شرکت را بدهد؛ برای مثال، "Add Sally to Engineering" یا "Add Amir to Sales". + سپس به کاربر اجازه دهید لیستی از تمام افراد در یک دپارتمان یا تمام افراد در شرکت بر اساس + دپارتمان، مرتب شده به صورت حروف الفبا، بازیابی کند. + +مستندات API کتابخانه استاندارد متدهایی را که بردارها، رشته‌ها، و هش مپ‌ها دارند و برای این تمرین‌ها +مفید خواهند بود توصیف می‌کنند! + +ما وارد برنامه‌های پیچیده‌تری شده‌ایم که در آن‌ها عملیات ممکن است با شکست مواجه شوند، بنابراین +زمان مناسبی است تا درباره مدیریت خطا صحبت کنیم. این کار را در ادامه انجام خواهیم داد! [validating-references-with-lifetimes]: ch10-03-lifetime-syntax.html#validating-references-with-lifetimes [access]: #accessing-values-in-a-hash-map diff --git a/src/ch09-00-error-handling.md b/src/ch09-00-error-handling.md index ad49b70a86..e29fd5c3bd 100644 --- a/src/ch09-00-error-handling.md +++ b/src/ch09-00-error-handling.md @@ -1,24 +1,22 @@ -# Error Handling +# مدیریت خطاها -Errors are a fact of life in software, so Rust has a number of features for -handling situations in which something goes wrong. In many cases, Rust requires -you to acknowledge the possibility of an error and take some action before your -code will compile. This requirement makes your program more robust by ensuring -that you’ll discover errors and handle them appropriately before deploying your -code to production! +خطاها بخشی اجتناب‌ناپذیر از زندگی در دنیای نرم‌افزار هستند، +و به همین دلیل، Rust ویژگی‌های متعددی برای مدیریت موقعیت‌هایی دارد که در آن‌ها مشکلی پیش می‌آید. +در بسیاری از موارد، Rust شما را ملزم می‌کند که امکان وقوع یک خطا را به رسمیت بشناسید +و پیش از آن‌که کد شما کامپایل شود، اقدامی انجام دهید. +این الزام باعث می‌شود برنامه‌ی شما مقاوم‌تر باشد، +زیرا تضمین می‌کند که خطاها را پیش از استقرار کد در محیط اجرایی (production) شناسایی کرده +و به‌درستی مدیریت کرده‌اید. -Rust groups errors into two major categories: _recoverable_ and _unrecoverable_ -errors. For a recoverable error, such as a _file not found_ error, we most -likely just want to report the problem to the user and retry the operation. -Unrecoverable errors are always symptoms of bugs, such as trying to access a -location beyond the end of an array, and so we want to immediately stop the -program. +Rust خطاها را به دو دسته اصلی تقسیم می‌کند: خطاهای _قابل بازیابی_ و خطاهای _غیرقابل بازیابی_. +برای یک خطای قابل بازیابی، مانند خطای _فایل یافت نشد_، احتمالاً می‌خواهیم مشکل را به کاربر +گزارش دهیم و عملیات را دوباره انجام دهیم. خطاهای غیرقابل بازیابی همیشه نشانه‌های باگ‌ها هستند، +مانند تلاش برای دسترسی به مکانی خارج از انتهای یک آرایه، بنابراین می‌خواهیم بلافاصله برنامه +را متوقف کنیم. -Most languages don’t distinguish between these two kinds of errors and handle -both in the same way, using mechanisms such as exceptions. Rust doesn’t have -exceptions. Instead, it has the type `Result` for recoverable errors and -the `panic!` macro that stops execution when the program encounters an -unrecoverable error. This chapter covers calling `panic!` first and then talks -about returning `Result` values. Additionally, we’ll explore -considerations when deciding whether to try to recover from an error or to stop -execution. +بیشتر زبان‌ها بین این دو نوع خطا تفاوت قائل نمی‌شوند و هر دو را به یک شکل مدیریت می‌کنند، +با استفاده از مکانیزم‌هایی مانند استثناها. Rust استثناها ندارد. در عوض، نوع `Result` برای +خطاهای قابل بازیابی و ماکروی `panic!` که اجرای برنامه را زمانی که با یک خطای غیرقابل بازیابی +روبرو می‌شود متوقف می‌کند، ارائه می‌دهد. این فصل ابتدا به فراخوانی `panic!` می‌پردازد و سپس +در مورد بازگرداندن مقادیر `Result` صحبت می‌کند. علاوه بر این، ملاحظاتی را هنگام تصمیم‌گیری +در مورد اینکه آیا سعی در بازیابی از یک خطا کنیم یا اجرای برنامه را متوقف کنیم، بررسی خواهیم کرد. diff --git a/src/ch09-01-unrecoverable-errors-with-panic.md b/src/ch09-01-unrecoverable-errors-with-panic.md index f00bcc9632..a0b7c342f4 100644 --- a/src/ch09-01-unrecoverable-errors-with-panic.md +++ b/src/ch09-01-unrecoverable-errors-with-panic.md @@ -1,35 +1,32 @@ -## Unrecoverable Errors with `panic!` - -Sometimes bad things happen in your code, and there’s nothing you can do about -it. In these cases, Rust has the `panic!` macro. There are two ways to cause a -panic in practice: by taking an action that causes our code to panic (such as -accessing an array past the end) or by explicitly calling the `panic!` macro. -In both cases, we cause a panic in our program. By default, these panics will -print a failure message, unwind, clean up the stack, and quit. Via an -environment variable, you can also have Rust display the call stack when a -panic occurs to make it easier to track down the source of the panic. - -> ### Unwinding the Stack or Aborting in Response to a Panic +## خطاهای غیرقابل بازیابی با `panic!` + +گاهی اوقات اتفاقات بدی در کد شما رخ می‌دهد و هیچ کاری نمی‌توانید در مورد آن انجام دهید. در این موارد، +Rust ماکروی `panic!` را ارائه می‌دهد. دو راه برای ایجاد یک خطا با `panic!` وجود دارد: با انجام عملی +که باعث ایجاد خطا می‌شود (مانند دسترسی به یک اندیس (index)خارج از محدوده در یک آرایه) یا با صراحت +فراخوانی ماکروی `panic!`. در هر دو حالت، ما یک خطا در برنامه خود ایجاد می‌کنیم. به طور پیش‌فرض، +این خطاها یک پیام خطا چاپ می‌کنند، استک را unwind می‌کنند، داده‌ها را پاکسازی می‌کنند و برنامه +را متوقف می‌کنند. با استفاده از یک متغیر محیطی، می‌توانید Rust را مجبور کنید هنگام وقوع یک +`panic`، استک فراخوانی را نمایش دهد تا ردیابی منبع خطا آسان‌تر شود. + +> ### Unwinding the Stack یا متوقف کردن در پاسخ به یک Panic > -> By default, when a panic occurs the program starts _unwinding_, which means -> Rust walks back up the stack and cleans up the data from each function it -> encounters. However, walking back and cleaning up is a lot of work. Rust, -> therefore, allows you to choose the alternative of immediately _aborting_, -> which ends the program without cleaning up. +> به طور پیش‌فرض، هنگامی که یک خطا رخ می‌دهد، برنامه شروع به _unwinding_ می‌کند، که به معنی این +> است که Rust استک را به سمت بالا پیمایش می‌کند و داده‌ها را از هر تابعی که با آن برخورد می‌کند +> پاکسازی می‌کند. با این حال، پیمایش به بالا و پاکسازی کار زیادی است. بنابراین، Rust به شما اجازه +> می‌دهد گزینه جایگزین _abort_ کردن فوری را انتخاب کنید، که برنامه را بدون پاکسازی متوقف می‌کند. > -> Memory that the program was using will then need to be cleaned up by the -> operating system. If in your project you need to make the resultant binary as -> small as possible, you can switch from unwinding to aborting upon a panic by -> adding `panic = 'abort'` to the appropriate `[profile]` sections in your -> _Cargo.toml_ file. For example, if you want to abort on panic in release mode, -> add this: +> حافظه‌ای که برنامه استفاده می‌کرد نیاز به پاکسازی توسط سیستم عامل خواهد داشت. اگر در پروژه +> خود نیاز دارید تا فایل باینری حاصل را تا حد ممکن کوچک کنید، می‌توانید از unwind به abort در +> زمان خطا تغییر دهید با اضافه کردن `panic = 'abort'` به بخش‌های مناسب `[profile]` در فایل +> _Cargo.toml_ خود. برای مثال، اگر می‌خواهید در حالت release در زمان وقوع خطا متوقف شوید، +> این مورد را اضافه کنید: > > ```toml > [profile.release] > panic = 'abort' > ``` -Let’s try calling `panic!` in a simple program: +بیایید فراخوانی `panic!` را در یک برنامه ساده امتحان کنیم: @@ -39,35 +36,32 @@ Let’s try calling `panic!` in a simple program: -When you run the program, you’ll see something like this: +وقتی برنامه را اجرا کنید، چیزی شبیه به این خواهید دید: ```console {{#include ../listings/ch09-error-handling/no-listing-01-panic/output.txt}} ``` -The call to `panic!` causes the error message contained in the last two lines. -The first line shows our panic message and the place in our source code where -the panic occurred: _src/main.rs:2:5_ indicates that it’s the second line, -fifth character of our _src/main.rs_ file. +فراخوانی `panic!` پیام خطای موجود در دو خط آخر را ایجاد می‌کند. خط اول پیام خطای `panic!` ما +و مکانی در کد منبع ما که این خطا رخ داده است را نشان می‌دهد: _src/main.rs:2:5_ نشان می‌دهد +که این خط دوم، پنجمین کاراکتر در فایل _src/main.rs_ ما است. -In this case, the line indicated is part of our code, and if we go to that -line, we see the `panic!` macro call. In other cases, the `panic!` call might -be in code that our code calls, and the filename and line number reported by -the error message will be someone else’s code where the `panic!` macro is -called, not the line of our code that eventually led to the `panic!` call. - - +در این مورد، خط نشان داده شده بخشی از کد ما است، و اگر به آن خط برویم، فراخوانی ماکروی +`panic!` را می‌بینیم. در موارد دیگر، فراخوانی `panic!` ممکن است در کدی باشد که کد ما آن را +فراخوانی می‌کند، و نام فایل و شماره خط گزارش شده توسط پیام خطا کدی از دیگران را نشان می‌دهد +که در آن ماکروی `panic!` فراخوانی شده است، نه خطی از کد ما که در نهایت منجر به فراخوانی +`panic!` شد. -We can use the backtrace of the functions the `panic!` call came from to figure -out the part of our code that is causing the problem. To understand how to use -a `panic!` backtrace, let’s look at another example and see what it’s like when -a `panic!` call comes from a library because of a bug in our code instead of -from our code calling the macro directly. Listing 9-1 has some code that -attempts to access an index in a vector beyond the range of valid indexes. +ما می‌توانیم از backtrace توابعی که فراخوانی `panic!` از آن‌ها آمده است استفاده کنیم تا بخش کد +ما که باعث مشکل شده است را پیدا کنیم. برای درک نحوه استفاده از backtrace یک `panic!`، بیایید +یک مثال دیگر ببینیم و مشاهده کنیم زمانی که یک فراخوانی `panic!` از یک کتابخانه به دلیل یک باگ +در کد ما رخ می‌دهد، به جای اینکه کد ما مستقیماً ماکرو را فراخوانی کند، چگونه است. لیست ۹-۱ +کدی دارد که تلاش می‌کند به یک اندیس (index)در یک بردار که خارج از محدوده اندیس‌های معتبر است +دسترسی پیدا کند. -+ ```rust,should_panic,panics {{#rustdoc_include ../listings/ch09-error-handling/listing-09-01/src/main.rs}} @@ -75,51 +69,21 @@ attempts to access an index in a vector beyond the range of valid indexes. -Here, we’re attempting to access the 100th element of our vector (which is at -index 99 because indexing starts at zero), but the vector has only three -elements. In this situation, Rust will panic. Using `[]` is supposed to return -an element, but if you pass an invalid index, there’s no element that Rust -could return here that would be correct. +در اینجا، ما سعی داریم به عنصر صدم بردار خود دسترسی پیدا کنیم (که در اندیس (index)۹۹ است زیرا اندیس‌گذاری از صفر شروع می‌شود)، اما بردار فقط سه عنصر دارد. در این وضعیت، Rust با یک خطا متوقف می‌شود. استفاده از `[]` قرار است یک عنصر را بازگرداند، اما اگر یک اندیس (index)نامعتبر را ارائه دهید، هیچ عنصری وجود ندارد که Rust بتواند به درستی بازگرداند. -In C, attempting to read beyond the end of a data structure is undefined -behavior. You might get whatever is at the location in memory that would -correspond to that element in the data structure, even though the memory -doesn’t belong to that structure. This is called a _buffer overread_ and can -lead to security vulnerabilities if an attacker is able to manipulate the index -in such a way as to read data they shouldn’t be allowed to that is stored after -the data structure. +در زبان C، تلاش برای خواندن فراتر از انتهای یک ساختار داده رفتاری نامشخص دارد. ممکن است هر چیزی که در مکان حافظه‌ای که با آن عنصر در ساختار داده مطابقت دارد باشد را دریافت کنید، حتی اگر آن حافظه متعلق به آن ساختار نباشد. این به عنوان _buffer overread_ شناخته می‌شود و می‌تواند به آسیب‌پذیری‌های امنیتی منجر شود اگر یک مهاجم بتواند اندیس (index)را به گونه‌ای دستکاری کند که داده‌هایی را بخواند که نباید به آن‌ها دسترسی داشته باشد و پس از ساختار داده ذخیره شده‌اند. -To protect your program from this sort of vulnerability, if you try to read an -element at an index that doesn’t exist, Rust will stop execution and refuse to -continue. Let’s try it and see: +برای محافظت از برنامه شما در برابر این نوع آسیب‌پذیری، اگر تلاش کنید یک عنصر را در یک اندیسی که وجود ندارد بخوانید، Rust اجرای برنامه را متوقف کرده و از ادامه دادن امتناع می‌کند. بیایید این موضوع را امتحان کنیم و ببینیم: ```console {{#include ../listings/ch09-error-handling/listing-09-01/output.txt}} ``` -This error points at line 4 of our _main.rs_ where we attempt to access index -`99` of the vector in `v`. - -The `note:` line tells us that we can set the `RUST_BACKTRACE` environment -variable to get a backtrace of exactly what happened to cause the error. A -_backtrace_ is a list of all the functions that have been called to get to this -point. Backtraces in Rust work as they do in other languages: the key to -reading the backtrace is to start from the top and read until you see files you -wrote. That’s the spot where the problem originated. The lines above that spot -are code that your code has called; the lines below are code that called your -code. These before-and-after lines might include core Rust code, standard -library code, or crates that you’re using. Let’s try getting a backtrace by -setting the `RUST_BACKTRACE` environment variable to any value except `0`. -Listing 9-2 shows output similar to what you’ll see. - - - -+این خطا به خط ۴ فایل _main.rs_ ما اشاره می‌کند، جایی که سعی داریم به اندیس (index)`99` بردار `v` دسترسی پیدا کنیم. + +خط `note:` به ما می‌گوید که می‌توانیم متغیر محیطی `RUST_BACKTRACE` را تنظیم کنیم تا یک backtrace دقیقاً از آنچه باعث خطا شده است دریافت کنیم. یک _backtrace_ لیستی از تمام توابعی است که تا این نقطه فراخوانی شده‌اند. backtraceها در Rust مانند زبان‌های دیگر کار می‌کنند: کلید خواندن backtrace این است که از بالا شروع کرده و تا زمانی که فایل‌هایی که شما نوشته‌اید را ببینید، بخوانید. این همان جایی است که مشکل از آنجا منشأ گرفته است. خطوط بالاتر از آن نقطه کدی است که کد شما فراخوانی کرده است؛ خطوط پایین‌تر کدی است که کد شما را فراخوانی کرده است. این خطوط قبل و بعد ممکن است شامل کد هسته Rust، کد کتابخانه استاندارد، یا جعبه(crate)هایی که استفاده می‌کنید باشند. بیایید با تنظیم متغیر محیطی `RUST_BACKTRACE` به هر مقداری به غیر از `0` یک backtrace دریافت کنیم. لیست ۹-۲ خروجی مشابه چیزی را که خواهید دید نشان می‌دهد. + + ```console $ RUST_BACKTRACE=1 cargo run @@ -147,24 +111,10 @@ note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose bac -That’s a lot of output! The exact output you see might be different depending -on your operating system and Rust version. In order to get backtraces with this -information, debug symbols must be enabled. Debug symbols are enabled by -default when using `cargo build` or `cargo run` without the `--release` flag, -as we have here. - -In the output in Listing 9-2, line 6 of the backtrace points to the line in our -project that’s causing the problem: line 4 of _src/main.rs_. If we don’t want -our program to panic, we should start our investigation at the location pointed -to by the first line mentioning a file we wrote. In Listing 9-1, where we -deliberately wrote code that would panic, the way to fix the panic is to not -request an element beyond the range of the vector indexes. When your code -panics in the future, you’ll need to figure out what action the code is taking -with what values to cause the panic and what the code should do instead. - -We’ll come back to `panic!` and when we should and should not use `panic!` to -handle error conditions in the [“To `panic!` or Not to -`panic!`”][to-panic-or-not-to-panic] section later in this -chapter. Next, we’ll look at how to recover from an error using `Result`. +این مقدار زیادی خروجی است! خروجی دقیق ممکن است بسته به سیستم عامل و نسخه Rust شما متفاوت باشد. برای دریافت backtraceها با این اطلاعات، باید نمادهای اشکال‌زدایی (debug symbols) فعال باشند. نمادهای اشکال‌زدایی به طور پیش‌فرض هنگام استفاده از `cargo build` یا `cargo run` بدون فلگ `--release` فعال هستند، همانطور که در اینجا انجام دادیم. + +در خروجی لیست ۹-۲، خط ۶ از backtrace به خطی در پروژه ما اشاره می‌کند که باعث مشکل شده است: خط ۴ فایل _src/main.rs_. اگر نمی‌خواهیم برنامه ما دچار خطا شود، باید بررسی خود را از مکانی که توسط اولین خطی که اشاره به فایلی که نوشته‌ایم دارد، آغاز کنیم. در لیست ۹-۱، جایی که به عمد کدی نوشته‌ایم که باعث خطا شود، راه حل رفع این خطا این است که درخواست یک عنصر فراتر از محدوده اندیس‌های بردار نکنیم. زمانی که کد شما در آینده دچار خطا می‌شود، باید بفهمید که کد با چه مقادیری چه عملی انجام می‌دهد که باعث خطا می‌شود و کد چه کاری باید انجام دهد. + +ما در بخش [“To `panic!` or Not to `panic!`”][to-panic-or-not-to-panic] که بعداً در این فصل آمده است، دوباره به موضوع `panic!` و زمانی که باید و نباید از `panic!` برای مدیریت شرایط خطا استفاده کنیم بازخواهیم گشت. اکنون، به بررسی نحوه بازیابی از یک خطا با استفاده از `Result` می‌پردازیم. [to-panic-or-not-to-panic]: ch09-03-to-panic-or-not-to-panic.html#to-panic-or-not-to-panic diff --git a/src/ch09-02-recoverable-errors-with-result.md b/src/ch09-02-recoverable-errors-with-result.md index bfb3c70e4f..69e96b2c53 100644 --- a/src/ch09-02-recoverable-errors-with-result.md +++ b/src/ch09-02-recoverable-errors-with-result.md @@ -1,14 +1,12 @@ -## Recoverable Errors with `Result` +## خطاهای قابل بازیابی با `Result` -Most errors aren’t serious enough to require the program to stop entirely. -Sometimes when a function fails it’s for a reason that you can easily interpret -and respond to. For example, if you try to open a file and that operation fails -because the file doesn’t exist, you might want to create the file instead of -terminating the process. +بیشتر خطاها به اندازه‌ای جدی نیستند که نیاز به توقف کامل برنامه داشته باشند. گاهی اوقات وقتی +یک تابع با شکست مواجه می‌شود، دلیلی وجود دارد که می‌توانید آن را به راحتی تفسیر کرده و به آن +پاسخ دهید. برای مثال، اگر بخواهید یک فایل را باز کنید و این عملیات به دلیل وجود نداشتن فایل شکست +بخورد، ممکن است بخواهید فایل را ایجاد کنید به جای اینکه فرآیند را متوقف کنید. -Recall from [“Handling Potential Failure with `Result`”][handle_failure] in Chapter 2 that the `Result` enum is defined as having two -variants, `Ok` and `Err`, as follows: +به یاد بیاورید از بخش [“Handling Potential Failure with `Result`”][handle_failure] +در فصل ۲ که `Result` به صورت یک enum تعریف شده که دو حالت دارد، `Ok` و `Err`، به صورت زیر: ```rust enum Result { @@ -17,19 +15,16 @@ enum Result { } ``` -The `T` and `E` are generic type parameters: we’ll discuss generics in more -detail in Chapter 10. What you need to know right now is that `T` represents -the type of the value that will be returned in a success case within the `Ok` -variant, and `E` represents the type of the error that will be returned in a -failure case within the `Err` variant. Because `Result` has these generic type -parameters, we can use the `Result` type and the functions defined on it in -many different situations where the success value and error value we want to -return may differ. +`T` و `E` پارامترهای نوع جنریک هستند: ما درباره جنریک‌ها به طور کامل‌تر در فصل ۱۰ صحبت خواهیم کرد. +چیزی که اکنون باید بدانید این است که `T` نمایانگر نوع مقداری است که در حالت موفقیت در داخل `Ok` +بازگردانده می‌شود، و `E` نمایانگر نوع خطایی است که در حالت شکست در داخل `Err` بازگردانده می‌شود. +زیرا `Result` این پارامترهای نوع جنریک را دارد، می‌توانیم نوع `Result` و توابع تعریف شده روی آن را +در بسیاری از شرایط مختلف که مقادیر موفقیت و خطا ممکن است متفاوت باشند، استفاده کنیم. -Let’s call a function that returns a `Result` value because the function could -fail. In Listing 9-3 we try to open a file. +بیایید تابعی را فراخوانی کنیم که یک مقدار `Result` را بازمی‌گرداند زیرا این تابع ممکن است با شکست +مواجه شود. در لیست ۹-۳ سعی می‌کنیم یک فایل را باز کنیم. -+ ```rust {{#rustdoc_include ../listings/ch09-error-handling/listing-09-03/src/main.rs}} @@ -37,29 +32,25 @@ fail. In Listing 9-3 we try to open a file. -The return type of `File::open` is a `Result`. The generic parameter `T` -has been filled in by the implementation of `File::open` with the type of the -success value, `std::fs::File`, which is a file handle. The type of `E` used in -the error value is `std::io::Error`. This return type means the call to -`File::open` might succeed and return a file handle that we can read from or -write to. The function call also might fail: for example, the file might not -exist, or we might not have permission to access the file. The `File::open` -function needs to have a way to tell us whether it succeeded or failed and at -the same time give us either the file handle or error information. This -information is exactly what the `Result` enum conveys. - -In the case where `File::open` succeeds, the value in the variable -`greeting_file_result` will be an instance of `Ok` that contains a file handle. -In the case where it fails, the value in `greeting_file_result` will be an -instance of `Err` that contains more information about the kind of error that -occurred. - -We need to add to the code in Listing 9-3 to take different actions depending -on the value `File::open` returns. Listing 9-4 shows one way to handle the -`Result` using a basic tool, the `match` expression that we discussed in -Chapter 6. - -+نوع بازگشتی `File::open` یک `Result` است. پارامتر نوع جنریک `T` توسط پیاده‌سازی +`File::open` با نوع مقدار موفقیت، یعنی `std::fs::File`، که یک فایل هندل است، مقداردهی +شده است. نوع `E` استفاده شده در مقدار خطا `std::io::Error` است. این نوع بازگشتی به این معنی +است که فراخوانی `File::open` ممکن است موفقیت‌آمیز باشد و یک فایل هندل بازگرداند که می‌توانیم از +آن برای خواندن یا نوشتن استفاده کنیم. همچنین ممکن است این فراخوانی با شکست مواجه شود: برای مثال، +فایل ممکن است وجود نداشته باشد یا ممکن است مجوز دسترسی به فایل را نداشته باشیم. تابع `File::open` +باید روشی داشته باشد تا به ما بگوید که آیا موفقیت‌آمیز بود یا شکست خورد و در عین حال فایل هندل یا +اطلاعات خطا را به ما بدهد. این اطلاعات دقیقاً همان چیزی است که enum `Result` منتقل می‌کند. + +در حالتی که `File::open` موفقیت‌آمیز باشد، مقدار در متغیر `greeting_file_result` یک نمونه از `Ok` +خواهد بود که یک فایل هندل را شامل می‌شود. در حالتی که با شکست مواجه شود، مقدار در +`greeting_file_result` یک نمونه از `Err` خواهد بود که اطلاعات بیشتری در مورد نوع خطایی که رخ +داده است را شامل می‌شود. + +باید به کد در لیست ۹-۳ اضافه کنیم تا اقدامات متفاوتی بسته به مقداری که `File::open` بازمی‌گرداند +انجام دهیم. لیست ۹-۴ یک روش برای مدیریت `Result` با استفاده از یک ابزار پایه، یعنی عبارت `match` +که در فصل ۶ مورد بحث قرار گرفت، نشان می‌دهد. + + ```rust,should_panic {{#rustdoc_include ../listings/ch09-error-handling/listing-09-04/src/main.rs}} @@ -67,40 +58,23 @@ Chapter 6. -Note that, like the `Option` enum, the `Result` enum and its variants have been -brought into scope by the prelude, so we don’t need to specify `Result::` -before the `Ok` and `Err` variants in the `match` arms. +توجه داشته باشید که مانند enum `Option`، enum `Result` و حالات آن به وسیله prelude به محدوده آورده شده‌اند، بنابراین نیازی نیست قبل از حالات `Ok` و `Err` در بازوهای `match` از `Result::` استفاده کنیم. -When the result is `Ok`, this code will return the inner `file` value out of -the `Ok` variant, and we then assign that file handle value to the variable -`greeting_file`. After the `match`, we can use the file handle for reading or -writing. +وقتی نتیجه `Ok` باشد، این کد مقدار داخلی `file` را از حالت `Ok` بازمی‌گرداند و سپس آن مقدار فایل هندل را به متغیر `greeting_file` اختصاص می‌دهیم. بعد از `match`، می‌توانیم از فایل هندل برای خواندن یا نوشتن استفاده کنیم. -The other arm of the `match` handles the case where we get an `Err` value from -`File::open`. In this example, we’ve chosen to call the `panic!` macro. If -there’s no file named _hello.txt_ in our current directory and we run this -code, we’ll see the following output from the `panic!` macro: +بازوی دیگر `match` حالت زمانی را مدیریت می‌کند که از `File::open` یک مقدار `Err` دریافت می‌کنیم. در این مثال، تصمیم گرفته‌ایم ماکروی `panic!` را فراخوانی کنیم. اگر فایل _hello.txt_ در دایرکتوری فعلی ما وجود نداشته باشد و این کد را اجرا کنیم، خروجی زیر را از ماکروی `panic!` خواهیم دید: ```console {{#include ../listings/ch09-error-handling/listing-09-04/output.txt}} ``` -As usual, this output tells us exactly what has gone wrong. +مثل همیشه، این خروجی دقیقاً به ما می‌گوید چه اشتباهی رخ داده است. -### Matching on Different Errors +### مطابقت بر اساس خطاهای مختلف -The code in Listing 9-4 will `panic!` no matter why `File::open` failed. -However, we want to take different actions for different failure reasons. If -`File::open` failed because the file doesn’t exist, we want to create the file -and return the handle to the new file. If `File::open` failed for any other -reason—for example, because we didn’t have permission to open the file—we still -want the code to `panic!` in the same way it did in Listing 9-4. For this, we -add an inner `match` expression, shown in Listing 9-5. +کد در لیست ۹-۴ در هر صورتی که `File::open` با شکست مواجه شود، ماکروی `panic!` را فراخوانی می‌کند. با این حال، ما می‌خواهیم اقدامات متفاوتی برای دلایل مختلف شکست انجام دهیم. اگر `File::open` به دلیل وجود نداشتن فایل شکست بخورد، می‌خواهیم فایل را ایجاد کنیم و هندل فایل جدید را بازگردانیم. اگر `File::open` به دلایل دیگری شکست بخورد—برای مثال، به دلیل نداشتن مجوز باز کردن فایل—همچنان می‌خواهیم کد مانند لیست ۹-۴ `panic!` کند. برای این کار، یک عبارت `match` داخلی اضافه می‌کنیم که در لیست ۹-۵ نشان داده شده است. -- - + ```rust,ignore {{#rustdoc_include ../listings/ch09-error-handling/listing-09-05/src/main.rs}} @@ -108,34 +82,19 @@ tests to fail lol --> -The type of the value that `File::open` returns inside the `Err` variant is -`io::Error`, which is a struct provided by the standard library. This struct -has a method `kind` that we can call to get an `io::ErrorKind` value. The enum -`io::ErrorKind` is provided by the standard library and has variants -representing the different kinds of errors that might result from an `io` -operation. The variant we want to use is `ErrorKind::NotFound`, which indicates -the file we’re trying to open doesn’t exist yet. So we match on -`greeting_file_result`, but we also have an inner match on `error.kind()`. - -The condition we want to check in the inner match is whether the value returned -by `error.kind()` is the `NotFound` variant of the `ErrorKind` enum. If it is, -we try to create the file with `File::create`. However, because `File::create` -could also fail, we need a second arm in the inner `match` expression. When the -file can’t be created, a different error message is printed. The second arm of -the outer `match` stays the same, so the program panics on any error besides -the missing file error. - -> #### Alternatives to Using `match` with `Result` -> -> That’s a lot of `match`! The `match` expression is very useful but also very -> much a primitive. In Chapter 13, you’ll learn about closures, which are used -> with many of the methods defined on `Result`. These methods can be more -> concise than using `match` when handling `Result` values in your code. +نوع مقداری که `File::open` درون حالت `Err` بازمی‌گرداند، `io::Error` است که یک ساختار داده ارائه شده توسط کتابخانه استاندارد است. این ساختار دارای متدی به نام `kind` است که می‌توانیم آن را برای دریافت مقدار `io::ErrorKind` فراخوانی کنیم. enum `io::ErrorKind` توسط کتابخانه استاندارد ارائه شده و شامل حالت‌هایی است که انواع مختلف خطاهای ممکن در یک عملیات `io` را نمایش می‌دهد. حالتی که می‌خواهیم از آن استفاده کنیم `ErrorKind::NotFound` است که نشان می‌دهد فایل مورد نظر برای باز کردن هنوز وجود ندارد. بنابراین، ما بر روی `greeting_file_result` مطابقت می‌دهیم، اما همچنین یک `match` داخلی بر روی `error.kind()` داریم. + +شرطی که می‌خواهیم در `match` داخلی بررسی کنیم این است که آیا مقدار بازگردانده شده توسط `error.kind()` همان حالت `NotFound` از enum `ErrorKind` است یا خیر. اگر چنین باشد، سعی می‌کنیم فایل را با `File::create` ایجاد کنیم. با این حال، از آنجایی که `File::create` نیز ممکن است شکست بخورد، به یک بازوی دوم در عبارت `match` داخلی نیاز داریم. هنگامی که فایل نمی‌تواند ایجاد شود، یک پیام خطای متفاوت چاپ می‌شود. بازوی دوم `match` بیرونی به همان شکل باقی می‌ماند، بنابراین برنامه برای هر خطایی به جز خطای وجود نداشتن فایل، با خطا متوقف می‌شود. + +> #### جایگزین‌هایی برای استفاده از `match` با `Result` > -> For example, here’s another way to write the same logic as shown in Listing -> 9-5, this time using closures and the `unwrap_or_else` method: +> استفاده از `match` زیاد است! عبارت `match` بسیار مفید است اما همچنان ابتدایی محسوب می‌شود. +> در فصل ۱۳، درباره closures یاد خواهید گرفت که در بسیاری از متدهایی که روی `Result` +> تعریف شده‌اند استفاده می‌شوند. این متدها می‌توانند هنگام مدیریت مقادیر `Result` در کد شما، +> مختصرتر از استفاده از `match` باشند. > -> +> برای مثال، در اینجا راه دیگری برای نوشتن همان منطق نشان داده شده در لیست ۹-۵ آورده شده است، +> این بار با استفاده از closures و متد `unwrap_or_else`: > > ```rust,ignore > use std::fs::File; @@ -154,21 +113,18 @@ the missing file error. > } > ``` > -> Although this code has the same behavior as Listing 9-5, it doesn’t contain -> any `match` expressions and is cleaner to read. Come back to this example -> after you’ve read Chapter 13, and look up the `unwrap_or_else` method in the -> standard library documentation. Many more of these methods can clean up huge -> nested `match` expressions when you’re dealing with errors. - -#### Shortcuts for Panic on Error: `unwrap` and `expect` - -Using `match` works well enough, but it can be a bit verbose and doesn’t always -communicate intent well. The `Result` type has many helper methods -defined on it to do various, more specific tasks. The `unwrap` method is a -shortcut method implemented just like the `match` expression we wrote in -Listing 9-4. If the `Result` value is the `Ok` variant, `unwrap` will return -the value inside the `Ok`. If the `Result` is the `Err` variant, `unwrap` will -call the `panic!` macro for us. Here is an example of `unwrap` in action: +> اگرچه این کد همان رفتار لیست ۹-۵ را دارد، اما شامل هیچ عبارت `match` نیست و خواندن آن تمیزتر است. +> بعد از خواندن فصل ۱۳، به این مثال بازگردید و متد `unwrap_or_else` را در مستندات کتابخانه استاندارد +> بررسی کنید. بسیاری از این متدها می‌توانند عبارت‌های `match` تو در تو را هنگام کار با خطاها ساده کنند. + +#### میان‌برهایی برای توقف برنامه در صورت خطا: `unwrap` و `expect` + +استفاده از `match` به اندازه کافی خوب کار می‌کند، اما ممکن است کمی طولانی باشد و همیشه به خوبی نیت +را منتقل نکند. نوع `Result` دارای بسیاری از متدهای کمکی است که برای انجام وظایف خاص‌تر تعریف +شده‌اند. متد `unwrap` یک روش میان‌بر است که دقیقاً مانند عبارت `match` که در لیست ۹-۴ نوشتیم، +پیاده‌سازی شده است. اگر مقدار `Result` در حالت `Ok` باشد، `unwrap` مقدار داخل `Ok` را بازمی‌گرداند. +اگر مقدار `Result` در حالت `Err` باشد، `unwrap` ماکروی `panic!` را برای ما فراخوانی می‌کند. در اینجا +یک مثال از استفاده از `unwrap` آورده شده است: @@ -178,24 +134,17 @@ call the `panic!` macro for us. Here is an example of `unwrap` in action: -If we run this code without a _hello.txt_ file, we’ll see an error message from -the `panic!` call that the `unwrap` method makes: - - +اگر این کد را بدون فایل _hello.txt_ اجرا کنیم، یک پیام خطا از فراخوانی `panic!` که متد `unwrap` انجام +می‌دهد خواهیم دید: ```text thread 'main' panicked at src/main.rs:4:49: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" } ``` -Similarly, the `expect` method lets us also choose the `panic!` error message. -Using `expect` instead of `unwrap` and providing good error messages can convey -your intent and make tracking down the source of a panic easier. The syntax of -`expect` looks like this: +به همین ترتیب، متد `expect` به ما اجازه می‌دهد پیام خطای ماکروی `panic!` را نیز انتخاب کنیم. استفاده +از `expect` به جای `unwrap` و ارائه پیام‌های خطای خوب می‌تواند نیت شما را بهتر منتقل کند و پیگیری منبع +یک خطا را آسان‌تر کند. سینتکس `expect` به این شکل است: @@ -205,45 +154,28 @@ your intent and make tracking down the source of a panic easier. The syntax of -We use `expect` in the same way as `unwrap`: to return the file handle or call -the `panic!` macro. The error message used by `expect` in its call to `panic!` -will be the parameter that we pass to `expect`, rather than the default -`panic!` message that `unwrap` uses. Here’s what it looks like: - - +ما از `expect` به همان شیوه‌ای استفاده می‌کنیم که از `unwrap` استفاده می‌کنیم: برای بازگرداندن فایل هندل یا فراخوانی ماکروی `panic!`. پیام خطایی که توسط `expect` در فراخوانی `panic!` استفاده می‌شود، پارامتری است که ما به `expect` می‌دهیم، به جای پیام پیش‌فرض `panic!` که توسط `unwrap` استفاده می‌شود. اینجا چیزی است که به نظر می‌رسد: ```text thread 'main' panicked at src/main.rs:5:10: hello.txt should be included in this project: Os { code: 2, kind: NotFound, message: "No such file or directory" } ``` -In production-quality code, most Rustaceans choose `expect` rather than -`unwrap` and give more context about why the operation is expected to always -succeed. That way, if your assumptions are ever proven wrong, you have more -information to use in debugging. - -### Propagating Errors +در کد با کیفیت تولید، بیشتر Rustaceanها `expect` را به جای `unwrap` انتخاب می‌کنند و اطلاعات بیشتری درباره اینکه چرا عملیات باید همیشه موفقیت‌آمیز باشد ارائه می‌دهند. به این ترتیب، اگر فرضیات شما هرگز اشتباه ثابت شوند، اطلاعات بیشتری برای استفاده در اشکال‌زدایی خواهید داشت. -When a function’s implementation calls something that might fail, instead of -handling the error within the function itself, you can return the error to the -calling code so that it can decide what to do. This is known as _propagating_ -the error and gives more control to the calling code, where there might be more -information or logic that dictates how the error should be handled than what -you have available in the context of your code. +### انتشار خطاها (Propagating Errors) -For example, Listing 9-6 shows a function that reads a username from a file. If -the file doesn’t exist or can’t be read, this function will return those errors -to the code that called the function. +وقتی پیاده‌سازی یک تابع چیزی را فراخوانی می‌کند که ممکن است شکست بخورد، +به‌جای آن‌که خطا را درون خود تابع مدیریت کند، می‌توانید آن خطا را به کدی که تابع را فراخوانی کرده برگردانید +تا آن کد تصمیم بگیرد که چه کاری باید انجام شود. +این روش به *انتقال (propagating)* خطا معروف است +و کنترل بیشتری را به کد فراخواننده می‌دهد، +جایی که ممکن است اطلاعات یا منطق بیشتری برای تصمیم‌گیری در مورد نحوه‌ی مدیریت خطا وجود داشته باشد +نسبت به آن‌چه در زمینه‌ی تابع فعلی در دسترس است. -+برای مثال، لیست ۹-۶ یک تابع را نشان می‌دهد که یک نام کاربری را از یک فایل می‌خواند. اگر فایل وجود نداشته باشد یا قابل خواندن نباشد، این تابع آن خطاها را به کدی که تابع را فراخوانی کرده بازمی‌گرداند. - + ```rust {{#include ../listings/ch09-error-handling/listing-09-06/src/main.rs:here}} @@ -251,68 +183,43 @@ don't want to include it for rustdoc testing purposes. --> -This function can be written in a much shorter way, but we’re going to start by -doing a lot of it manually in order to explore error handling; at the end, -we’ll show the shorter way. Let’s look at the return type of the function -first: `Result`. This means the function is returning a -value of the type `Result`, where the generic parameter `T` has been -filled in with the concrete type `String` and the generic type `E` has been -filled in with the concrete type `io::Error`. - -If this function succeeds without any problems, the code that calls this -function will receive an `Ok` value that holds a `String`—the `username` that -this function read from the file. If this function encounters any problems, the -calling code will receive an `Err` value that holds an instance of `io::Error` -that contains more information about what the problems were. We chose -`io::Error` as the return type of this function because that happens to be the -type of the error value returned from both of the operations we’re calling in -this function’s body that might fail: the `File::open` function and the -`read_to_string` method. - -The body of the function starts by calling the `File::open` function. Then we -handle the `Result` value with a `match` similar to the `match` in Listing 9-4. -If `File::open` succeeds, the file handle in the pattern variable `file` -becomes the value in the mutable variable `username_file` and the function -continues. In the `Err` case, instead of calling `panic!`, we use the `return` -keyword to return early out of the function entirely and pass the error value -from `File::open`, now in the pattern variable `e`, back to the calling code as -this function’s error value. - -So, if we have a file handle in `username_file`, the function then creates a -new `String` in variable `username` and calls the `read_to_string` method on -the file handle in `username_file` to read the contents of the file into -`username`. The `read_to_string` method also returns a `Result` because it -might fail, even though `File::open` succeeded. So we need another `match` to -handle that `Result`: if `read_to_string` succeeds, then our function has -succeeded, and we return the username from the file that’s now in `username` -wrapped in an `Ok`. If `read_to_string` fails, we return the error value in the -same way that we returned the error value in the `match` that handled the -return value of `File::open`. However, we don’t need to explicitly say -`return`, because this is the last expression in the function. - -The code that calls this code will then handle getting either an `Ok` value -that contains a username or an `Err` value that contains an `io::Error`. It’s -up to the calling code to decide what to do with those values. If the calling -code gets an `Err` value, it could call `panic!` and crash the program, use a -default username, or look up the username from somewhere other than a file, for -example. We don’t have enough information on what the calling code is actually -trying to do, so we propagate all the success or error information upward for -it to handle appropriately. - -This pattern of propagating errors is so common in Rust that Rust provides the -question mark operator `?` to make this easier. - -#### A Shortcut for Propagating Errors: The `?` Operator - -Listing 9-7 shows an implementation of `read_username_from_file` that has the -same functionality as in Listing 9-6, but this implementation uses the `?` -operator. - -- - +این تابع می‌تواند به روشی بسیار کوتاه‌تر نوشته شود، اما ما قرار است با انجام بسیاری از کارها به صورت دستی، مدیریت خطاها را بررسی کنیم. در انتها، راه کوتاه‌تر را نشان خواهیم داد. بیایید ابتدا به نوع بازگشتی تابع نگاه کنیم: `Result`. این به این معناست که تابع مقداری از نوع `Result` بازمی‌گرداند، جایی که پارامتر جنریک `T` با نوع مشخص `String` مقداردهی شده است و نوع جنریک `E` با نوع مشخص `io::Error`. + +اگر این تابع بدون هیچ مشکلی موفقیت‌آمیز باشد، کدی که این تابع را فراخوانی می‌کند یک مقدار `Ok` دریافت می‌کند که یک `String` را نگهداری می‌کند—نام کاربری‌ای که این تابع از فایل خوانده است. اگر این تابع با مشکلی مواجه شود، کدی که آن را فراخوانی کرده است یک مقدار `Err` دریافت می‌کند که یک نمونه از `io::Error` را نگهداری می‌کند که اطلاعات بیشتری درباره مشکلاتی که رخ داده‌اند شامل می‌شود. ما `io::Error` را به عنوان نوع بازگشتی این تابع انتخاب کردیم زیرا این همان نوعی است که مقدار خطا از هر دو عملیات فراخوانی شده در بدنه این تابع که ممکن است شکست بخورند بازمی‌گرداند: تابع `File::open` و متد `read_to_string`. + +بدنه تابع با فراخوانی تابع `File::open` شروع می‌شود. سپس مقدار `Result` را با یک `match` مشابه +آنچه در لیست ۹-۴ دیدیم مدیریت می‌کنیم. اگر `File::open` موفق شود، هندل فایل در متغیر الگو `file` +به مقدار در متغیر قابل تغییر `username_file` تبدیل می‌شود و تابع ادامه می‌یابد. در حالت `Err`، +به جای فراخوانی `panic!`، از کلیدواژه `return` استفاده می‌کنیم تا زودتر از تابع خارج شویم و مقدار +خطا از `File::open` که اکنون در متغیر الگو `e` قرار دارد را به کدی که تابع را فراخوانی کرده بازگردانیم. + +بنابراین، اگر یک هندل فایل در `username_file` داشته باشیم، تابع سپس یک `String` جدید در متغیر +`username` ایجاد کرده و متد `read_to_string` را روی هندل فایل در `username_file` فراخوانی می‌کند +تا محتوای فایل را در `username` بخواند. متد `read_to_string` نیز یک مقدار `Result` بازمی‌گرداند +زیرا ممکن است با شکست مواجه شود، حتی اگر `File::open` موفق بوده باشد. بنابراین، به یک `match` +دیگر برای مدیریت آن `Result` نیاز داریم: اگر `read_to_string` موفق شود، آنگاه تابع ما موفقیت‌آمیز +بوده و نام کاربری از فایل که اکنون در `username` است، درون یک `Ok` بازمی‌گرداند. اگر +`read_to_string` شکست بخورد، مقدار خطا را به همان شیوه‌ای که مقدار خطا را در `match` که مقدار +بازگشتی `File::open` را مدیریت می‌کرد بازمی‌گردانیم. با این حال، نیازی نیست که به صراحت بگوییم +`return`، زیرا این آخرین عبارت در تابع است. + +کدی که این تابع را فراخوانی می‌کند سپس مدیریت دریافت مقدار `Ok` که شامل یک نام کاربری است یا +مقدار `Err` که شامل یک `io::Error` است را انجام می‌دهد. این به کدی که تابع را فراخوانی می‌کند بستگی دارد +که تصمیم بگیرد با این مقادیر چه کاری انجام دهد. اگر کد فراخوانی‌کننده یک مقدار `Err` دریافت کند، +می‌تواند `panic!` را فراخوانی کرده و برنامه را متوقف کند، از یک نام کاربری پیش‌فرض استفاده کند، یا +به جای فایل نام کاربری را از مکان دیگری جستجو کند، برای مثال. ما اطلاعات کافی درباره اینکه کد فراخوانی‌کننده +دقیقاً چه می‌خواهد انجام دهد نداریم، بنابراین تمام اطلاعات موفقیت یا خطا را به بالا منتقل می‌کنیم +تا آن را به درستی مدیریت کند. + +این الگوی انتشار خطاها در Rust آن‌قدر رایج است که Rust عملگر `?` را برای آسان‌تر کردن این کار +فراهم می‌کند. + +#### یک میان‌بر برای انتشار خطاها: عملگر `?` + +لیست ۹-۷ پیاده‌سازی `read_username_from_file` را نشان می‌دهد که همان عملکرد لیست ۹-۶ را دارد، +اما این پیاده‌سازی از عملگر `?` استفاده می‌کند. + + ```rust {{#include ../listings/ch09-error-handling/listing-09-07/src/main.rs:here}} @@ -320,46 +227,27 @@ don't want to include it for rustdoc testing purposes. --> -The `?` placed after a `Result` value is defined to work in almost the same way -as the `match` expressions we defined to handle the `Result` values in Listing -9-6. If the value of the `Result` is an `Ok`, the value inside the `Ok` will -get returned from this expression, and the program will continue. If the value -is an `Err`, the `Err` will be returned from the whole function as if we had -used the `return` keyword so the error value gets propagated to the calling -code. - -There is a difference between what the `match` expression from Listing 9-6 does -and what the `?` operator does: error values that have the `?` operator called -on them go through the `from` function, defined in the `From` trait in the -standard library, which is used to convert values from one type into another. -When the `?` operator calls the `from` function, the error type received is -converted into the error type defined in the return type of the current -function. This is useful when a function returns one error type to represent -all the ways a function might fail, even if parts might fail for many different -reasons. - -For example, we could change the `read_username_from_file` function in Listing -9-7 to return a custom error type named `OurError` that we define. If we also -define `impl From for OurError` to construct an instance of -`OurError` from an `io::Error`, then the `?` operator calls in the body of -`read_username_from_file` will call `from` and convert the error types without -needing to add any more code to the function. - -In the context of Listing 9-7, the `?` at the end of the `File::open` call will -return the value inside an `Ok` to the variable `username_file`. If an error -occurs, the `?` operator will return early out of the whole function and give -any `Err` value to the calling code. The same thing applies to the `?` at the -end of the `read_to_string` call. - -The `?` operator eliminates a lot of boilerplate and makes this function’s -implementation simpler. We could even shorten this code further by chaining -method calls immediately after the `?`, as shown in Listing 9-8. - -- - +عملگر `?` که پس از یک مقدار `Result` قرار می‌گیرد تقریباً به همان شیوه‌ای عمل می‌کند که عبارات +`match` که برای مدیریت مقادیر `Result` در لیست ۹-۶ تعریف کردیم. اگر مقدار `Result` در حالت +`Ok` باشد، مقدار درون `Ok` از این عبارت بازگردانده می‌شود و برنامه ادامه می‌یابد. اگر مقدار در حالت +`Err` باشد، مقدار `Err` از کل تابع بازگردانده می‌شود به گونه‌ای که انگار کلیدواژه `return` را +استفاده کرده‌ایم تا مقدار خطا به کد فراخوانی‌کننده منتقل شود. + +تفاوتی بین کاری که عبارت `match` در لیست ۹-۶ انجام می‌دهد و کاری که عملگر `?` انجام می‌دهد وجود +دارد: مقادیر خطا که عملگر `?` روی آن‌ها فراخوانی می‌شود از طریق تابع `from` که در ویژگی +`From` کتابخانه استاندارد تعریف شده است عبور می‌کنند، که برای تبدیل مقادیر از یک نوع به نوع دیگر +استفاده می‌شود. وقتی عملگر `?` تابع `from` را فراخوانی می‌کند، نوع خطای دریافت شده به نوع خطای +تعریف شده در نوع بازگشتی تابع فعلی تبدیل می‌شود. این موضوع زمانی مفید است که یک تابع یک نوع خطا +را برای نمایش تمام راه‌هایی که ممکن است یک تابع شکست بخورد بازگرداند، حتی اگر بخش‌هایی ممکن است +به دلایل بسیار مختلفی شکست بخورند. + +برای مثال، می‌توانیم تابع `read_username_from_file` در لیست ۹-۷ را تغییر دهیم تا یک نوع خطای سفارشی به نام `OurError` که تعریف کرده‌ایم بازگرداند. اگر همچنین `impl From for OurError` را تعریف کنیم تا یک نمونه از `OurError` را از یک `io::Error` بسازد، سپس فراخوانی‌های عملگر `?` در بدنه تابع `read_username_from_file` تابع `from` را فراخوانی کرده و نوع خطاها را بدون نیاز به افزودن کد اضافی به تابع تبدیل می‌کنند. + +در زمینه لیست ۹-۷، عملگر `?` در انتهای فراخوانی `File::open` مقدار درون یک `Ok` را به متغیر `username_file` بازمی‌گرداند. اگر خطایی رخ دهد، عملگر `?` زودتر از کل تابع خارج شده و هر مقدار `Err` را به کد فراخوانی‌کننده بازمی‌گرداند. همین موضوع برای عملگر `?` در انتهای فراخوانی `read_to_string` صدق می‌کند. + +عملگر `?` مقدار زیادی از کد اضافی را حذف کرده و پیاده‌سازی این تابع را ساده‌تر می‌کند. حتی می‌توانیم این کد را بیشتر کوتاه کنیم با زنجیره کردن فراخوانی متدها بلافاصله بعد از `?`، همانطور که در لیست ۹-۸ نشان داده شده است. + + ```rust {{#include ../listings/ch09-error-handling/listing-09-08/src/main.rs:here}} @@ -367,22 +255,11 @@ don't want to include it for rustdoc testing purposes. --> -We’ve moved the creation of the new `String` in `username` to the beginning of -the function; that part hasn’t changed. Instead of creating a variable -`username_file`, we’ve chained the call to `read_to_string` directly onto the -result of `File::open("hello.txt")?`. We still have a `?` at the end of the -`read_to_string` call, and we still return an `Ok` value containing `username` -when both `File::open` and `read_to_string` succeed rather than returning -errors. The functionality is again the same as in Listing 9-6 and Listing 9-7; -this is just a different, more ergonomic way to write it. +ما ایجاد `String` جدید در `username` را به ابتدای تابع منتقل کرده‌ایم؛ آن قسمت تغییر نکرده است. به جای ایجاد یک متغیر `username_file`، ما فراخوانی `read_to_string` را مستقیماً به نتیجه `File::open("hello.txt")?` زنجیره کرده‌ایم. همچنان یک عملگر `?` در انتهای فراخوانی `read_to_string` داریم و همچنان مقدار `Ok` شامل `username` را زمانی که هر دو `File::open` و `read_to_string` موفق هستند بازمی‌گردانیم، به جای بازگرداندن خطاها. عملکرد دوباره همانند لیست ۹-۶ و لیست ۹-۷ است؛ این فقط یک روش متفاوت و کاربرپسندتر برای نوشتن آن است. -Listing 9-9 shows a way to make this even shorter using `fs::read_to_string`. +لیست ۹-۹ روشی برای کوتاه‌تر کردن این کد با استفاده از `fs::read_to_string` را نشان می‌دهد. -- - + ```rust {{#include ../listings/ch09-error-handling/listing-09-09/src/main.rs:here}} @@ -390,28 +267,15 @@ don't want to include it for rustdoc testing purposes. --> -Reading a file into a string is a fairly common operation, so the standard -library provides the convenient `fs::read_to_string` function that opens the -file, creates a new `String`, reads the contents of the file, puts the contents -into that `String`, and returns it. Of course, using `fs::read_to_string` -doesn’t give us the opportunity to explain all the error handling, so we did it -the longer way first. +خواندن یک فایل به یک رشته یک عملیات نسبتاً رایج است، بنابراین کتابخانه استاندارد تابع مناسب `fs::read_to_string` را فراهم می‌کند که فایل را باز می‌کند، یک `String` جدید ایجاد می‌کند، محتوای فایل را می‌خواند، محتوا را در آن `String` قرار می‌دهد و آن را بازمی‌گرداند. البته، استفاده از `fs::read_to_string` به ما فرصتی برای توضیح تمام مدیریت خطاها نمی‌دهد، بنابراین ابتدا آن را به روش طولانی‌تر انجام دادیم. -#### Where the `?` Operator Can Be Used +#### جایی که می‌توان از عملگر `?` استفاده کرد -The `?` operator can only be used in functions whose return type is compatible -with the value the `?` is used on. This is because the `?` operator is defined -to perform an early return of a value out of the function, in the same manner -as the `match` expression we defined in Listing 9-6. In Listing 9-6, the -`match` was using a `Result` value, and the early return arm returned an -`Err(e)` value. The return type of the function has to be a `Result` so that -it’s compatible with this `return`. +عملگر `?` فقط در توابعی استفاده می‌شود که نوع بازگشتی آن‌ها با مقدار استفاده شده توسط `?` سازگار باشد. این به این دلیل است که عملگر `?` برای بازگرداندن زودهنگام یک مقدار از تابع تعریف شده است، به همان شیوه‌ای که عبارت `match` در لیست ۹-۶ تعریف شده است. در لیست ۹-۶، `match` از یک مقدار `Result` استفاده می‌کرد و بازوی بازگشتی زودهنگام یک مقدار `Err(e)` را بازمی‌گرداند. نوع بازگشتی تابع باید یک `Result` باشد تا با این بازگشت سازگار باشد. -In Listing 9-10, let’s look at the error we’ll get if we use the `?` operator -in a `main` function with a return type that is incompatible with the type of -the value we use `?` on. +در لیست ۹-۱۰، بیایید به خطایی که دریافت می‌کنیم وقتی که از عملگر `?` در یک تابع `main` با نوع بازگشتی‌ای که با نوع مقدار استفاده شده در `?` سازگار نیست استفاده می‌کنیم نگاه کنیم. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch09-error-handling/listing-09-10/src/main.rs}} @@ -419,36 +283,19 @@ the value we use `?` on. -This code opens a file, which might fail. The `?` operator follows the `Result` -value returned by `File::open`, but this `main` function has the return type of -`()`, not `Result`. When we compile this code, we get the following error -message: +این کد یک فایل را باز می‌کند، که ممکن است شکست بخورد. عملگر `?` مقدار `Result` بازگردانده شده توسط `File::open` را دنبال می‌کند، اما این تابع `main` نوع بازگشتی `()` دارد، نه `Result`. وقتی این کد را کامپایل می‌کنیم، پیام خطای زیر را دریافت می‌کنیم: ```console {{#include ../listings/ch09-error-handling/listing-09-10/output.txt}} ``` -This error points out that we’re only allowed to use the `?` operator in a -function that returns `Result`, `Option`, or another type that implements -`FromResidual`. +این خطا نشان می‌دهد که فقط می‌توان از عملگر `?` در توابعی که نوع بازگشتی آن‌ها `Result`، `Option`، یا نوع دیگری که `FromResidual` را پیاده‌سازی می‌کند استفاده کرد. -To fix the error, you have two choices. One choice is to change the return type -of your function to be compatible with the value you’re using the `?` operator -on as long as you have no restrictions preventing that. The other choice is to -use a `match` or one of the `Result` methods to handle the `Result` -in whatever way is appropriate. +برای رفع این خطا، دو انتخاب دارید. یکی این است که نوع بازگشتی تابع خود را تغییر دهید تا با مقداری که از عملگر `?` استفاده می‌کنید سازگار باشد، به شرطی که محدودیتی مانع از انجام این کار نداشته باشید. انتخاب دیگر این است که از یک `match` یا یکی از متدهای `Result` برای مدیریت `Result` به شیوه‌ای که مناسب است استفاده کنید. -The error message also mentioned that `?` can be used with `Option` values -as well. As with using `?` on `Result`, you can only use `?` on `Option` in a -function that returns an `Option`. The behavior of the `?` operator when called -on an `Option` is similar to its behavior when called on a `Result`: -if the value is `None`, the `None` will be returned early from the function at -that point. If the value is `Some`, the value inside the `Some` is the -resultant value of the expression, and the function continues. Listing 9-11 has -an example of a function that finds the last character of the first line in the -given text. +پیام خطا همچنین اشاره کرد که `?` می‌تواند با مقادیر `Option` نیز استفاده شود. همانند استفاده از `?` روی `Result`، فقط می‌توانید از `?` روی `Option` در تابعی استفاده کنید که یک `Option` بازمی‌گرداند. رفتار عملگر `?` وقتی روی یک `Option` فراخوانی می‌شود شبیه به رفتار آن وقتی روی یک `Result` فراخوانی می‌شود: اگر مقدار `None` باشد، `None` زودهنگام از تابع بازگردانده می‌شود. اگر مقدار `Some` باشد، مقدار داخل `Some` مقدار نتیجه عبارت است و تابع ادامه می‌دهد. لیست ۹-۱۱ مثالی از تابعی را نشان می‌دهد که آخرین کاراکتر خط اول متن داده شده را پیدا می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch09-error-handling/listing-09-11/src/main.rs:here}} @@ -456,45 +303,17 @@ given text. -This function returns `Option` because it’s possible that there is a -character there, but it’s also possible that there isn’t. This code takes the -`text` string slice argument and calls the `lines` method on it, which returns -an iterator over the lines in the string. Because this function wants to -examine the first line, it calls `next` on the iterator to get the first value -from the iterator. If `text` is the empty string, this call to `next` will -return `None`, in which case we use `?` to stop and return `None` from -`last_char_of_first_line`. If `text` is not the empty string, `next` will -return a `Some` value containing a string slice of the first line in `text`. - -The `?` extracts the string slice, and we can call `chars` on that string slice -to get an iterator of its characters. We’re interested in the last character in -this first line, so we call `last` to return the last item in the iterator. -This is an `Option` because it’s possible that the first line is the empty -string; for example, if `text` starts with a blank line but has characters on -other lines, as in `"\nhi"`. However, if there is a last character on the first -line, it will be returned in the `Some` variant. The `?` operator in the middle -gives us a concise way to express this logic, allowing us to implement the -function in one line. If we couldn’t use the `?` operator on `Option`, we’d -have to implement this logic using more method calls or a `match` expression. - -Note that you can use the `?` operator on a `Result` in a function that returns -`Result`, and you can use the `?` operator on an `Option` in a function that -returns `Option`, but you can’t mix and match. The `?` operator won’t -automatically convert a `Result` to an `Option` or vice versa; in those cases, -you can use methods like the `ok` method on `Result` or the `ok_or` method on -`Option` to do the conversion explicitly. - -So far, all the `main` functions we’ve used return `()`. The `main` function is -special because it’s the entry point and exit point of an executable program, -and there are restrictions on what its return type can be for the program to -behave as expected. - -Luckily, `main` can also return a `Result<(), E>`. Listing 9-12 has the code -from Listing 9-10, but we’ve changed the return type of `main` to be -`Result<(), Box>` and added a return value `Ok(())` to the end. This -code will now compile. - -+این تابع `Option` بازمی‌گرداند زیرا ممکن است یک کاراکتر وجود داشته باشد، اما ممکن است وجود نداشته باشد. این کد آرگومان قطعه رشته `text` را می‌گیرد و متد `lines` را روی آن فراخوانی می‌کند، که یک iterator روی خطوط درون رشته بازمی‌گرداند. چون این تابع می‌خواهد خط اول را بررسی کند، `next` را روی iterator فراخوانی می‌کند تا اولین مقدار از iterator را دریافت کند. اگر `text` رشته‌ای خالی باشد، این فراخوانی به `next` مقدار `None` بازمی‌گرداند، که در این صورت از `?` برای متوقف کردن و بازگرداندن `None` از `last_char_of_first_line` استفاده می‌کنیم. اگر `text` رشته خالی نباشد، `next` یک مقدار `Some` شامل یک قطعه رشته از خط اول در `text` بازمی‌گرداند. + +عملگر `?` قطعه رشته را استخراج می‌کند و می‌توانیم متد `chars` را روی آن فراخوانی کنیم تا یک iterator از کاراکترهای آن دریافت کنیم. ما به آخرین کاراکتر در این خط اول علاقه‌مند هستیم، بنابراین متد `last` را فراخوانی می‌کنیم تا آخرین مورد در iterator را بازگرداند. این یک `Option` است زیرا ممکن است خط اول رشته‌ای خالی باشد؛ برای مثال، اگر `text` با یک خط خالی شروع شود اما کاراکترهایی در خطوط دیگر داشته باشد، مانند `"\nhi"`. با این حال، اگر آخرین کاراکتری در خط اول وجود داشته باشد، در حالت `Some` بازگردانده می‌شود. عملگر `?` در میانه به ما راهی مختصر برای بیان این منطق می‌دهد و اجازه می‌دهد تابع را در یک خط پیاده‌سازی کنیم. اگر نمی‌توانستیم از عملگر `?` روی `Option` استفاده کنیم، باید این منطق را با فراخوانی متدهای بیشتر یا یک عبارت `match` پیاده‌سازی می‌کردیم. + +توجه داشته باشید که می‌توانید از عملگر `?` روی یک `Result` در یک تابع که یک `Result` بازمی‌گرداند استفاده کنید، و می‌توانید از عملگر `?` روی یک `Option` در یک تابع که یک `Option` بازمی‌گرداند استفاده کنید، اما نمی‌توانید این دو را با هم ترکیب کنید. عملگر `?` به طور خودکار یک `Result` را به یک `Option` یا برعکس تبدیل نمی‌کند؛ در این موارد، می‌توانید از متدهایی مانند `ok` روی `Result` یا `ok_or` روی `Option` برای تبدیل صریح استفاده کنید. + +تا کنون، تمام توابع `main` که استفاده کرده‌ایم مقدار `()` بازمی‌گرداندند. تابع `main` خاص است زیرا نقطه ورود و خروج یک برنامه اجرایی است، و محدودیت‌هایی در نوع بازگشتی آن وجود دارد تا برنامه همانطور که انتظار می‌رود رفتار کند. + +خوشبختانه، `main` می‌تواند یک `Result<(), E>` نیز بازگرداند. لیست ۹-۱۲ کد لیست ۹-۱۰ را دارد، اما نوع بازگشتی `main` را به `Result<(), Box>` تغییر داده‌ایم و یک مقدار بازگشتی `Ok(())` به انتهای آن اضافه کرده‌ایم. این کد اکنون کامپایل می‌شود. + + ```rust,ignore {{#rustdoc_include ../listings/ch09-error-handling/listing-09-12/src/main.rs}} @@ -502,33 +321,22 @@ code will now compile. -The `Box` type is a _trait object_, which we’ll talk about in [“Using -Trait Objects That Allow for Values of Different Types”][trait-objects] in Chapter 18. For now, you can read `Box` to mean “any -kind of error.” Using `?` on a `Result` value in a `main` function with the -error type `Box` is allowed because it allows any `Err` value to be -returned early. Even though the body of this `main` function will only ever -return errors of type `std::io::Error`, by specifying `Box`, this -signature will continue to be correct even if more code that returns other -errors is added to the body of `main`. - -When a `main` function returns a `Result<(), E>`, the executable will exit with -a value of `0` if `main` returns `Ok(())` and will exit with a nonzero value if -`main` returns an `Err` value. Executables written in C return integers when -they exit: programs that exit successfully return the integer `0`, and programs -that error return some integer other than `0`. Rust also returns integers from -executables to be compatible with this convention. - -The `main` function may return any types that implement [the -`std::process::Termination` trait][termination], which contains -a function `report` that returns an `ExitCode`. Consult the standard library -documentation for more information on implementing the `Termination` trait for -your own types. - -Now that we’ve discussed the details of calling `panic!` or returning `Result`, -let’s return to the topic of how to decide which is appropriate to use in which -cases. - -[handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-result -[trait-objects]: ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types -[termination]: ../std/process/trait.Termination.html +نوع `Box` یک *trait object* است، +که در فصل ۱۸ در بخش [“استفاده از Trait Objectها برای مقادیر با انواع متفاوت”][trait-objects] +درباره‌ی آن صحبت خواهیم کرد. +فعلاً می‌توانید `Box` را به معنای «هر نوع خطایی» در نظر بگیرید. +استفاده از `?` روی یک مقدار `Result` در تابع `main` با نوع خطای `Box` مجاز است، +زیرا این امکان را می‌دهد که هر مقدار `Err` زودتر بازگردانده شود. +حتی اگر بدنه‌ی این تابع `main` فقط خطاهایی از نوع `std::io::Error` بازگرداند، +با مشخص کردن `Box` در امضا، این امضا همچنان معتبر باقی خواهد ماند +حتی اگر بعداً کدی به `main` اضافه شود که خطاهای دیگری را بازمی‌گرداند. + +وقتی یک تابع `main` یک `Result<(), E>` بازمی‌گرداند، برنامه اجرایی با مقدار `0` خارج می‌شود اگر `main` مقدار `Ok(())` بازگرداند و با یک مقدار غیر صفر خارج می‌شود اگر `main` مقدار `Err` بازگرداند. برنامه‌های اجرایی نوشته شده در C هنگام خروج مقادیر صحیح بازمی‌گردانند: برنامه‌هایی که با موفقیت خارج می‌شوند مقدار صحیح `0` را بازمی‌گردانند و برنامه‌هایی که دچار خطا می‌شوند مقداری غیر از `0` بازمی‌گردانند. Rust نیز مقادیر صحیح را از برنامه‌های اجرایی بازمی‌گرداند تا با این قرارداد سازگار باشد. + +تابع `main` می‌تواند هر نوعی را که ویژگی [`std::process::Termination`][termination] را پیاده‌سازی می‌کند بازگرداند، که شامل تابع `report` است که یک `ExitCode` بازمی‌گرداند. مستندات کتابخانه استاندارد را برای اطلاعات بیشتر درباره پیاده‌سازی ویژگی `Termination` برای انواع خودتان مطالعه کنید. + +اکنون که جزئیات فراخوانی `panic!` یا بازگرداندن `Result` را بررسی کردیم، بیایید به موضوع نحوه تصمیم‌گیری درباره اینکه کدامیک در چه مواردی مناسب است بازگردیم. + +[handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-result +[trait-objects]: ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types +[termination]: https://doc.rust-lang.org/std/process/trait.Termination.html diff --git a/src/ch09-03-to-panic-or-not-to-panic.md b/src/ch09-03-to-panic-or-not-to-panic.md index e14c98dd53..db318d63f6 100644 --- a/src/ch09-03-to-panic-or-not-to-panic.md +++ b/src/ch09-03-to-panic-or-not-to-panic.md @@ -1,144 +1,61 @@ -## To `panic!` or Not to `panic!` - -So how do you decide when you should call `panic!` and when you should return -`Result`? When code panics, there’s no way to recover. You could call `panic!` -for any error situation, whether there’s a possible way to recover or not, but -then you’re making the decision that a situation is unrecoverable on behalf of -the calling code. When you choose to return a `Result` value, you give the -calling code options. The calling code could choose to attempt to recover in a -way that’s appropriate for its situation, or it could decide that an `Err` -value in this case is unrecoverable, so it can call `panic!` and turn your -recoverable error into an unrecoverable one. Therefore, returning `Result` is a -good default choice when you’re defining a function that might fail. - -In situations such as examples, prototype code, and tests, it’s more -appropriate to write code that panics instead of returning a `Result`. Let’s -explore why, then discuss situations in which the compiler can’t tell that -failure is impossible, but you as a human can. The chapter will conclude with -some general guidelines on how to decide whether to panic in library code. - -### Examples, Prototype Code, and Tests - -When you’re writing an example to illustrate some concept, also including -robust error-handling code can make the example less clear. In examples, it’s -understood that a call to a method like `unwrap` that could panic is meant as a -placeholder for the way you’d want your application to handle errors, which can -differ based on what the rest of your code is doing. - -Similarly, the `unwrap` and `expect` methods are very handy when prototyping, -before you’re ready to decide how to handle errors. They leave clear markers in -your code for when you’re ready to make your program more robust. - -If a method call fails in a test, you’d want the whole test to fail, even if -that method isn’t the functionality under test. Because `panic!` is how a test -is marked as a failure, calling `unwrap` or `expect` is exactly what should -happen. - -### Cases in Which You Have More Information Than the Compiler - -It would also be appropriate to call `expect` when you have some other logic -that ensures the `Result` will have an `Ok` value, but the logic isn’t -something the compiler understands. You’ll still have a `Result` value that you -need to handle: whatever operation you’re calling still has the possibility of -failing in general, even though it’s logically impossible in your particular -situation. If you can ensure by manually inspecting the code that you’ll never -have an `Err` variant, it’s perfectly acceptable to call `expect` and document -the reason you think you’ll never have an `Err` variant in the argument text. -Here’s an example: +## آیا باید از `panic!` استفاده کنیم یا نه؟ + +چگونه تصمیم می‌گیرید که چه زمانی باید `panic!` را فراخوانی کنید و چه زمانی باید یک `Result` بازگردانید؟ وقتی کد دچار خطا می‌شود، هیچ راهی برای بازیابی وجود ندارد. شما می‌توانید در هر وضعیت خطایی، چه قابل بازیابی باشد و چه نباشد، `panic!` را فراخوانی کنید، اما در این صورت، شما به جای کد فراخوانی‌کننده تصمیم می‌گیرید که وضعیت غیرقابل بازیابی است. وقتی تصمیم می‌گیرید یک مقدار `Result` بازگردانید، به کد فراخوانی‌کننده گزینه‌هایی می‌دهید. کد فراخوانی‌کننده می‌تواند انتخاب کند که تلاش کند خطا را به روشی که برای وضعیت خودش مناسب است بازیابی کند، یا می‌تواند تصمیم بگیرد که مقدار `Err` در این مورد غیرقابل بازیابی است و بنابراین `panic!` را فراخوانی کرده و خطای قابل بازیابی شما را به یک خطای غیرقابل بازیابی تبدیل کند. بنابراین، بازگرداندن `Result` یک انتخاب پیش‌فرض خوب است وقتی تابعی تعریف می‌کنید که ممکن است شکست بخورد. + +در وضعیت‌هایی مانند مثال‌ها، کد نمونه‌سازی (prototype) و آزمون‌ها، مناسب‌تر است که کدی بنویسید که متوقف شود به جای بازگرداندن یک `Result`. بیایید بررسی کنیم چرا، سپس وضعیت‌هایی را بحث کنیم که کامپایلر نمی‌تواند بفهمد که شکست غیرممکن است، اما شما به عنوان یک انسان می‌توانید. این فصل با برخی دستورالعمل‌های کلی درباره تصمیم‌گیری درباره اینکه آیا در کد کتابخانه باید از `panic!` استفاده کرد یا نه، به پایان خواهد رسید. + +### مثال‌ها، کد نمونه‌سازی، و آزمون‌ها + +وقتی مثالی می‌نویسید تا یک مفهوم را توضیح دهید، همچنین افزودن کد مدیریت خطای قدرتمند می‌تواند مثال را کمتر واضح کند. در مثال‌ها، این نکته فهمیده می‌شود که فراخوانی به متدی مانند `unwrap` که ممکن است متوقف شود، به عنوان یک جایگزین برای روشی که می‌خواهید برنامه شما خطاها را مدیریت کند در نظر گرفته می‌شود، که می‌تواند بسته به آنچه بقیه کد شما انجام می‌دهد متفاوت باشد. + +به همین ترتیب، متدهای `unwrap` و `expect` بسیار مفید هستند وقتی که در حال نمونه‌سازی هستید و هنوز تصمیم نگرفته‌اید که چگونه خطاها را مدیریت کنید. آن‌ها نشانه‌های واضحی در کد شما می‌گذارند برای زمانی که آماده باشید برنامه خود را قدرتمندتر کنید. + +اگر یک متد در یک آزمون شکست بخورد، می‌خواهید کل آزمون شکست بخورد، حتی اگر آن متد ویژگی‌ای که تحت آزمون قرار دارد نباشد. از آنجا که `panic!` راهی است که یک آزمون به عنوان شکست‌خورده علامت‌گذاری می‌شود، فراخوانی `unwrap` یا `expect` دقیقاً همان چیزی است که باید اتفاق بیفتد. + +### مواردی که شما اطلاعات بیشتری نسبت به کامپایلر دارید + +در زمانی که منطق دیگری در برنامه شما وجود دارد که تضمین می‌کند مقدار `Result` از نوع `Ok` خواهد بود، +اما این منطق چیزی نیست که کامپایلر بتواند آن را درک کند، +استفاده از `expect` نیز مناسب خواهد بود. +شما همچنان با یک مقدار `Result` مواجه هستید که باید آن را مدیریت کنید: +عملیاتی که فراخوانی می‌کنید به‌صورت کلی ممکن است شکست بخورد، +حتی اگر در موقعیت خاص شما از نظر منطقی وقوع خطا غیرممکن باشد. +اگر با بررسی دستی کد بتوانید اطمینان حاصل کنید که هیچ‌گاه با واریانت `Err` روبه‌رو نخواهید شد، +استفاده از `expect` کاملاً قابل‌قبول است، +به شرط آن‌که دلیل این اطمینان خود را در قالب متن آرگومان `expect` مستندسازی کنید. +در این‌جا یک مثال آورده شده است: ```rust {{#rustdoc_include ../listings/ch09-error-handling/no-listing-08-unwrap-that-cant-fail/src/main.rs:here}} ``` -We’re creating an `IpAddr` instance by parsing a hardcoded string. We can see -that `127.0.0.1` is a valid IP address, so it’s acceptable to use `expect` -here. However, having a hardcoded, valid string doesn’t change the return type -of the `parse` method: we still get a `Result` value, and the compiler will -still make us handle the `Result` as if the `Err` variant is a possibility -because the compiler isn’t smart enough to see that this string is always a -valid IP address. If the IP address string came from a user rather than being -hardcoded into the program and therefore _did_ have a possibility of failure, -we’d definitely want to handle the `Result` in a more robust way instead. -Mentioning the assumption that this IP address is hardcoded will prompt us to -change `expect` to better error-handling code if, in the future, we need to get -the IP address from some other source instead. - -### Guidelines for Error Handling - -It’s advisable to have your code panic when it’s possible that your code could -end up in a bad state. In this context, a _bad state_ is when some assumption, -guarantee, contract, or invariant has been broken, such as when invalid values, -contradictory values, or missing values are passed to your code—plus one or -more of the following: - -- The bad state is something that is unexpected, as opposed to something that - will likely happen occasionally, like a user entering data in the wrong - format. -- Your code after this point needs to rely on not being in this bad state, - rather than checking for the problem at every step. -- There’s not a good way to encode this information in the types you use. We’ll - work through an example of what we mean in [“Encoding States and Behavior as - Types”][encoding] in Chapter 18. - -If someone calls your code and passes in values that don’t make sense, it’s -best to return an error if you can so the user of the library can decide what -they want to do in that case. However, in cases where continuing could be -insecure or harmful, the best choice might be to call `panic!` and alert the -person using your library to the bug in their code so they can fix it during -development. Similarly, `panic!` is often appropriate if you’re calling -external code that is out of your control and it returns an invalid state that -you have no way of fixing. - -However, when failure is expected, it’s more appropriate to return a `Result` -than to make a `panic!` call. Examples include a parser being given malformed -data or an HTTP request returning a status that indicates you have hit a rate -limit. In these cases, returning a `Result` indicates that failure is an -expected possibility that the calling code must decide how to handle. - -When your code performs an operation that could put a user at risk if it’s -called using invalid values, your code should verify the values are valid first -and panic if the values aren’t valid. This is mostly for safety reasons: -attempting to operate on invalid data can expose your code to vulnerabilities. -This is the main reason the standard library will call `panic!` if you attempt -an out-of-bounds memory access: trying to access memory that doesn’t belong to -the current data structure is a common security problem. Functions often have -_contracts_: their behavior is only guaranteed if the inputs meet particular -requirements. Panicking when the contract is violated makes sense because a -contract violation always indicates a caller-side bug, and it’s not a kind of -error you want the calling code to have to explicitly handle. In fact, there’s -no reasonable way for calling code to recover; the calling _programmers_ need -to fix the code. Contracts for a function, especially when a violation will -cause a panic, should be explained in the API documentation for the function. - -However, having lots of error checks in all of your functions would be verbose -and annoying. Fortunately, you can use Rust’s type system (and thus the type -checking done by the compiler) to do many of the checks for you. If your -function has a particular type as a parameter, you can proceed with your code’s -logic knowing that the compiler has already ensured you have a valid value. For -example, if you have a type rather than an `Option`, your program expects to -have _something_ rather than _nothing_. Your code then doesn’t have to handle -two cases for the `Some` and `None` variants: it will only have one case for -definitely having a value. Code trying to pass nothing to your function won’t -even compile, so your function doesn’t have to check for that case at runtime. -Another example is using an unsigned integer type such as `u32`, which ensures -the parameter is never negative. - -### Creating Custom Types for Validation - -Let’s take the idea of using Rust’s type system to ensure we have a valid value -one step further and look at creating a custom type for validation. Recall the -guessing game in Chapter 2 in which our code asked the user to guess a number -between 1 and 100. We never validated that the user’s guess was between those -numbers before checking it against our secret number; we only validated that -the guess was positive. In this case, the consequences were not very dire: our -output of “Too high” or “Too low” would still be correct. But it would be a -useful enhancement to guide the user toward valid guesses and have different -behavior when the user guesses a number that’s out of range versus when the -user types, for example, letters instead. - -One way to do this would be to parse the guess as an `i32` instead of only a -`u32` to allow potentially negative numbers, and then add a check for the -number being in range, like so: +ما یک نمونه `IpAddr` را با تجزیه یک رشته ثابت‌شده ایجاد می‌کنیم. ما می‌توانیم ببینیم که `127.0.0.1` یک آدرس IP معتبر است، بنابراین استفاده از `expect` در اینجا قابل قبول است. با این حال، داشتن یک رشته ثابت‌شده و معتبر نوع بازگشتی متد `parse` را تغییر نمی‌دهد: ما همچنان یک مقدار `Result` دریافت می‌کنیم و کامپایلر همچنان ما را مجبور می‌کند که با `Result` برخورد کنیم، انگار که حالت `Err` ممکن است، زیرا کامپایلر به اندازه کافی هوشمند نیست تا ببیند این رشته همیشه یک آدرس IP معتبر است. اگر رشته آدرس IP از یک کاربر می‌آمد به جای اینکه در برنامه ثابت شده باشد و بنابراین امکان شکست وجود داشت، قطعاً می‌خواستیم که `Result` را به روشی قدرتمندتر مدیریت کنیم. اشاره به این فرض که این آدرس IP ثابت‌شده است، ما را ترغیب می‌کند که در صورت نیاز به دریافت آدرس IP از منبع دیگری در آینده، `expect` را به کد مدیریت خطای بهتر تغییر دهیم. + +### دستورالعمل‌هایی برای مدیریت خطاها + +توصیه می‌شود که کد شما زمانی که ممکن است به وضعیت نامناسبی برسد، دچار `panic!` شود. در این زمینه، یک _وضعیت نامناسب_ زمانی رخ می‌دهد که برخی فرضیات، تضمین‌ها، قراردادها، یا تغییرناپذیری‌ها شکسته شوند، مانند زمانی که مقادیر نامعتبر، مقادیر متناقض، یا مقادیر گمشده به کد شما پاس داده می‌شوند—به علاوه یکی یا بیشتر از شرایط زیر: + +* وضعیت نادرست (bad state) چیزی غیرمنتظره است، بر خلاف موقعیت‌هایی که احتمالاً گاهی اتفاق می‌افتند، + مانند وارد کردن داده با فرمت نادرست توسط کاربر. +* کد شما پس از این نقطه باید به نبودن در چنین وضعیت نادرستی تکیه کند، + به‌جای آن‌که در هر مرحله مشکل را بررسی کند. +* راه مناسبی برای رمزگذاری این اطلاعات در قالب typeهایی که استفاده می‌کنید وجود ندارد. + در فصل ۱۸، در بخش [“رمزگذاری وضعیت‌ها و رفتارها به‌صورت type”][encoding] + با مثالی منظور خود را توضیح خواهیم داد. + +اگر کسی کد شما را فراخوانی کند و مقادیری که منطقی نیستند را پاس دهد، بهتر است که یک خطا بازگردانید تا کاربر کتابخانه بتواند تصمیم بگیرد که در آن مورد چه کاری انجام دهد. با این حال، در مواردی که ادامه دادن می‌تواند ناامن یا مضر باشد، بهترین انتخاب ممکن است فراخوانی `panic!` و هشدار به شخصی که از کتابخانه شما استفاده می‌کند درباره باگ در کد آن‌ها باشد تا بتوانند آن را در حین توسعه رفع کنند. به همین ترتیب، `panic!` اغلب مناسب است اگر کد خارجی که از کنترل شما خارج است را فراخوانی می‌کنید و آن کد یک وضعیت نامعتبر بازمی‌گرداند که شما هیچ راهی برای رفع آن ندارید. + +با این حال، زمانی که شکست مورد انتظار است، مناسب‌تر است که یک `Result` بازگردانید تا یک فراخوانی `panic!`. مثال‌ها شامل پردازشی هستند که داده‌های نادرست دریافت می‌کند یا یک درخواست HTTP که بازگشت وضعیت نشان می‌دهد که به محدودیت نرخ برخورد کرده‌اید. در این موارد، بازگرداندن یک `Result` نشان می‌دهد که شکست یک احتمال مورد انتظار است که کد فراخوانی‌کننده باید تصمیم بگیرد چگونه آن را مدیریت کند. + +وقتی کد شما عملیاتی انجام می‌دهد که می‌تواند در صورت فراخوانی با مقادیر نامعتبر کاربر را در معرض خطر قرار دهد، کد شما باید ابتدا مقادیر را تأیید کند و اگر مقادیر نامعتبر هستند دچار `panic!` شود. این بیشتر به دلایل ایمنی است: تلاش برای انجام عملیات روی داده‌های نامعتبر می‌تواند کد شما را در معرض آسیب‌پذیری‌ها قرار دهد. این دلیل اصلی است که کتابخانه استاندارد اگر شما تلاش کنید به حافظه خارج از محدوده دسترسی پیدا کنید، دچار `panic!` می‌شود: تلاش برای دسترسی به حافظه‌ای که به ساختار داده جاری تعلق ندارد یک مشکل امنیتی رایج است. توابع اغلب _قراردادهایی_ دارند: رفتار آن‌ها فقط در صورتی تضمین می‌شود که ورودی‌ها نیازمندی‌های خاصی را برآورده کنند. دچار `panic!` شدن وقتی که قرارداد نقض می‌شود منطقی است زیرا نقض قرارداد همیشه نشان‌دهنده یک باگ در طرف فراخوانی‌کننده است و نوع خطایی نیست که بخواهید کد فراخوانی‌کننده به طور صریح مدیریت کند. در واقع، هیچ راه معقولی برای بازیابی کد فراخوانی‌کننده وجود ندارد؛ _برنامه‌نویسان فراخوانی‌کننده_ باید کد را اصلاح کنند. قراردادهای یک تابع، به خصوص زمانی که نقض آن باعث `panic!` می‌شود، باید در مستندات API تابع توضیح داده شوند. + +با این حال، داشتن بررسی‌های خطا در تمام توابع شما بسیار طولانی و ناخوشایند خواهد بود. خوشبختانه، شما می‌توانید از سیستم نوع Rust (و در نتیجه بررسی نوعی که توسط کامپایلر انجام می‌شود) برای انجام بسیاری از بررسی‌ها استفاده کنید. اگر تابع شما یک نوع خاص را به عنوان پارامتر داشته باشد، می‌توانید با اطمینان از اینکه کامپایلر قبلاً تضمین کرده است که یک مقدار معتبر دارید، منطق کد خود را پیش ببرید. برای مثال، اگر شما یک نوع به جای یک `Option` داشته باشید، برنامه شما انتظار دارد که _چیزی_ به جای _هیچ‌چیز_ وجود داشته باشد. سپس کد شما نیازی به مدیریت دو حالت برای حالت‌های `Some` و `None` ندارد: فقط یک حالت برای داشتن یک مقدار به طور قطعی خواهد داشت. کدی که سعی می‌کند هیچ‌چیز به تابع شما پاس دهد حتی کامپایل نخواهد شد، بنابراین تابع شما نیازی به بررسی این حالت در زمان اجرا ندارد. مثال دیگر استفاده از یک نوع عددی بدون علامت مانند `u32` است که تضمین می‌کند پارامتر هرگز منفی نخواهد بود. + +### ایجاد انواع سفارشی برای اعتبارسنجی + +بیایید ایده استفاده از سیستم نوع Rust برای اطمینان از داشتن یک مقدار معتبر را یک قدم فراتر ببریم و به ایجاد یک نوع سفارشی برای اعتبارسنجی نگاه کنیم. بازی حدس عدد در فصل ۲ را به یاد بیاورید که کد ما از کاربر خواست تا یک عدد بین ۱ تا ۱۰۰ حدس بزند. ما هرگز اعتبارسنجی نکردیم که حدس کاربر بین این اعداد باشد قبل از اینکه آن را با عدد مخفی مقایسه کنیم؛ فقط بررسی کردیم که حدس مثبت باشد. در این مورد، پیامدها چندان شدید نبودند: خروجی ما با پیام‌های "خیلی بزرگ" یا "خیلی کوچک" همچنان درست بود. اما این می‌تواند بهبودی مفید باشد که کاربر را به سمت حدس‌های معتبر هدایت کنیم و رفتار متفاوتی داشته باشیم وقتی کاربر عددی خارج از محدوده حدس می‌زند در مقابل زمانی که، برای مثال، حروف تایپ می‌کند. + +یک راه برای انجام این کار این است که حدس را به جای فقط یک `u32`، به صورت یک `i32` تجزیه کنیم تا اجازه دهیم اعداد منفی نیز در نظر گرفته شوند، و سپس یک بررسی برای اینکه عدد در محدوده است یا نه اضافه کنیم، مانند زیر: @@ -148,25 +65,19 @@ number being in range, like so: -The `if` expression checks whether our value is out of range, tells the user -about the problem, and calls `continue` to start the next iteration of the loop -and ask for another guess. After the `if` expression, we can proceed with the -comparisons between `guess` and the secret number knowing that `guess` is -between 1 and 100. +عبارت `if` بررسی می‌کند که آیا مقدار ما خارج از محدوده است، به کاربر درباره مشکل اطلاع می‌دهد و `continue` را فراخوانی می‌کند تا تکرار بعدی حلقه شروع شود و درخواست یک حدس دیگر شود. بعد از عبارت `if`، می‌توانیم با مقایسه بین `guess` و عدد مخفی ادامه دهیم، زیرا می‌دانیم که `guess` بین ۱ و ۱۰۰ است. -However, this is not an ideal solution: if it were absolutely critical that the -program only operated on values between 1 and 100, and it had many functions -with this requirement, having a check like this in every function would be -tedious (and might impact performance). +با این حال، این یک راه‌حل ایده‌آل نیست: اگر بسیار حیاتی باشد که برنامه فقط بر روی مقادیر بین ۱ و ۱۰۰ عمل کند، و برنامه توابع زیادی با این نیاز داشته باشد، داشتن چنین بررسی‌هایی در هر تابع خسته‌کننده خواهد بود (و ممکن است عملکرد را تحت تأثیر قرار دهد). -Instead, we can make a new type in a dedicated module and put the validations in -a function to create an instance of the type rather than repeating the -validations everywhere. That way, it’s safe for functions to use the new type in -their signatures and confidently use the values they receive. Listing 9-13 shows -one way to define a `Guess` type that will only create an instance of `Guess` if -the `new` function receives a value between 1 and 100. +در عوض، می‌توانیم یک نوع جدید در یک ماژول اختصاصی تعریف کنیم +و اعتبارسنجی‌ها (validations) را در تابعی قرار دهیم که وظیفه‌ی ایجاد یک نمونه از آن نوع را دارد، +به‌جای آن‌که این اعتبارسنجی‌ها را در همه‌جا تکرار کنیم. +به این ترتیب، استفاده از این نوع جدید در امضای توابع ایمن خواهد بود +و می‌توان با اطمینان از مقادیری که دریافت می‌کنند استفاده کرد. +لیستینگ 9-13 یک روش برای تعریف نوع `Guess` را نشان می‌دهد +که تنها زمانی یک نمونه از `Guess` ایجاد می‌کند که تابع `new` مقداری بین 1 تا 100 دریافت کرده باشد. -+ ```rust {{#rustdoc_include ../listings/ch09-error-handling/listing-09-13/src/guessing_game.rs}} @@ -174,53 +85,31 @@ the `new` function receives a value between 1 and 100. -Note that this code in *src/guessing_game.rs* depends on adding a module -declaration `mod guessing_game;` in *src/lib.rs* that we haven’t shown here. -Within this new module’s file, we define a struct in that module named `Guess` -that has a field named `value` that holds an `i32`. This is where the number -will be stored. - -Then we implement an associated function named `new` on `Guess` that creates -instances of `Guess` values. The `new` function is defined to have one -parameter named `value` of type `i32` and to return a `Guess`. The code in the -body of the `new` function tests `value` to make sure it’s between 1 and 100. -If `value` doesn’t pass this test, we make a `panic!` call, which will alert -the programmer who is writing the calling code that they have a bug they need -to fix, because creating a `Guess` with a `value` outside this range would -violate the contract that `Guess::new` is relying on. The conditions in which -`Guess::new` might panic should be discussed in its public-facing API -documentation; we’ll cover documentation conventions indicating the possibility -of a `panic!` in the API documentation that you create in Chapter 14. If -`value` does pass the test, we create a new `Guess` with its `value` field set -to the `value` parameter and return the `Guess`. - -Next, we implement a method named `value` that borrows `self`, doesn’t have any -other parameters, and returns an `i32`. This kind of method is sometimes called -a _getter_ because its purpose is to get some data from its fields and return -it. This public method is necessary because the `value` field of the `Guess` -struct is private. It’s important that the `value` field be private so code -using the `Guess` struct is not allowed to set `value` directly: code outside -the `guessing_game` module _must_ use the `Guess::new` function to create an -instance of `Guess`, thereby ensuring there’s no way for a `Guess` to have a -`value` that hasn’t been checked by the conditions in the `Guess::new` function. - -A function that has a parameter or returns only numbers between 1 and 100 could -then declare in its signature that it takes or returns a `Guess` rather than an -`i32` and wouldn’t need to do any additional checks in its body. - -## Summary - -Rust’s error-handling features are designed to help you write more robust code. -The `panic!` macro signals that your program is in a state it can’t handle and -lets you tell the process to stop instead of trying to proceed with invalid or -incorrect values. The `Result` enum uses Rust’s type system to indicate that -operations might fail in a way that your code could recover from. You can use -`Result` to tell code that calls your code that it needs to handle potential -success or failure as well. Using `panic!` and `Result` in the appropriate -situations will make your code more reliable in the face of inevitable problems. - -Now that you’ve seen useful ways that the standard library uses generics with -the `Option` and `Result` enums, we’ll talk about how generics work and how you -can use them in your code. +توجه داشته باشید که این کد در فایل *src/guessing_game.rs* +بستگی به اضافه کردن یک اعلان ماژول به شکل `mod guessing_game;` در فایل *src/lib.rs* دارد +که در این‌جا نشان داده نشده است. +درون فایل این ماژول جدید، یک `struct` به نام `Guess` تعریف کرده‌ایم +که در همان ماژول قرار دارد و دارای یک فیلد به نام `value` است که یک مقدار از نوع `i32` را نگه می‌دارد. +این همان مکانی است که عدد در آن ذخیره خواهد شد. + +سپس یک تابع وابسته به نام `new` روی `Guess` پیاده‌سازی می‌کنیم که نمونه‌هایی از مقادیر `Guess` ایجاد می‌کند. تابع `new` به گونه‌ای تعریف شده که یک پارامتر به نام `value` از نوع `i32` داشته باشد و یک `Guess` بازگرداند. کدی که در بدنه تابع `new` قرار دارد مقدار `value` را بررسی می‌کند تا مطمئن شود که بین ۱ و ۱۰۰ است. اگر مقدار `value` این آزمون را پاس نکند، یک فراخوانی به `panic!` انجام می‌دهیم، که به برنامه‌نویسی که کد فراخوانی‌کننده را می‌نویسد هشدار می‌دهد که باگی دارد که باید برطرف کند، زیرا ایجاد یک `Guess` با مقدار `value` خارج از این محدوده قرارداد تابع `Guess::new` را نقض می‌کند. شرایطی که ممکن است باعث `panic!` در `Guess::new` شود باید در مستندات عمومی API آن مورد بحث قرار گیرد؛ ما در فصل ۱۴ درباره قراردادهای مستندات که نشان‌دهنده احتمال وقوع `panic!` هستند صحبت خواهیم کرد. اگر مقدار `value` آزمون را پاس کند، یک `Guess` جدید با فیلد `value` تنظیم شده به پارامتر `value` ایجاد می‌کنیم و `Guess` را بازمی‌گردانیم. + +در مرحله‌ی بعد، متدی به نام `value` پیاده‌سازی می‌کنیم که `self` را به‌صورت رفرنس قرض می‌گیرد، +پارامتر دیگری ندارد، و یک مقدار `i32` بازمی‌گرداند. +این نوع متد گاهی *getter* نامیده می‌شود، زیرا هدف آن دریافت داده‌ای از فیلدهای ساختار و بازگرداندن آن است. +این متد عمومی لازم است چون فیلد `value` در `struct` مربوط به `Guess` خصوصی است. +خصوصی بودن فیلد `value` اهمیت دارد تا کدی که از `struct` `Guess` استفاده می‌کند، +اجازه نداشته باشد مستقیماً `value` را مقداردهی کند: +کد خارج از ماژول `guessing_game` *باید* از تابع `Guess::new` برای ساخت نمونه‌ای از `Guess` استفاده کند، +و به این ترتیب، اطمینان حاصل می‌شود که هیچ راهی برای ساخت یک `Guess` با مقدار `value`ای +که بررسی‌های موجود در تابع `Guess::new` را نگذرانده، وجود ندارد. + +تابعی که یک پارامتر می‌گیرد یا فقط اعدادی بین ۱ و ۱۰۰ بازمی‌گرداند می‌تواند در امضای خود اعلام کند که یک `Guess` می‌گیرد یا بازمی‌گرداند به جای یک `i32` و نیازی به انجام بررسی‌های اضافی در بدنه خود ندارد. + +## خلاصه + +ویژگی‌های مدیریت خطای Rust طراحی شده‌اند تا به شما کمک کنند کدی قدرتمندتر بنویسید. ماکروی `panic!` نشان می‌دهد که برنامه شما در حالتی قرار دارد که نمی‌تواند آن را مدیریت کند و به شما امکان می‌دهد فرآیند را متوقف کنید به جای اینکه سعی کنید با مقادیر نامعتبر یا نادرست ادامه دهید. Enum `Result` از سیستم نوع Rust استفاده می‌کند تا نشان دهد که عملیات ممکن است به روشی شکست بخورد که کد شما می‌تواند از آن بازیابی کند. می‌توانید از `Result` برای اطلاع دادن به کدی که کد شما را فراخوانی می‌کند استفاده کنید که باید موفقیت یا شکست احتمالی را نیز مدیریت کند. استفاده از `panic!` و `Result` در شرایط مناسب باعث می‌شود کد شما در برابر مشکلات اجتناب‌ناپذیر قابل اطمینان‌تر شود. + +حالا که راه‌های مفید استفاده کتابخانه استاندارد از جنریک‌ها با Enums `Option` و `Result` را دیده‌اید، درباره نحوه عملکرد جنریک‌ها و نحوه استفاده از آن‌ها در کد خود صحبت خواهیم کرد. [encoding]: ch18-03-oo-design-patterns.html#encoding-states-and-behavior-as-types diff --git a/src/ch10-00-generics.md b/src/ch10-00-generics.md index 20289557f1..cd4c3ef7d2 100644 --- a/src/ch10-00-generics.md +++ b/src/ch10-00-generics.md @@ -1,48 +1,22 @@ -# Generic Types, Traits, and Lifetimes - -Every programming language has tools for effectively handling the duplication -of concepts. In Rust, one such tool is _generics_: abstract stand-ins for -concrete types or other properties. We can express the behavior of generics or -how they relate to other generics without knowing what will be in their place -when compiling and running the code. - -Functions can take parameters of some generic type, instead of a concrete type -like `i32` or `String`, in the same way they take parameters with unknown -values to run the same code on multiple concrete values. In fact, we’ve already -used generics in Chapter 6 with `Option`, in Chapter 8 with `Vec` and -`HashMap`, and in Chapter 9 with `Result`. In this chapter, you’ll -explore how to define your own types, functions, and methods with generics! - -First we’ll review how to extract a function to reduce code duplication. We’ll -then use the same technique to make a generic function from two functions that -differ only in the types of their parameters. We’ll also explain how to use -generic types in struct and enum definitions. - -Then you’ll learn how to use _traits_ to define behavior in a generic way. You -can combine traits with generic types to constrain a generic type to accept -only those types that have a particular behavior, as opposed to just any type. - -Finally, we’ll discuss _lifetimes_: a variety of generics that give the -compiler information about how references relate to each other. Lifetimes allow -us to give the compiler enough information about borrowed values so that it can -ensure references will be valid in more situations than it could without our -help. - -## Removing Duplication by Extracting a Function - -Generics allow us to replace specific types with a placeholder that represents -multiple types to remove code duplication. Before diving into generics syntax, -let’s first look at how to remove duplication in a way that doesn’t involve -generic types by extracting a function that replaces specific values with a -placeholder that represents multiple values. Then we’ll apply the same -technique to extract a generic function! By looking at how to recognize -duplicated code you can extract into a function, you’ll start to recognize -duplicated code that can use generics. - -We’ll begin with the short program in Listing 10-1 that finds the largest -number in a list. - -+# انواع جنریک، ویژگی‌ها (Traits)، و طول عمرها (Lifetimes) + +هر زبان برنامه‌نویسی ابزارهایی برای مدیریت موثر تکرار مفاهیم دارد. در Rust، یکی از این ابزارها _جنریک‌ها_ هستند: جایگزین‌های انتزاعی برای انواع مشخص یا ویژگی‌های دیگر. ما می‌توانیم رفتار جنریک‌ها یا نحوه ارتباط آن‌ها با جنریک‌های دیگر را بیان کنیم بدون اینکه بدانیم هنگام کامپایل و اجرای کد چه چیزی جایگزین آن‌ها خواهد شد. + +توابع می‌توانند پارامترهایی از نوع جنریک بگیرند، به جای یک نوع مشخص مانند `i32` یا `String`، به همان روشی که پارامترهایی با مقادیر ناشناخته می‌گیرند تا بتوانند کد مشابهی را روی مقادیر مشخص مختلف اجرا کنند. در واقع، ما قبلاً در فصل ۶ با `Option`، در فصل ۸ با `Vec` و `HashMap`، و در فصل ۹ با `Result` از جنریک‌ها استفاده کرده‌ایم. در این فصل، یاد خواهید گرفت که چگونه انواع، توابع، و متدهای خود را با جنریک‌ها تعریف کنید! + +ابتدا نحوه استخراج یک تابع برای کاهش تکرار کد را مرور می‌کنیم. سپس از همان تکنیک برای ایجاد یک تابع جنریک از دو تابع که تنها در نوع پارامترهایشان متفاوت هستند استفاده خواهیم کرد. همچنین توضیح خواهیم داد که چگونه می‌توان از انواع جنریک در تعریف ساختار داده‌ها (struct) و شمارش‌ها (enum) استفاده کرد. + +سپس یاد می‌گیرید که چگونه از _ویژگی‌ها_ (Traits) برای تعریف رفتار به صورت جنریک استفاده کنید. می‌توانید ویژگی‌ها را با انواع جنریک ترکیب کنید تا نوع جنریک را محدود کنید که فقط آن نوع‌هایی را بپذیرد که رفتار خاصی دارند، به جای هر نوعی. + +در نهایت، درباره _طول عمر‌ها_ (Lifetimes) صحبت خواهیم کرد: نوعی از جنریک‌ها که به کامپایلر اطلاعاتی درباره نحوه ارتباط مراجع با یکدیگر می‌دهند. طول عمرها به ما اجازه می‌دهند اطلاعات کافی درباره مقادیر قرض گرفته شده به کامپایلر بدهیم تا اطمینان حاصل کند که مراجع در شرایط بیشتری معتبر خواهند بود. + +## حذف تکرار با استخراج یک تابع + +جنریک‌ها به ما اجازه می‌دهند که نوع‌های مشخص را با یک جایگزین که نمایانگر چندین نوع است جایگزین کنیم تا تکرار کد را حذف کنیم. قبل از ورود به نحو جنریک‌ها، ابتدا به نحوه حذف تکرار به روشی که شامل انواع جنریک نمی‌شود، با استخراج یک تابع که مقادیر مشخص را با یک جایگزین که نمایانگر مقادیر چندگانه است جایگزین می‌کند، نگاه خواهیم کرد. سپس از همان تکنیک برای استخراج یک تابع جنریک استفاده خواهیم کرد! با بررسی نحوه تشخیص کد تکراری که می‌توانید به یک تابع استخراج کنید، شروع به تشخیص کد تکراری خواهید کرد که می‌تواند از جنریک‌ها استفاده کند. + +با برنامه کوتاه در لیست ۱۰-۱ که بزرگ‌ترین عدد را در یک لیست پیدا می‌کند، شروع می‌کنیم. + + ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-01/src/main.rs:here}} @@ -50,20 +24,11 @@ number in a list. -We store a list of integers in the variable `number_list` and place a reference -to the first number in the list in a variable named `largest`. We then iterate -through all the numbers in the list, and if the current number is greater than -the number stored in `largest`, we replace the reference in that variable. -However, if the current number is less than or equal to the largest number seen -so far, the variable doesn’t change, and the code moves on to the next number -in the list. After considering all the numbers in the list, `largest` should -refer to the largest number, which in this case is 100. +ما یک لیست از اعداد صحیح را در متغیر `number_list` ذخیره می‌کنیم و یک مرجع به اولین عدد در لیست را در متغیری به نام `largest` قرار می‌دهیم. سپس تمام اعداد لیست را پیمایش می‌کنیم و اگر عدد فعلی بزرگ‌تر از عدد ذخیره شده در `largest` باشد، مرجع در آن متغیر را جایگزین می‌کنیم. با این حال، اگر عدد فعلی کوچک‌تر یا مساوی با بزرگ‌ترین عدد دیده شده تاکنون باشد، متغیر تغییری نمی‌کند و کد به عدد بعدی در لیست می‌رود. پس از بررسی تمام اعداد در لیست، `largest` باید به بزرگ‌ترین عدد اشاره کند که در این مورد ۱۰۰ است. -We’ve now been tasked with finding the largest number in two different lists of -numbers. To do so, we can choose to duplicate the code in Listing 10-1 and use -the same logic at two different places in the program, as shown in Listing 10-2. +اکنون از ما خواسته شده است که بزرگ‌ترین عدد را در دو لیست مختلف اعداد پیدا کنیم. برای انجام این کار، می‌توانیم انتخاب کنیم که کد در لیست ۱۰-۱ را تکرار کنیم و از همان منطق در دو مکان مختلف در برنامه استفاده کنیم، همانطور که در لیست ۱۰-۲ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-02/src/main.rs}} @@ -71,21 +36,13 @@ the same logic at two different places in the program, as shown in Listing 10-2. -Although this code works, duplicating code is tedious and error prone. We also -have to remember to update the code in multiple places when we want to change -it. +اگرچه این کد کار می‌کند، تکرار کد خسته‌کننده و مستعد خطاست. همچنین وقتی بخواهیم کد را تغییر دهیم، باید به یاد داشته باشیم که آن را در مکان‌های مختلف به‌روزرسانی کنیم. -To eliminate this duplication, we’ll create an abstraction by defining a -function that operates on any list of integers passed in as a parameter. This -solution makes our code clearer and lets us express the concept of finding the -largest number in a list abstractly. +برای حذف این تکرار، یک انتزاع ایجاد خواهیم کرد با تعریف یک تابع که روی هر لیستی از اعداد صحیح که به عنوان پارامتر پاس داده می‌شود عمل می‌کند. این راه‌حل کد ما را واضح‌تر می‌کند و به ما اجازه می‌دهد مفهوم یافتن بزرگ‌ترین عدد در یک لیست را به صورت انتزاعی بیان کنیم. -In Listing 10-3, we extract the code that finds the largest number into a -function named `largest`. Then we call the function to find the largest number -in the two lists from Listing 10-2. We could also use the function on any other -list of `i32` values we might have in the future. +در لیست ۱۰-۳، کدی که بزرگ‌ترین عدد را پیدا می‌کند در تابعی به نام `largest` استخراج می‌کنیم. سپس این تابع را فراخوانی می‌کنیم تا بزرگ‌ترین عدد را در دو لیست از لیست ۱۰-۲ پیدا کنیم. همچنین می‌توانیم از این تابع روی هر لیست دیگری از مقادیر `i32` که ممکن است در آینده داشته باشیم استفاده کنیم. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-03/src/main.rs:here}} @@ -93,23 +50,14 @@ list of `i32` values we might have in the future. -The `largest` function has a parameter called `list`, which represents any -concrete slice of `i32` values we might pass into the function. As a result, -when we call the function, the code runs on the specific values that we pass -in. +تابع `largest` یک پارامتر به نام `list` دارد که نمایانگر هر بخش مشخصی از مقادیر `i32` است که ممکن است به تابع پاس دهیم. در نتیجه، وقتی تابع را فراخوانی می‌کنیم، کد روی مقادیر مشخصی که پاس می‌دهیم اجرا می‌شود. -In summary, here are the steps we took to change the code from Listing 10-2 to -Listing 10-3: +به طور خلاصه، مراحل زیر را برای تغییر کد از لیست ۱۰-۲ به لیست ۱۰-۳ طی کردیم: -1. Identify duplicate code. -1. Extract the duplicate code into the body of the function, and specify the - inputs and return values of that code in the function signature. -1. Update the two instances of duplicated code to call the function instead. +1. کد تکراری را شناسایی کنید. +2. کد تکراری را به بدنه یک تابع استخراج کرده و ورودی‌ها و مقادیر بازگشتی آن کد را در امضای تابع مشخص کنید. +3. دو نمونه از کد تکراری را به جای آن با فراخوانی تابع به‌روزرسانی کنید. -Next, we’ll use these same steps with generics to reduce code duplication. In -the same way that the function body can operate on an abstract `list` instead -of specific values, generics allow code to operate on abstract types. +در مرحله بعد، از همین مراحل با جنریک‌ها برای کاهش تکرار کد استفاده خواهیم کرد. همانطور که بدنه تابع می‌تواند روی یک `list` انتزاعی به جای مقادیر مشخص عمل کند، جنریک‌ها به کد اجازه می‌دهند که روی انواع انتزاعی عمل کند. -For example, say we had two functions: one that finds the largest item in a -slice of `i32` values and one that finds the largest item in a slice of `char` -values. How would we eliminate that duplication? Let’s find out! +برای مثال، فرض کنید دو تابع داشتیم: یکی که بزرگ‌ترین مورد را در یک بخش از مقادیر `i32` پیدا می‌کند و دیگری که بزرگ‌ترین مورد را در یک بخش از مقادیر `char` پیدا می‌کند. چگونه می‌توانیم این تکرار را حذف کنیم؟ بیایید پیدا کنیم! diff --git a/src/ch10-01-syntax.md b/src/ch10-01-syntax.md index 9be7e005a1..47af515565 100644 --- a/src/ch10-01-syntax.md +++ b/src/ch10-01-syntax.md @@ -1,22 +1,14 @@ -## Generic Data Types +## انواع داده جنریک -We use generics to create definitions for items like function signatures or -structs, which we can then use with many different concrete data types. Let’s -first look at how to define functions, structs, enums, and methods using -generics. Then we’ll discuss how generics affect code performance. +ما از جنریک‌ها برای ایجاد تعریف‌هایی برای مواردی مانند امضای توابع یا ساختارها (struct) استفاده می‌کنیم، که سپس می‌توانیم با انواع داده مشخص مختلف از آن‌ها استفاده کنیم. بیایید ابتدا ببینیم چگونه می‌توان توابع، ساختارها، شمارش‌ها (enum)، و متدها را با استفاده از جنریک‌ها تعریف کرد. سپس درباره اینکه جنریک‌ها چگونه بر عملکرد کد تأثیر می‌گذارند صحبت خواهیم کرد. -### In Function Definitions +### در تعریف توابع -When defining a function that uses generics, we place the generics in the -signature of the function where we would usually specify the data types of the -parameters and return value. Doing so makes our code more flexible and provides -more functionality to callers of our function while preventing code duplication. +هنگام تعریف یک تابع که از جنریک‌ها استفاده می‌کند، جنریک‌ها را در امضای تابع قرار می‌دهیم، جایی که معمولاً نوع داده پارامترها و مقدار بازگشتی را مشخص می‌کنیم. این کار کد ما را انعطاف‌پذیرتر می‌کند و به فراخوانی‌کنندگان تابع ما عملکرد بیشتری ارائه می‌دهد، در حالی که از تکرار کد جلوگیری می‌کند. -Continuing with our `largest` function, Listing 10-4 shows two functions that -both find the largest value in a slice. We’ll then combine these into a single -function that uses generics. +با ادامه تابع `largest`، لیست ۱۰-۴ دو تابع را نشان می‌دهد که هر دو بزرگ‌ترین مقدار را در یک بخش (slice) پیدا می‌کنند. سپس این‌ها را به یک تابع واحد که از جنریک‌ها استفاده می‌کند ترکیب خواهیم کرد. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-04/src/main.rs:here}} @@ -24,40 +16,28 @@ function that uses generics. -The `largest_i32` function is the one we extracted in Listing 10-3 that finds -the largest `i32` in a slice. The `largest_char` function finds the largest -`char` in a slice. The function bodies have the same code, so let’s eliminate -the duplication by introducing a generic type parameter in a single function. - -To parameterize the types in a new single function, we need to name the type -parameter, just as we do for the value parameters to a function. You can use -any identifier as a type parameter name. But we’ll use `T` because, by -convention, type parameter names in Rust are short, often just one letter, and -Rust’s type-naming convention is CamelCase. Short for _type_, `T` is the default -choice of most Rust programmers. - -When we use a parameter in the body of the function, we have to declare the -parameter name in the signature so the compiler knows what that name means. -Similarly, when we use a type parameter name in a function signature, we have -to declare the type parameter name before we use it. To define the generic -`largest` function, we place type name declarations inside angle brackets, -`<>`, between the name of the function and the parameter list, like this: +تابع `largest_i32` همان تابعی است که در لیست ۱۰-۳ استخراج کردیم و بزرگ‌ترین مقدار `i32` را در یک بخش پیدا می‌کند. تابع `largest_char` بزرگ‌ترین مقدار `char` را در یک بخش پیدا می‌کند. بدنه توابع دارای کد یکسانی هستند، بنابراین با معرفی یک پارامتر نوع جنریک در یک تابع واحد، تکرار را حذف می‌کنیم. + +برای پارامتری‌سازی نوع‌ها در یک تابع جدید، باید همان‌طور که برای پارامترهای مقداری (value parameters) نام مشخص می‌کنیم، +برای پارامتر نوع نیز یک نام تعیین کنیم. +می‌توانید از هر شناسه‌ای به عنوان نام پارامتر نوع استفاده کنید، +اما ما از `T` استفاده خواهیم کرد، چون طبق قرارداد، نام پارامترهای نوع در Rust کوتاه هستند— +اغلب تنها یک حرف—و همچنین طبق قرارداد نام‌گذاری نوع‌ها در Rust به‌صورت CamelCase نوشته می‌شوند. +`T` که مخفف *type* است، انتخاب پیش‌فرض اکثر برنامه‌نویسان Rust می‌باشد. + +وقتی از یک پارامتر در بدنه تابع استفاده می‌کنیم، باید نام پارامتر را در امضا اعلام کنیم تا کامپایلر بداند آن نام به چه معناست. به طور مشابه، وقتی از نام پارامتر نوع در امضای تابع استفاده می‌کنیم، باید نام پارامتر نوع را قبل از استفاده از آن اعلام کنیم. برای تعریف تابع جنریک `largest`، نام نوع‌ها را داخل پرانتزهای زاویه‌ای، `<>`، بین نام تابع و لیست پارامتر قرار می‌دهیم، مانند زیر: ```rust,ignore fn largest(list: &[T]) -> &T { ``` -We read this definition as: the function `largest` is generic over some type -`T`. This function has one parameter named `list`, which is a slice of values -of type `T`. The `largest` function will return a reference to a value of the -same type `T`. +این تعریف را به این صورت می‌خوانیم: تابع `largest` بر روی یک نوع `T` جنریک است. این تابع یک پارامتر به نام `list` دارد، که یک بخش از مقادیر نوع `T` است. تابع `largest` یک مرجع به مقداری از همان نوع `T` بازمی‌گرداند. -Listing 10-5 shows the combined `largest` function definition using the generic -data type in its signature. The listing also shows how we can call the function -with either a slice of `i32` values or `char` values. Note that this code won’t -compile yet. +لیستینگ 10-5 تعریف ترکیبی تابع `largest` را نشان می‌دهد که از نوع داده‌ی generic در امضای خود استفاده می‌کند. +این لیستینگ همچنین نشان می‌دهد که چگونه می‌توان این تابع را با یک `slice` از مقادیر `i32` یا مقادیر `char` فراخوانی کرد. +توجه داشته باشید که این کد هنوز قابل کامپایل نیست. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-05/src/main.rs}} @@ -65,30 +45,30 @@ compile yet. -If we compile this code right now, we’ll get this error: +اگر همین حالا این کد را کامپایل کنیم، این خطا را دریافت می‌کنیم: ```console {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-05/output.txt}} ``` -The help text mentions `std::cmp::PartialOrd`, which is a _trait_, and we’re -going to talk about traits in the next section. For now, know that this error -states that the body of `largest` won’t work for all possible types that `T` -could be. Because we want to compare values of type `T` in the body, we can -only use types whose values can be ordered. To enable comparisons, the standard -library has the `std::cmp::PartialOrd` trait that you can implement on types -(see Appendix C for more on this trait). To fix Listing 10-5, we can follow the -help text’s suggestion and restrict the types valid for `T` to only those that -implement `PartialOrd`. The listing will then compile, because the standard -library implements `PartialOrd` on both `i32` and `char`. +متن راهنمای خطا به `std::cmp::PartialOrd` اشاره می‌کند که یک *trait* است، +و ما در بخش بعدی درباره‌ی `trait`ها صحبت خواهیم کرد. +فعلاً بدانید که این خطا بیان می‌کند بدنه‌ی تابع `largest` برای همه‌ی نوع‌هایی که `T` می‌تواند باشد، +کار نخواهد کرد. +چون می‌خواهیم در بدنه‌ی تابع مقادیری از نوع `T` را با هم مقایسه کنیم، +تنها می‌توانیم از نوع‌هایی استفاده کنیم که مقادیر آن‌ها قابل مقایسه (ترتیب‌پذیر) باشند. +برای فعال کردن امکان مقایسه، کتابخانه‌ی استاندارد `trait`ای به نام `std::cmp::PartialOrd` دارد +که می‌توان آن را روی نوع‌ها پیاده‌سازی کرد +(برای اطلاعات بیشتر درباره‌ی این trait، به ضمیمه‌ی C مراجعه کنید). +برای اصلاح لیستینگ 10-5، می‌توانیم پیشنهاد متن خطا را دنبال کنیم +و نوع‌های معتبر برای `T` را تنها به آن‌هایی محدود کنیم که `PartialOrd` را پیاده‌سازی کرده‌اند. +در این صورت لیستینگ کامپایل خواهد شد، چون کتابخانه‌ی استاندارد `PartialOrd` را روی هر دو نوع `i32` و `char` پیاده‌سازی کرده است. -### In Struct Definitions +### در تعریف ساختارها (Struct) -We can also define structs to use a generic type parameter in one or more -fields using the `<>` syntax. Listing 10-6 defines a `Point` struct to hold -`x` and `y` coordinate values of any type. +ما می‌توانیم ساختارها را نیز به گونه‌ای تعریف کنیم که از یک پارامتر نوع جنریک در یک یا چند فیلد استفاده کنند، با استفاده از نحو `<>`. لیست ۱۰-۶ ساختار `Point` را تعریف می‌کند که مقادیر مختصات `x` و `y` از هر نوعی را نگه می‌دارد. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-06/src/main.rs}} @@ -96,19 +76,11 @@ fields using the `<>` syntax. Listing 10-6 defines a `Point` struct to hold -The syntax for using generics in struct definitions is similar to that used in -function definitions. First we declare the name of the type parameter inside -angle brackets just after the name of the struct. Then we use the generic -type in the struct definition where we would otherwise specify concrete data -types. +نحو استفاده از جنریک‌ها در تعریف ساختارها مشابه استفاده آن‌ها در تعریف توابع است. ابتدا نام پارامتر نوع را در داخل پرانتزهای زاویه‌ای بلافاصله پس از نام ساختار اعلام می‌کنیم. سپس نوع جنریک را در تعریف ساختار استفاده می‌کنیم، جایی که در غیر این صورت نوع داده مشخص را مشخص می‌کردیم. -Note that because we’ve used only one generic type to define `Point`, this -definition says that the `Point` struct is generic over some type `T`, and -the fields `x` and `y` are _both_ that same type, whatever that type may be. If -we create an instance of a `Point` that has values of different types, as in -Listing 10-7, our code won’t compile. +توجه داشته باشید که از آنجا که فقط یک نوع جنریک برای تعریف `Point` استفاده کرده‌ایم، این تعریف بیان می‌کند که ساختار `Point` برای یک نوع `T` جنریک است و فیلدهای `x` و `y` _هر دو_ از همان نوع هستند، هرچه که آن نوع باشد. اگر نمونه‌ای از `Point` ایجاد کنیم که مقادیر آن انواع مختلف داشته باشند، همانطور که در لیست ۱۰-۷ آمده است، کد ما کامپایل نخواهد شد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-07/src/main.rs}} @@ -116,21 +88,15 @@ Listing 10-7, our code won’t compile. -In this example, when we assign the integer value `5` to `x`, we let the -compiler know that the generic type `T` will be an integer for this instance of -`Point`. Then when we specify `4.0` for `y`, which we’ve defined to have the -same type as `x`, we’ll get a type mismatch error like this: +در این مثال، وقتی مقدار عدد صحیح `5` را به `x` اختصاص می‌دهیم، به کامپایلر اطلاع می‌دهیم که نوع جنریک `T` برای این نمونه از `Point` یک عدد صحیح خواهد بود. سپس وقتی `4.0` را برای `y` مشخص می‌کنیم، که تعریف کرده‌ایم همان نوع `x` را داشته باشد، یک خطای عدم تطابق نوع دریافت می‌کنیم، مانند این: ```console {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-07/output.txt}} ``` -To define a `Point` struct where `x` and `y` are both generics but could have -different types, we can use multiple generic type parameters. For example, in -Listing 10-8, we change the definition of `Point` to be generic over types `T` -and `U` where `x` is of type `T` and `y` is of type `U`. +برای تعریف یک ساختار `Point` که در آن `x` و `y` هر دو جنریک هستند اما می‌توانند انواع مختلفی داشته باشند، می‌توانیم از پارامترهای نوع جنریک چندگانه استفاده کنیم. برای مثال، در لیست ۱۰-۸، تعریف `Point` را تغییر می‌دهیم تا برای نوع‌های `T` و `U` جنریک باشد، جایی که `x` از نوع `T` و `y` از نوع `U` است. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-08/src/main.rs}} @@ -138,17 +104,11 @@ and `U` where `x` is of type `T` and `y` is of type `U`. -Now all the instances of `Point` shown are allowed! You can use as many generic -type parameters in a definition as you want, but using more than a few makes -your code hard to read. If you’re finding you need lots of generic types in -your code, it could indicate that your code needs restructuring into smaller -pieces. +حالا تمام نمونه‌های `Point` نشان داده شده معتبر هستند! شما می‌توانید به تعداد دلخواه پارامترهای نوع جنریک در یک تعریف استفاده کنید، اما استفاده از تعداد زیاد خوانایی کد شما را دشوار می‌کند. اگر می‌بینید که نیاز به انواع جنریک زیادی در کد خود دارید، ممکن است نشان‌دهنده این باشد که کد شما نیاز به ساختاربندی مجدد به بخش‌های کوچک‌تر دارد. -### In Enum Definitions +### در تعریف شمارش‌ها (Enum) -As we did with structs, we can define enums to hold generic data types in their -variants. Let’s take another look at the `Option` enum that the standard -library provides, which we used in Chapter 6: +همانطور که با ساختارها انجام دادیم، می‌توانیم شمارش‌ها را به گونه‌ای تعریف کنیم که نوع داده‌های جنریک را در حالت‌های خود نگه دارند. بیایید دوباره به شمارش `Option` که کتابخانه استاندارد ارائه می‌دهد و در فصل ۶ از آن استفاده کردیم نگاه کنیم: ```rust enum Option { @@ -157,15 +117,9 @@ enum Option { } ``` -This definition should now make more sense to you. As you can see, the -`Option` enum is generic over type `T` and has two variants: `Some`, which -holds one value of type `T`, and a `None` variant that doesn’t hold any value. -By using the `Option` enum, we can express the abstract concept of an -optional value, and because `Option` is generic, we can use this abstraction -no matter what the type of the optional value is. +این تعریف اکنون باید برای شما بیشتر معنا پیدا کند. همانطور که می‌بینید، شمارش `Option` بر روی نوع `T` جنریک است و دو حالت دارد: `Some` که یک مقدار از نوع `T` را نگه می‌دارد و حالت `None` که هیچ مقداری را نگه نمی‌دارد. با استفاده از شمارش `Option`، می‌توانیم مفهوم انتزاعی یک مقدار اختیاری را بیان کنیم، و از آنجا که `Option` جنریک است، می‌توانیم از این انتزاع بدون توجه به نوع مقدار اختیاری استفاده کنیم. -Enums can use multiple generic types as well. The definition of the `Result` -enum that we used in Chapter 9 is one example: +شمارش‌ها نیز می‌توانند از انواع جنریک چندگانه استفاده کنند. تعریف شمارش `Result` که در فصل ۹ استفاده کردیم یک مثال است: ```rust enum Result { @@ -174,26 +128,15 @@ enum Result { } ``` -The `Result` enum is generic over two types, `T` and `E`, and has two variants: -`Ok`, which holds a value of type `T`, and `Err`, which holds a value of type -`E`. This definition makes it convenient to use the `Result` enum anywhere we -have an operation that might succeed (return a value of some type `T`) or fail -(return an error of some type `E`). In fact, this is what we used to open a -file in Listing 9-3, where `T` was filled in with the type `std::fs::File` when -the file was opened successfully and `E` was filled in with the type -`std::io::Error` when there were problems opening the file. +شمارش `Result` بر روی دو نوع جنریک `T` و `E` است و دو حالت دارد: `Ok` که یک مقدار از نوع `T` نگه می‌دارد و `Err` که یک مقدار از نوع `E` نگه می‌دارد. این تعریف استفاده از شمارش `Result` را در هر جایی که یک عملیات ممکن است موفق شود (یک مقدار از نوع `T` بازگرداند) یا شکست بخورد (یک خطا از نوع `E` بازگرداند) آسان می‌کند. در واقع، این همان چیزی است که برای باز کردن یک فایل در لیست ۹-۳ استفاده کردیم، جایی که `T` با نوع `std::fs::File` پر شده بود وقتی فایل با موفقیت باز شد و `E` با نوع `std::io::Error` پر شده بود وقتی مشکلاتی در باز کردن فایل وجود داشت. -When you recognize situations in your code with multiple struct or enum -definitions that differ only in the types of the values they hold, you can -avoid duplication by using generic types instead. +وقتی وضعیت‌هایی در کد خود را شناسایی کردید که چندین تعریف ساختار یا شمارش وجود دارد که فقط در نوع مقادیر نگهداری شده متفاوت هستند، می‌توانید با استفاده از نوع‌های جنریک از تکرار جلوگیری کنید. -### In Method Definitions +### در تعریف متدها -We can implement methods on structs and enums (as we did in Chapter 5) and use -generic types in their definitions too. Listing 10-9 shows the `Point` -struct we defined in Listing 10-6 with a method named `x` implemented on it. +ما می‌توانیم متدهایی را روی ساختارها و شمارش‌ها پیاده‌سازی کنیم (همانطور که در فصل ۵ انجام دادیم) و از انواع جنریک در تعریف آن‌ها نیز استفاده کنیم. لیست ۱۰-۹ ساختار `Point` که در لیست ۱۰-۶ تعریف کردیم را نشان می‌دهد، با متدی به نام `x` که روی آن پیاده‌سازی شده است. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-09/src/main.rs}} @@ -201,25 +144,13 @@ struct we defined in Listing 10-6 with a method named `x` implemented on it. -Here, we’ve defined a method named `x` on `Point` that returns a reference -to the data in the field `x`. +در اینجا، یک متد به نام `x` روی `Point` تعریف کرده‌ایم که یک مرجع به داده موجود در فیلد `x` بازمی‌گرداند. -Note that we have to declare `T` just after `impl` so we can use `T` to specify -that we’re implementing methods on the type `Point`. By declaring `T` as a -generic type after `impl`, Rust can identify that the type in the angle -brackets in `Point` is a generic type rather than a concrete type. We could -have chosen a different name for this generic parameter than the generic -parameter declared in the struct definition, but using the same name is -conventional. If you write a method within an `impl` that declares a generic -type, that method will be defined on any instance of the type, no matter what -concrete type ends up substituting for the generic type. +توجه داشته باشید که باید `T` را بلافاصله بعد از `impl` اعلام کنیم تا بتوانیم از `T` برای مشخص کردن اینکه داریم متدها را روی نوع `Point` پیاده‌سازی می‌کنیم، استفاده کنیم. با اعلام `T` به عنوان یک نوع جنریک بعد از `impl`، Rust می‌تواند تشخیص دهد که نوع موجود در پرانتزهای زاویه‌ای در `Point` یک نوع جنریک است، نه یک نوع مشخص. می‌توانستیم نامی متفاوت از پارامتر جنریک اعلام‌شده در تعریف ساختار برای این پارامتر جنریک انتخاب کنیم، اما استفاده از همان نام یک عرف است. اگر یک متد را درون یک `impl` که یک نوع جنریک اعلام می‌کند بنویسید، آن متد روی هر نمونه‌ای از آن نوع تعریف می‌شود، بدون توجه به اینکه چه نوع مشخصی جایگزین نوع جنریک می‌شود. -We can also specify constraints on generic types when defining methods on the -type. We could, for example, implement methods only on `Point` instances -rather than on `Point` instances with any generic type. In Listing 10-10 we -use the concrete type `f32`, meaning we don’t declare any types after `impl`. +همچنین می‌توانیم محدودیت‌هایی بر روی نوع‌های جنریک هنگام تعریف متدها روی یک نوع مشخص کنیم. می‌توانیم، برای مثال، متدهایی را فقط روی نمونه‌های `Point` پیاده‌سازی کنیم، نه روی نمونه‌های `Point` با هر نوع جنریک. در لیست ۱۰-۱۰ از نوع مشخص `f32` استفاده کرده‌ایم، به این معنی که هیچ نوعی را بعد از `impl` اعلام نمی‌کنیم. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-10/src/main.rs:here}} @@ -227,20 +158,11 @@ use the concrete type `f32`, meaning we don’t declare any types after `impl`. -This code means the type `Point` will have a `distance_from_origin` -method; other instances of `Point` where `T` is not of type `f32` will not -have this method defined. The method measures how far our point is from the -point at coordinates (0.0, 0.0) and uses mathematical operations that are -available only for floating-point types. +این کد به این معنی است که نوع `Point` دارای یک متد `distance_from_origin` خواهد بود؛ سایر نمونه‌های `Point` که `T` از نوع `f32` نیستند، این متد را تعریف نخواهند کرد. این متد فاصله نقطه ما از نقطه‌ای با مختصات (0.0, 0.0) را اندازه‌گیری می‌کند و از عملیات ریاضی استفاده می‌کند که فقط برای نوع‌های اعداد اعشاری در دسترس هستند. -Generic type parameters in a struct definition aren’t always the same as those -you use in that same struct’s method signatures. Listing 10-11 uses the generic -types `X1` and `Y1` for the `Point` struct and `X2` `Y2` for the `mixup` method -signature to make the example clearer. The method creates a new `Point` -instance with the `x` value from the `self` `Point` (of type `X1`) and the `y` -value from the passed-in `Point` (of type `Y2`). +پارامترهای نوع جنریک در تعریف یک ساختار همیشه با آن‌هایی که در امضاهای متد همان ساختار استفاده می‌شوند یکسان نیستند. لیست ۱۰-۱۱ از نوع‌های جنریک `X1` و `Y1` برای ساختار `Point` و `X2` و `Y2` برای امضای متد `mixup` استفاده می‌کند تا مثال را واضح‌تر کند. این متد یک نمونه جدید از `Point` ایجاد می‌کند با مقدار `x` از `Point` `self` (از نوع `X1`) و مقدار `y` از `Point` پاس‌داده‌شده (از نوع `Y2`). -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-11/src/main.rs}} @@ -248,52 +170,26 @@ value from the passed-in `Point` (of type `Y2`). -In `main`, we’ve defined a `Point` that has an `i32` for `x` (with value `5`) -and an `f64` for `y` (with value `10.4`). The `p2` variable is a `Point` struct -that has a string slice for `x` (with value `"Hello"`) and a `char` for `y` -(with value `c`). Calling `mixup` on `p1` with the argument `p2` gives us `p3`, -which will have an `i32` for `x` because `x` came from `p1`. The `p3` variable -will have a `char` for `y` because `y` came from `p2`. The `println!` macro -call will print `p3.x = 5, p3.y = c`. - -The purpose of this example is to demonstrate a situation in which some generic -parameters are declared with `impl` and some are declared with the method -definition. Here, the generic parameters `X1` and `Y1` are declared after -`impl` because they go with the struct definition. The generic parameters `X2` -and `Y2` are declared after `fn mixup` because they’re only relevant to the -method. - -### Performance of Code Using Generics - -You might be wondering whether there is a runtime cost when using generic type -parameters. The good news is that using generic types won’t make your program -run any slower than it would with concrete types. - -Rust accomplishes this by performing monomorphization of the code using -generics at compile time. _Monomorphization_ is the process of turning generic -code into specific code by filling in the concrete types that are used when -compiled. In this process, the compiler does the opposite of the steps we used -to create the generic function in Listing 10-5: the compiler looks at all the -places where generic code is called and generates code for the concrete types -the generic code is called with. - -Let’s look at how this works by using the standard library’s generic -`Option` enum: +در تابع `main`، یک `Point` تعریف کرده‌ایم که `x` آن یک `i32` (با مقدار `5`) و `y` آن یک `f64` (با مقدار `10.4`) است. متغیر `p2` یک ساختار `Point` است که `x` آن یک قطعه رشته (با مقدار `"Hello"`) و `y` آن یک `char` (با مقدار `c`) است. فراخوانی `mixup` روی `p1` با آرگومان `p2` به ما `p3` را می‌دهد، که `x` آن یک `i32` خواهد بود زیرا `x` از `p1` آمده است. متغیر `p3` یک `char` برای `y` خواهد داشت زیرا `y` از `p2` آمده است. فراخوانی ماکرو `println!` مقدار `p3.x = 5, p3.y = c` را چاپ می‌کند. + +هدف این مثال این است که وضعیتی را نشان دهد که در آن برخی پارامترهای جنریک با `impl` اعلام می‌شوند و برخی دیگر با تعریف متد اعلام می‌شوند. در اینجا، پارامترهای جنریک `X1` و `Y1` بعد از `impl` اعلام شده‌اند زیرا با تعریف ساختار همراه هستند. پارامترهای جنریک `X2` و `Y2` بعد از `fn mixup` اعلام شده‌اند زیرا فقط به متد مربوط هستند. + +### عملکرد کدی که از جنریک‌ها استفاده می‌کند + +ممکن است این سوال برای شما پیش بیاید که آیا هنگام استفاده از پارامترهای نوع جنریک، هزینه‌ای در زمان اجرا وجود دارد یا خیر. خبر خوب این است که استفاده از انواع جنریک برنامه شما را کندتر از حالتی که از انواع مشخص استفاده می‌کردید، نمی‌کند. + +Rust این کار را با انجام فرآیندی به نام _تک‌ریخت‌سازی_ (monomorphization) روی کدی که از جنریک‌ها استفاده می‌کند در زمان کامپایل انجام می‌دهد. _تک‌ریخت‌سازی_ فرآیند تبدیل کد جنریک به کد مشخص است با پر کردن انواع مشخصی که هنگام کامپایل استفاده می‌شوند. در این فرآیند، کامپایلر برعکس مراحلی که برای ایجاد تابع جنریک در لیست ۱۰-۵ استفاده کردیم را انجام می‌دهد: کامپایلر به تمام جاهایی که کد جنریک فراخوانی شده نگاه می‌کند و کدی را برای انواع مشخصی که کد جنریک با آن‌ها فراخوانی شده ایجاد می‌کند. + +بیایید ببینیم این کار چگونه انجام می‌شود با استفاده از شمارش جنریک `Option` در کتابخانه استاندارد: ```rust let integer = Some(5); let float = Some(5.0); ``` -When Rust compiles this code, it performs monomorphization. During that -process, the compiler reads the values that have been used in `Option` -instances and identifies two kinds of `Option`: one is `i32` and the other -is `f64`. As such, it expands the generic definition of `Option` into two -definitions specialized to `i32` and `f64`, thereby replacing the generic -definition with the specific ones. +وقتی Rust این کد را کامپایل می‌کند، فرآیند تک‌ریخت‌سازی را انجام می‌دهد. در طول این فرآیند، کامپایلر مقادیر استفاده شده در نمونه‌های `Option` را می‌خواند و دو نوع `Option` را شناسایی می‌کند: یکی `i32` و دیگری `f64`. به این ترتیب، تعریف جنریک `Option` را به دو تعریف ویژه برای `i32` و `f64` گسترش می‌دهد و بنابراین تعریف جنریک را با تعریف‌های مشخص جایگزین می‌کند. -The monomorphized version of the code looks similar to the following (the -compiler uses different names than what we’re using here for illustration): +نسخه تک‌ریخت‌سازی شده کد شبیه به چیزی به نظر می‌رسد (کامپایلر از نام‌های متفاوتی استفاده می‌کند، اما برای توضیح از این نام‌ها استفاده کرده‌ایم): @@ -316,9 +212,4 @@ fn main() { -The generic `Option` is replaced with the specific definitions created by -the compiler. Because Rust compiles generic code into code that specifies the -type in each instance, we pay no runtime cost for using generics. When the code -runs, it performs just as it would if we had duplicated each definition by -hand. The process of monomorphization makes Rust’s generics extremely efficient -at runtime. +شمارش جنریک `Option` با تعریف‌های مشخص ایجاد شده توسط کامپایلر جایگزین شده است. از آنجا که Rust کد جنریک را به کدی که نوع را در هر نمونه مشخص می‌کند کامپایل می‌کند، هیچ هزینه‌ای در زمان اجرا برای استفاده از جنریک‌ها پرداخت نمی‌کنیم. وقتی کد اجرا می‌شود، دقیقاً همان‌طور عمل می‌کند که اگر هر تعریف را به صورت دستی تکرار کرده بودیم. فرآیند تک‌ریخت‌سازی جنریک‌های Rust را در زمان اجرا بسیار کارآمد می‌کند. diff --git a/src/ch10-02-traits.md b/src/ch10-02-traits.md index e853e9d757..153e6fe6b8 100644 --- a/src/ch10-02-traits.md +++ b/src/ch10-02-traits.md @@ -1,34 +1,25 @@ -## Traits: Defining Shared Behavior +## ویژگی‌ها (Traits): تعریف رفتار مشترک -A _trait_ defines the functionality a particular type has and can share with -other types. We can use traits to define shared behavior in an abstract way. We -can use _trait bounds_ to specify that a generic type can be any type that has -certain behavior. +یک _ویژگی_ (trait) عملکردی را که یک نوع خاص دارد تعریف می‌کند و می‌تواند با انواع دیگر به اشتراک بگذارد. ما می‌توانیم از traitها برای تعریف رفتار مشترک به صورت انتزاعی استفاده کنیم. همچنین می‌توانیم از _محدودیت‌های ویژگی_ (trait bounds) برای مشخص کردن اینکه یک نوع جنریک می‌تواند هر نوعی باشد که رفتار خاصی دارد، استفاده کنیم. -> Note: Traits are similar to a feature often called _interfaces_ in other -> languages, although with some differences. +> توجه: ویژگی‌ها شبیه به مفهومی هستند که اغلب در زبان‌های دیگر به نام _interfaces_ شناخته می‌شود، البته با برخی تفاوت‌ها. -### Defining a Trait +### تعریف یک trait -A type’s behavior consists of the methods we can call on that type. Different -types share the same behavior if we can call the same methods on all of those -types. Trait definitions are a way to group method signatures together to -define a set of behaviors necessary to accomplish some purpose. +رفتار یک نوع شامل متدهایی است که می‌توانیم روی آن نوع فراخوانی کنیم. انواع مختلف یک رفتار مشترک دارند اگر بتوانیم همان متدها را روی تمام آن انواع فراخوانی کنیم. تعریف ویژگی‌ها راهی برای گروه‌بندی امضاهای متدها با هم است تا مجموعه‌ای از رفتارها را که برای دستیابی به یک هدف خاص ضروری است، تعریف کنیم. -For example, let’s say we have multiple structs that hold various kinds and -amounts of text: a `NewsArticle` struct that holds a news story filed in a -particular location and a `SocialPost` that can have, at most, 280 characters -along with metadata that indicates whether it was a new post, a repost, or a -reply to another post. +برای مثال، فرض کنید چندین `struct` داریم که انواع مختلفی از متن با اندازه‌های متفاوت را نگهداری می‌کنند: +یک ساختار `NewsArticle` که یک خبر را در مکان خاصی نگهداری می‌کند، +و یک `SocialPost` که حداکثر می‌تواند ۲۸۰ کاراکتر داشته باشد +به‌همراه متاداده‌ای که مشخص می‌کند آیا پست جدید، بازنشر (repost)، یا پاسخ به پست دیگری بوده است. -We want to make a media aggregator library crate named `aggregator` that can -display summaries of data that might be stored in a `NewsArticle` or -`SocialPost` instance. To do this, we need a summary from each type, and we’ll -request that summary by calling a `summarize` method on an instance. Listing -10-12 shows the definition of a public `Summary` trait that expresses this -behavior. +ما می‌خواهیم یک crate کتابخانه‌ای برای جمع‌آوری رسانه‌ها به نام `aggregator` بسازیم +که بتواند خلاصه‌هایی از داده‌هایی که ممکن است در نمونه‌هایی از `NewsArticle` یا `SocialPost` ذخیره شده باشند را نمایش دهد. +برای انجام این کار، به یک خلاصه از هر نوع نیاز داریم، +و این خلاصه را با فراخوانی متد `summarize` روی یک نمونه درخواست خواهیم کرد. +لیستینگ 10-12 تعریف یک trait عمومی به نام `Summary` را نشان می‌دهد که این رفتار را بیان می‌کند. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-12/src/lib.rs}} @@ -36,33 +27,22 @@ behavior. -Here, we declare a trait using the `trait` keyword and then the trait’s name, -which is `Summary` in this case. We also declare the trait as `pub` so that -crates depending on this crate can make use of this trait too, as we’ll see in -a few examples. Inside the curly brackets, we declare the method signatures -that describe the behaviors of the types that implement this trait, which in -this case is `fn summarize(&self) -> String`. +در اینجا، یک ویژگی با استفاده از کلیدواژه `trait` و سپس نام ویژگی، که در اینجا `Summary` است، اعلام می‌کنیم. همچنین ویژگی را به عنوان `pub` اعلام می‌کنیم تا کرایت‌هایی که به این کرایت وابسته هستند نیز بتوانند از این ویژگی استفاده کنند، همانطور که در چند مثال خواهیم دید. در داخل آکولادها، امضاهای متدی را اعلام می‌کنیم که رفتارهای نوع‌هایی که این ویژگی را پیاده‌سازی می‌کنند توصیف می‌کنند، که در این مورد `fn summarize(&self) -> String` است. -After the method signature, instead of providing an implementation within curly -brackets, we use a semicolon. Each type implementing this trait must provide -its own custom behavior for the body of the method. The compiler will enforce -that any type that has the `Summary` trait will have the method `summarize` -defined with this signature exactly. +بعد از امضای متد، به جای ارائه یک پیاده‌سازی در داخل آکولادها، از یک نقطه‌ویرگول استفاده می‌کنیم. هر نوعی که این ویژگی را پیاده‌سازی می‌کند باید رفتار سفارشی خود را برای بدنه متد ارائه دهد. کامپایلر اطمینان خواهد داد که هر نوعی که ویژگی `Summary` را دارد، متد `summarize` را دقیقاً با این امضا تعریف خواهد کرد. -A trait can have multiple methods in its body: the method signatures are listed -one per line, and each line ends in a semicolon. +یک ویژگی می‌تواند چندین متد در بدنه خود داشته باشد: امضاهای متدها به صورت یک خط در هر خط فهرست می‌شوند و هر خط با یک نقطه‌ویرگول پایان می‌یابد. -### Implementing a Trait on a Type +### پیاده‌سازی یک ویژگی (trait) روی یک نوع -Now that we’ve defined the desired signatures of the `Summary` trait’s methods, -we can implement it on the types in our media aggregator. Listing 10-13 shows -an implementation of the `Summary` trait on the `NewsArticle` struct that uses -the headline, the author, and the location to create the return value of -`summarize`. For the `SocialPost` struct, we define `summarize` as the username -followed by the entire text of the post, assuming that the post content is -already limited to 280 characters. +حالا که امضاهای مورد نظر برای متدهای trait به نام `Summary` را تعریف کرده‌ایم، +می‌توانیم آن را روی نوع‌های موجود در گردآورنده‌ی رسانه‌ای‌مان پیاده‌سازی کنیم. +لیستینگ 10-13 پیاده‌سازی trait `Summary` را روی `struct`ای به نام `NewsArticle` نشان می‌دهد، +که از عنوان (headline)، نویسنده (author)، و مکان (location) برای ایجاد مقدار بازگشتی متد `summarize` استفاده می‌کند. +برای ساختار `SocialPost`، متد `summarize` را به‌گونه‌ای تعریف می‌کنیم که ابتدا نام کاربری بیاید +و سپس تمام متن پست نمایش داده شود، با این فرض که محتوای پست از پیش به ۲۸۰ کاراکتر محدود شده است. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-13/src/lib.rs:here}} @@ -70,60 +50,36 @@ already limited to 280 characters. -Implementing a trait on a type is similar to implementing regular methods. The -difference is that after `impl`, we put the trait name we want to implement, -then use the `for` keyword, and then specify the name of the type we want to -implement the trait for. Within the `impl` block, we put the method signatures -that the trait definition has defined. Instead of adding a semicolon after each -signature, we use curly brackets and fill in the method body with the specific -behavior that we want the methods of the trait to have for the particular type. - -Now that the library has implemented the `Summary` trait on `NewsArticle` and -`SocialPost`, users of the crate can call the trait methods on instances of -`NewsArticle` and `SocialPost` in the same way we call regular methods. The only -difference is that the user must bring the trait into scope as well as the -types. Here’s an example of how a binary crate could use our `aggregator` -library crate: +پیاده‌سازی یک ویژگی روی یک نوع مشابه پیاده‌سازی متدهای معمولی است. تفاوت این است که بعد از `impl`، نام ویژگی‌ای که می‌خواهیم پیاده‌سازی کنیم را قرار می‌دهیم، سپس از کلمه کلیدی `for` استفاده می‌کنیم و سپس نام نوعی که می‌خواهیم ویژگی را برای آن پیاده‌سازی کنیم مشخص می‌کنیم. درون بلوک `impl`، امضاهای متدی که تعریف ویژگی مشخص کرده‌اند را قرار می‌دهیم. به جای اضافه کردن یک نقطه‌ویرگول بعد از هر امضا، از آکولادها استفاده می‌کنیم و بدنه متد را با رفتار خاصی که می‌خواهیم متدهای ویژگی برای نوع خاص داشته باشند پر می‌کنیم. + +حالا که کتابخانه ویژگی `Summary` را روی `NewsArticle` و `Tweet` پیاده‌سازی کرده است، کاربران این کرایت می‌توانند متدهای ویژگی را روی نمونه‌های `NewsArticle` و `Tweet` فراخوانی کنند، به همان روشی که متدهای معمولی را فراخوانی می‌کنیم. تنها تفاوت این است که کاربر باید ویژگی را به همراه نوع‌ها به محدوده وارد کند. در اینجا مثالی از اینکه چگونه یک کرایت باینری می‌تواند از کرایت کتابخانه `aggregator` ما استفاده کند آورده شده است: ```rust,ignore {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-01-calling-trait-method/src/main.rs}} ``` -This code prints `1 new post: horse_ebooks: of course, as you probably already -know, people`. - -Other crates that depend on the `aggregator` crate can also bring the `Summary` -trait into scope to implement `Summary` on their own types. One restriction to -note is that we can implement a trait on a type only if either the trait or the -type, or both, are local to our crate. For example, we can implement standard -library traits like `Display` on a custom type like `SocialPost` as part of our -`aggregator` crate functionality because the type `SocialPost` is local to our -`aggregator` crate. We can also implement `Summary` on `Vec` in our -`aggregator` crate because the trait `Summary` is local to our `aggregator` -crate. - -But we can’t implement external traits on external types. For example, we can’t -implement the `Display` trait on `Vec` within our `aggregator` crate because -`Display` and `Vec` are both defined in the standard library and aren’t -local to our `aggregator` crate. This restriction is part of a property called -_coherence_, and more specifically the _orphan rule_, so named because the -parent type is not present. This rule ensures that other people’s code can’t -break your code and vice versa. Without the rule, two crates could implement -the same trait for the same type, and Rust wouldn’t know which implementation -to use. - -### Default Implementations - -Sometimes it’s useful to have default behavior for some or all of the methods -in a trait instead of requiring implementations for all methods on every type. -Then, as we implement the trait on a particular type, we can keep or override -each method’s default behavior. - -In Listing 10-14, we specify a default string for the `summarize` method of the -`Summary` trait instead of only defining the method signature, as we did in -Listing 10-12. - -+این کد مقدار زیر را چاپ می‌کند: + +`1 new post: horse_ebooks: of course, as you probably already know, people` + +سایر crateهایی که به crate `aggregator` وابسته هستند نیز می‌توانند trait به نام `Summary` را وارد حوزه کنند +و آن را روی نوع‌های خودشان پیاده‌سازی نمایند. +یک محدودیت مهم این است که تنها زمانی می‌توانیم یک trait را روی یک نوع پیاده‌سازی کنیم +که یا trait یا نوع، یا هر دو، در crate ما محلی (local) باشند. +برای مثال، می‌توانیم traitهای کتابخانه‌ی استاندارد مانند `Display` را روی یک نوع سفارشی مانند `SocialPost` پیاده‌سازی کنیم +زیرا نوع `SocialPost` در crate `aggregator` محلی است. +همچنین می‌توانیم trait `Summary` را روی `Vec` در crate `aggregator` پیاده‌سازی کنیم +چون trait `Summary` در crate ما محلی است. + +اما نمی‌توانیم ویژگی‌های خارجی را روی نوع‌های خارجی پیاده‌سازی کنیم. برای مثال، نمی‌توانیم ویژگی `Display` را روی `Vec` در کرایت `aggregator` پیاده‌سازی کنیم زیرا `Display` و `Vec` هر دو در کتابخانه استاندارد تعریف شده‌اند و به کرایت `aggregator` محلی نیستند. این محدودیت بخشی از خاصیتی به نام _انسجام_ (coherence) و به طور خاص‌تر _قانون یتیم_ (orphan rule) است، که به این دلیل نامگذاری شده است که نوع والد وجود ندارد. این قانون اطمینان می‌دهد که کد دیگران نمی‌تواند کد شما را خراب کند و برعکس. بدون این قانون، دو کرایت می‌توانستند همان ویژگی را برای همان نوع پیاده‌سازی کنند و Rust نمی‌دانست کدام پیاده‌سازی را استفاده کند. + +### پیاده‌سازی‌های پیش‌فرض + +گاهی اوقات مفید است که رفتار پیش‌فرضی برای برخی یا همه متدهای یک ویژگی داشته باشید به جای اینکه پیاده‌سازی‌ها برای تمام متدها در هر نوع اجباری باشند. سپس، وقتی ویژگی را روی یک نوع خاص پیاده‌سازی می‌کنیم، می‌توانیم رفتار پیش‌فرض هر متد را نگه داریم یا جایگزین کنیم. + +در لیست ۱۰-۱۴، یک رشته پیش‌فرض برای متد `summarize` ویژگی `Summary` مشخص می‌کنیم به جای اینکه فقط امضای متد را تعریف کنیم، همانطور که در لیست ۱۰-۱۲ انجام دادیم. + + ```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-14/src/lib.rs:here}} @@ -131,90 +87,79 @@ Listing 10-12. -To use a default implementation to summarize instances of `NewsArticle`, we -specify an empty `impl` block with `impl Summary for NewsArticle {}`. +برای استفاده از یک پیاده‌سازی پیش‌فرض برای خلاصه کردن نمونه‌های `NewsArticle`، یک بلوک `impl` خالی با `impl Summary for NewsArticle {}` مشخص می‌کنیم. + +اگرچه دیگر متد `summarize` را مستقیماً روی `NewsArticle` تعریف نمی‌کنیم، یک پیاده‌سازی پیش‌فرض ارائه داده‌ایم و مشخص کرده‌ایم که `NewsArticle` ویژگی `Summary` را پیاده‌سازی می‌کند. در نتیجه، همچنان می‌توانیم متد `summarize` را روی یک نمونه از `NewsArticle` فراخوانی کنیم، مانند این: -Even though we’re no longer defining the `summarize` method on `NewsArticle` -directly, we’ve provided a default implementation and specified that -`NewsArticle` implements the `Summary` trait. As a result, we can still call -the `summarize` method on an instance of `NewsArticle`, like this: ```rust,ignore {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-02-calling-default-impl/src/main.rs:here}} ``` -This code prints `New article available! (Read more...)`. +این کد `New article available! (Read more...)` را چاپ می‌کند. -Creating a default implementation doesn’t require us to change anything about -the implementation of `Summary` on `SocialPost` in Listing 10-13. The reason is -that the syntax for overriding a default implementation is the same as the -syntax for implementing a trait method that doesn’t have a default -implementation. +ایجاد یک پیاده‌سازی پیش‌فرض (default) نیازی به تغییر در پیاده‌سازی trait `Summary` برای `SocialPost` در لیستینگ 10-13 ندارد. +دلیل آن این است که سینتکس بازنویسی (override) یک پیاده‌سازی پیش‌فرض، +دقیقاً همان سینتکسی است که برای پیاده‌سازی یک متد از trait که پیاده‌سازی پیش‌فرض ندارد استفاده می‌شود. -Default implementations can call other methods in the same trait, even if those -other methods don’t have a default implementation. In this way, a trait can -provide a lot of useful functionality and only require implementors to specify -a small part of it. For example, we could define the `Summary` trait to have a -`summarize_author` method whose implementation is required, and then define a -`summarize` method that has a default implementation that calls the -`summarize_author` method: +پیاده‌سازی‌های پیش‌فرض می‌توانند متدهای دیگر را در همان ویژگی فراخوانی کنند، حتی اگر آن متدهای دیگر پیاده‌سازی پیش‌فرض نداشته باشند. به این روش، یک ویژگی می‌تواند مقدار زیادی عملکرد مفید ارائه دهد و فقط از پیاده‌سازان بخواهد که بخشی از آن را مشخص کنند. برای مثال، می‌توانیم ویژگی `Summary` را به گونه‌ای تعریف کنیم که یک متد `summarize_author` داشته باشد که پیاده‌سازی آن الزامی است و سپس یک متد `summarize` تعریف کنیم که یک پیاده‌سازی پیش‌فرض دارد و متد `summarize_author` را فراخوانی می‌کند: ```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-03-default-impl-calls-other-methods/src/lib.rs:here}} ``` -To use this version of `Summary`, we only need to define `summarize_author` -when we implement the trait on a type: +برای استفاده از این نسخه از `Summary`، فقط باید `summarize_author` را هنگامی که ویژگی را روی یک نوع پیاده‌سازی می‌کنیم، تعریف کنیم: ```rust,ignore {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-03-default-impl-calls-other-methods/src/lib.rs:impl}} ``` -After we define `summarize_author`, we can call `summarize` on instances of the -`SocialPost` struct, and the default implementation of `summarize` will call the -definition of `summarize_author` that we’ve provided. Because we’ve implemented -`summarize_author`, the `Summary` trait has given us the behavior of the -`summarize` method without requiring us to write any more code. Here’s what -that looks like: +پس از این‌که `summarize_author` را تعریف کردیم، می‌توانیم متد `summarize` را روی نمونه‌هایی از `struct` به نام `SocialPost` فراخوانی کنیم، +و پیاده‌سازی پیش‌فرض متد `summarize`، از پیاده‌سازی‌ای که برای `summarize_author` ارائه داده‌ایم استفاده خواهد کرد. +از آن‌جا که ما `summarize_author` را پیاده‌سازی کرده‌ایم، trait به نام `Summary` +رفتار متد `summarize` را بدون نیاز به نوشتن کد اضافی در اختیار ما قرار داده است. +در اینجا نمونه‌ای از این وضعیت آمده است: ```rust,ignore {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-03-default-impl-calls-other-methods/src/main.rs:here}} ``` -This code prints `1 new post: (Read more from @horse_ebooks...)`. +این کد مقدار زیر را چاپ می‌کند: +```1 new post: (Read more from @horse_ebooks...)``` -Note that it isn’t possible to call the default implementation from an -overriding implementation of that same method. +توجه داشته باشید که امکان فراخوانی پیاده‌سازی پیش‌فرض از یک پیاده‌سازی بازنویسی شده از همان متد وجود ندارد. -### Traits as Parameters +### ویژگی‌ها (traits) به عنوان پارامترها -Now that you know how to define and implement traits, we can explore how to use -traits to define functions that accept many different types. We’ll use the -`Summary` trait we implemented on the `NewsArticle` and `SocialPost` types in -Listing 10-13 to define a `notify` function that calls the `summarize` method -on its `item` parameter, which is of some type that implements the `Summary` -trait. To do this, we use the `impl Trait` syntax, like this: +حالا که می‌دانید چگونه یک trait را تعریف و پیاده‌سازی کنید، +می‌توانیم بررسی کنیم که چگونه از traitها برای تعریف توابعی استفاده کنیم +که انواع مختلفی را به‌عنوان پارامتر بپذیرند. +ما از trait `Summary` که روی نوع‌های `NewsArticle` و `SocialPost` در لیستینگ 10-13 پیاده‌سازی کردیم، +استفاده خواهیم کرد تا تابعی به نام `notify` تعریف کنیم +که متد `summarize` را روی پارامتر `item` خود فراخوانی می‌کند— +پارامتری که از نوعی است که trait `Summary` را پیاده‌سازی کرده باشد. +برای این کار، از سینتکس `impl Trait` استفاده می‌کنیم، به این صورت: ```rust,ignore {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-04-traits-as-parameters/src/lib.rs:here}} ``` -Instead of a concrete type for the `item` parameter, we specify the `impl` -keyword and the trait name. This parameter accepts any type that implements the -specified trait. In the body of `notify`, we can call any methods on `item` -that come from the `Summary` trait, such as `summarize`. We can call `notify` -and pass in any instance of `NewsArticle` or `SocialPost`. Code that calls the -function with any other type, such as a `String` or an `i32`, won’t compile -because those types don’t implement `Summary`. +به جای استفاده از یک نوع مشخص برای پارامتر `item`، +از کلیدواژه‌ی `impl` و نام trait استفاده می‌کنیم. +این پارامتر هر نوعی را می‌پذیرد که trait مشخص‌شده را پیاده‌سازی کرده باشد. +در بدنه‌ی تابع `notify`، می‌توانیم هر متدی از trait `Summary` را روی `item` فراخوانی کنیم، +مانند متد `summarize`. +می‌توانیم `notify` را فراخوانی کرده و هر نمونه‌ای از `NewsArticle` یا `SocialPost` را به آن پاس دهیم. +کدی که تابع را با نوعی دیگر، مانند `String` یا `i32`، فراخوانی کند کامپایل نخواهد شد، +زیرا این نوع‌ها trait `Summary` را پیاده‌سازی نکرده‌اند. -#### Trait Bound Syntax +#### نحو محدودیت ویژگی (Trait Bound Syntax) -The `impl Trait` syntax works for straightforward cases but is actually syntax -sugar for a longer form known as a _trait bound_; it looks like this: +نحو `impl Trait` برای موارد ساده مناسب است اما در واقع یک شکل کوتاه‌شده از یک فرم طولانی‌تر به نام _محدودیت ویژگی_ (trait bound) است؛ به این صورت: ```rust,ignore pub fn notify(item: &T) { @@ -222,125 +167,85 @@ pub fn notify(item: &T) { } ``` -This longer form is equivalent to the example in the previous section but is -more verbose. We place trait bounds with the declaration of the generic type -parameter after a colon and inside angle brackets. +این فرم طولانی معادل مثال بخش قبلی است اما مفصل‌تر است. ما محدودیت‌های ویژگی را با اعلام پارامتر نوع جنریک بعد از یک دو‌نقطه و داخل پرانتزهای زاویه‌ای قرار می‌دهیم. -The `impl Trait` syntax is convenient and makes for more concise code in simple -cases, while the fuller trait bound syntax can express more complexity in other -cases. For example, we can have two parameters that implement `Summary`. Doing -so with the `impl Trait` syntax looks like this: +نحو `impl Trait` در موارد ساده مناسب است و کد را مختصرتر می‌کند، در حالی که نحو کامل‌تر محدودیت ویژگی می‌تواند پیچیدگی بیشتری را در موارد دیگر بیان کند. برای مثال، می‌توانیم دو پارامتر داشته باشیم که ویژگی `Summary` را پیاده‌سازی می‌کنند. انجام این کار با نحو `impl Trait` به این صورت است: ```rust,ignore pub fn notify(item1: &impl Summary, item2: &impl Summary) { ``` -Using `impl Trait` is appropriate if we want this function to allow `item1` and -`item2` to have different types (as long as both types implement `Summary`). If -we want to force both parameters to have the same type, however, we must use a -trait bound, like this: +استفاده از `impl Trait` مناسب است اگر بخواهیم این تابع اجازه دهد `item1` و `item2` انواع مختلفی داشته باشند (به شرطی که هر دو نوع ویژگی `Summary` را پیاده‌سازی کنند). اما اگر بخواهیم هر دو پارامتر یک نوع یکسان داشته باشند، باید از محدودیت ویژگی استفاده کنیم، مانند این: ```rust,ignore pub fn notify(item1: &T, item2: &T) { ``` -The generic type `T` specified as the type of the `item1` and `item2` -parameters constrains the function such that the concrete type of the value -passed as an argument for `item1` and `item2` must be the same. +نوع جنریک `T` که به عنوان نوع پارامترهای `item1` و `item2` مشخص شده است، تابع را محدود می‌کند به این صورت که نوع مشخص مقدار پاس‌داده‌شده به عنوان آرگومان برای `item1` و `item2` باید یکسان باشد. -#### Specifying Multiple Trait Bounds with the `+` Syntax +#### مشخص کردن محدودیت‌های ویژگی چندگانه با نحو `+` -We can also specify more than one trait bound. Say we wanted `notify` to use -display formatting as well as `summarize` on `item`: we specify in the `notify` -definition that `item` must implement both `Display` and `Summary`. We can do -so using the `+` syntax: +ما همچنین می‌توانیم بیش از یک محدودیت ویژگی مشخص کنیم. فرض کنید می‌خواهیم `notify` از فرمت‌بندی نمایش (display formatting) و همچنین `summarize` روی `item` استفاده کند: در تعریف `notify` مشخص می‌کنیم که `item` باید هر دو ویژگی `Display` و `Summary` را پیاده‌سازی کند. این کار را می‌توانیم با نحو `+` انجام دهیم: ```rust,ignore pub fn notify(item: &(impl Summary + Display)) { ``` -The `+` syntax is also valid with trait bounds on generic types: +نحو `+` همچنین با محدودیت ویژگی روی انواع جنریک معتبر است: ```rust,ignore pub fn notify(item: &T) { ``` -With the two trait bounds specified, the body of `notify` can call `summarize` -and use `{}` to format `item`. +با مشخص کردن این دو محدودیت ویژگی، بدنه `notify` می‌تواند متد `summarize` را فراخوانی کند و از `{}` برای فرمت‌بندی `item` استفاده کند. -#### Clearer Trait Bounds with `where` Clauses +#### محدودیت‌های ویژگی واضح‌تر با بندهای `where` -Using too many trait bounds has its downsides. Each generic has its own trait -bounds, so functions with multiple generic type parameters can contain lots of -trait bound information between the function’s name and its parameter list, -making the function signature hard to read. For this reason, Rust has alternate -syntax for specifying trait bounds inside a `where` clause after the function -signature. So, instead of writing this: +استفاده از تعداد زیادی محدودیت ویژگی معایب خود را دارد. هر جنریک محدودیت‌های ویژگی مخصوص به خود را دارد، بنابراین توابعی با چندین پارامتر نوع جنریک می‌توانند شامل اطلاعات زیادی درباره محدودیت‌های ویژگی بین نام تابع و لیست پارامترهای آن باشند، که باعث سخت شدن خواندن امضای تابع می‌شود. به همین دلیل، Rust نحو جایگزینی برای مشخص کردن محدودیت‌های ویژگی در داخل یک بند `where` پس از امضای تابع ارائه می‌دهد. بنابراین، به جای نوشتن این: ```rust,ignore fn some_function(t: &T, u: &U) -> i32 { ``` -we can use a `where` clause, like this: +می‌توانیم از یک بند `where` به این صورت استفاده کنیم: ```rust,ignore {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-07-where-clause/src/lib.rs:here}} ``` -This function’s signature is less cluttered: the function name, parameter list, -and return type are close together, similar to a function without lots of trait -bounds. +امضای این تابع کمتر شلوغ است: نام تابع، لیست پارامترها، و نوع بازگشتی به هم نزدیک‌تر هستند، مشابه یک تابع بدون محدودیت‌های ویژگی زیاد. -### Returning Types That Implement Traits +### بازگرداندن نوع‌هایی که ویژگی‌ها را پیاده‌سازی می‌کنند -We can also use the `impl Trait` syntax in the return position to return a -value of some type that implements a trait, as shown here: +ما همچنین می‌توانیم از نحو `impl Trait` در موقعیت بازگشتی استفاده کنیم تا مقداری از نوعی که یک ویژگی را پیاده‌سازی می‌کند بازگردانیم، همانطور که در اینجا نشان داده شده است: ```rust,ignore {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-05-returning-impl-trait/src/lib.rs:here}} ``` -By using `impl Summary` for the return type, we specify that the -`returns_summarizable` function returns some type that implements the `Summary` -trait without naming the concrete type. In this case, `returns_summarizable` -returns a `SocialPost`, but the code calling this function doesn’t need to know -that. +با استفاده از `impl Summary` برای نوع بازگشتی، مشخص می‌کنیم که تابع `returns_summarizable` +مقداری را بازمی‌گرداند که trait `Summary` را پیاده‌سازی می‌کند، بدون اینکه نوع مشخص آن را نام ببریم. +در این حالت، `returns_summarizable` یک `SocialPost` را بازمی‌گرداند، +اما کدی که این تابع را فراخوانی می‌کند نیازی به دانستن این موضوع ندارد. -The ability to specify a return type only by the trait it implements is -especially useful in the context of closures and iterators, which we cover in -Chapter 13. Closures and iterators create types that only the compiler knows or -types that are very long to specify. The `impl Trait` syntax lets you concisely -specify that a function returns some type that implements the `Iterator` trait -without needing to write out a very long type. +توانایی مشخص کردن یک نوع بازگشتی تنها بر اساس ویژگی‌ای که پیاده‌سازی می‌کند، به ویژه در زمینه closures و iterators مفید است، که در فصل ۱۳ به آن‌ها می‌پردازیم. closures و iterators نوع‌هایی ایجاد می‌کنند که تنها کامپایلر آن‌ها را می‌شناسد یا نوع‌هایی که بسیار طولانی هستند تا مشخص شوند. نحو `impl Trait` به شما اجازه می‌دهد که به طور مختصر مشخص کنید یک تابع نوعی که ویژگی `Iterator` را پیاده‌سازی می‌کند بازمی‌گرداند، بدون نیاز به نوشتن یک نوع بسیار طولانی. -However, you can only use `impl Trait` if you’re returning a single type. For -example, this code that returns either a `NewsArticle` or a `SocialPost` with -the return type specified as `impl Summary` wouldn’t work: +با این حال، تنها زمانی می‌توانید از `impl Trait` استفاده کنید که قرار است فقط یک نوع خاص را بازگردانید. +برای مثال، کدی که بسته به شرایط، یا یک `NewsArticle` یا یک `SocialPost` بازمی‌گرداند و +نوع بازگشتی آن به صورت `impl Summary` مشخص شده، کار نخواهد کرد: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-06-impl-trait-returns-one-type/src/lib.rs:here}} ``` -Returning either a `NewsArticle` or a `SocialPost` isn’t allowed due to -restrictions around how the `impl Trait` syntax is implemented in the compiler. -We’ll cover how to write a function with this behavior in the [“Using Trait -Objects That Allow for Values of Different -Types”][using-trait-objects-that-allow-for-values-of-different-types] section of Chapter 18. +بازگرداندن یکی از نوع‌های `NewsArticle` یا `SocialPost` مجاز نیست، به دلیل محدودیت‌هایی که در پیاده‌سازی نحوه عملکرد نحوی `impl Trait` در کامپایلر وجود دارد. +نحوه نوشتن تابعی با چنین رفتاری را در بخش [«استفاده از trait objectهایی که امکان داشتن مقادیر با نوع‌های مختلف را می‌دهند»][using-trait-objects-that-allow-for-values-of-different-types] از فصل ۱۸ بررسی خواهیم کرد. -### Using Trait Bounds to Conditionally Implement Methods +### استفاده از محدودیت‌های ویژگی برای پیاده‌سازی شرطی متدها -By using a trait bound with an `impl` block that uses generic type parameters, -we can implement methods conditionally for types that implement the specified -traits. For example, the type `Pair` in Listing 10-15 always implements the -`new` function to return a new instance of `Pair` (recall from the -[“Defining Methods”][methods] section of Chapter 5 that `Self` -is a type alias for the type of the `impl` block, which in this case is -`Pair`). But in the next `impl` block, `Pair` only implements the -`cmp_display` method if its inner type `T` implements the `PartialOrd` trait -that enables comparison _and_ the `Display` trait that enables printing. +با استفاده از یک محدودیت ویژگی در یک بلوک `impl` که از پارامترهای نوع جنریک استفاده می‌کند، می‌توانیم متدها را به طور شرطی برای نوع‌هایی که ویژگی‌های مشخص‌شده را پیاده‌سازی می‌کنند پیاده‌سازی کنیم. برای مثال، نوع `Pair` در لیست ۱۰-۱۵ همیشه تابع `new` را پیاده‌سازی می‌کند تا یک نمونه جدید از `Pair` بازگرداند (به یاد داشته باشید از بخش [“تعریف متدها”][methods] در فصل ۵ که `Self` یک نام مستعار برای نوع بلوک `impl` است که در اینجا `Pair` است). اما در بلوک `impl` بعدی، `Pair` فقط متد `cmp_display` را پیاده‌سازی می‌کند اگر نوع داخلی `T` ویژگی `PartialOrd` که مقایسه را ممکن می‌کند _و_ ویژگی `Display` که چاپ را ممکن می‌کند، پیاده‌سازی کند. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-15/src/lib.rs}} @@ -348,12 +253,7 @@ that enables comparison _and_ the `Display` trait that enables printing. -We can also conditionally implement a trait for any type that implements -another trait. Implementations of a trait on any type that satisfies the trait -bounds are called _blanket implementations_ and are used extensively in the -Rust standard library. For example, the standard library implements the -`ToString` trait on any type that implements the `Display` trait. The `impl` -block in the standard library looks similar to this code: +ما همچنین می‌توانیم یک ویژگی را به طور شرطی برای هر نوعی که ویژگی دیگری را پیاده‌سازی می‌کند، پیاده‌سازی کنیم. پیاده‌سازی‌های یک ویژگی روی هر نوعی که محدودیت‌های ویژگی را برآورده می‌کند _پیاده‌سازی‌های کلی_ (blanket implementations) نامیده می‌شوند و به طور گسترده در کتابخانه استاندارد Rust استفاده می‌شوند. برای مثال، کتابخانه استاندارد ویژگی `ToString` را روی هر نوعی که ویژگی `Display` را پیاده‌سازی می‌کند، پیاده‌سازی می‌کند. بلوک `impl` در کتابخانه استاندارد شبیه به این کد است: ```rust,ignore impl ToString for T { @@ -361,29 +261,15 @@ impl ToString for T { } ``` -Because the standard library has this blanket implementation, we can call the -`to_string` method defined by the `ToString` trait on any type that implements -the `Display` trait. For example, we can turn integers into their corresponding -`String` values like this because integers implement `Display`: +از آنجا که کتابخانه استاندارد این پیاده‌سازی کلی را دارد، می‌توانیم متد `to_string` تعریف‌شده توسط ویژگی `ToString` را روی هر نوعی که ویژگی `Display` را پیاده‌سازی می‌کند، فراخوانی کنیم. برای مثال، می‌توانیم اعداد صحیح را به مقادیر `String` متناظرشان تبدیل کنیم مانند این: ```rust let s = 3.to_string(); ``` -Blanket implementations appear in the documentation for the trait in the -“Implementors” section. - -Traits and trait bounds let us write code that uses generic type parameters to -reduce duplication but also specify to the compiler that we want the generic -type to have particular behavior. The compiler can then use the trait bound -information to check that all the concrete types used with our code provide the -correct behavior. In dynamically typed languages, we would get an error at -runtime if we called a method on a type which didn’t define the method. But -Rust moves these errors to compile time so we’re forced to fix the problems -before our code is even able to run. Additionally, we don’t have to write code -that checks for behavior at runtime because we’ve already checked at compile -time. Doing so improves performance without having to give up the flexibility -of generics. - -[using-trait-objects-that-allow-for-values-of-different-types]: ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types +پیاده‌سازی‌های کلی در مستندات ویژگی در بخش “Implementors” ظاهر می‌شوند. + +ویژگی‌ها و محدودیت‌های ویژگی به ما امکان می‌دهند که کدی بنویسیم که از پارامترهای نوع جنریک برای کاهش تکرار استفاده کند اما همچنین به کامپایلر مشخص کند که می‌خواهیم نوع جنریک رفتار خاصی داشته باشد. سپس کامپایلر می‌تواند از اطلاعات محدودیت ویژگی استفاده کند تا بررسی کند که تمام نوع‌های مشخص استفاده‌شده با کد ما رفتار صحیح را ارائه می‌دهند. در زبان‌های تایپ‌گذاری پویا، ما هنگام اجرا خطا دریافت می‌کنیم اگر یک متد روی یک نوع که آن متد را تعریف نکرده فراخوانی کنیم. اما Rust این خطاها را به زمان کامپایل منتقل می‌کند تا ما مجبور شویم مشکلات را قبل از اینکه کد ما اجرا شود برطرف کنیم. علاوه بر این، نیازی به نوشتن کدی نداریم که رفتار را در زمان اجرا بررسی کند زیرا قبلاً آن را در زمان کامپایل بررسی کرده‌ایم. این کار عملکرد را بهبود می‌بخشد بدون اینکه انعطاف‌پذیری جنریک‌ها را قربانی کند. + +[using-trait-objects-that-allow-for-values-of-different-types]: ch18-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types [methods]: ch05-03-method-syntax.html#defining-methods diff --git a/src/ch10-03-lifetime-syntax.md b/src/ch10-03-lifetime-syntax.md index 6b881da62a..d65e5b002b 100644 --- a/src/ch10-03-lifetime-syntax.md +++ b/src/ch10-03-lifetime-syntax.md @@ -1,33 +1,25 @@ -## Validating References with Lifetimes +## اعتبارسنجی مراجع با طول عمرها -Lifetimes are another kind of generic that we’ve already been using. Rather -than ensuring that a type has the behavior we want, lifetimes ensure that -references are valid as long as we need them to be. +طول عمرها نوع دیگری از جنریک‌ها هستند که ما قبلاً از آن‌ها استفاده کرده‌ایم. به جای اطمینان از اینکه یک نوع رفتار مورد نظر ما را دارد، طول عمرها تضمین می‌کنند که مراجع به اندازه‌ای که نیاز داریم معتبر باقی می‌مانند. -One detail we didn’t discuss in the [“References and -Borrowing”][references-and-borrowing] section in Chapter 4 is -that every reference in Rust has a _lifetime_, which is the scope for which -that reference is valid. Most of the time, lifetimes are implicit and inferred, -just like most of the time, types are inferred. We are only required to -annotate types when multiple types are possible. In a similar way, we have to -annotate lifetimes when the lifetimes of references could be related in a few -different ways. Rust requires us to annotate the relationships using generic -lifetime parameters to ensure the actual references used at runtime will -definitely be valid. +جزئیاتی که در بخش [“رفرنس‌ها و قرض‌گیری”][references-and-borrowing] در فصل ۴ مطرح نکردیم، این است که +هر رفرنس در Rust دارای یک *lifetime* است، یعنی حوزه‌ای که آن رفرنس در آن معتبر است. +بیشتر مواقع، lifetimeها به‌صورت ضمنی و استنتاج‌شده هستند، درست مانند بیشتر مواقعی که نوع‌ها به‌صورت استنتاج‌شده هستند. +ما تنها زمانی ملزم به افزودن annotation برای نوع‌ها هستیم که چند نوع ممکن وجود داشته باشد. +به‌طور مشابه، زمانی که lifetimeهای رفرنس‌ها ممکن است به چند شکل مختلف به هم مرتبط باشند، +باید رابطه‌ی آن‌ها را با پارامترهای lifetime generic مشخص کنیم. +Rust این الزام را دارد تا اطمینان حاصل شود که رفرنس‌های واقعی که در زمان اجرا استفاده می‌شوند، قطعاً معتبر خواهند بود. -Annotating lifetimes is not even a concept most other programming languages -have, so this is going to feel unfamiliar. Although we won’t cover lifetimes in -their entirety in this chapter, we’ll discuss common ways you might encounter -lifetime syntax so you can get comfortable with the concept. +افزودن annotation برای lifetimeها حتی مفهومی نیست که اکثر زبان‌های برنامه‌نویسی دیگر داشته باشند، +بنابراین این موضوع برای شما غریب به نظر خواهد رسید. +اگرچه در این فصل به‌صورت کامل به lifetimeها نمی‌پردازیم، +اما روش‌های رایجی که ممکن است با سینتکس lifetime مواجه شوید را بررسی خواهیم کرد تا با این مفهوم آشنا شوید. -### Preventing Dangling References with Lifetimes +### جلوگیری از مراجع آویزان (Dangling References) با طول عمرها -The main aim of lifetimes is to prevent _dangling references_, which cause a -program to reference data other than the data it’s intended to reference. -Consider the program in Listing 10-16, which has an outer scope and an inner -scope. +هدف اصلی طول عمرها جلوگیری از _مراجع آویزان_ است، که باعث می‌شوند یک برنامه به داده‌هایی غیر از داده‌هایی که قرار بوده مراجعه کند اشاره کند. برنامه‌ای را در نظر بگیرید که در لیست ۱۰-۱۶ نشان داده شده است و دارای یک محدوده خارجی و یک محدوده داخلی است. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-16/src/main.rs}} @@ -35,39 +27,25 @@ scope. -> Note: The examples in Listings 10-16, 10-17, and 10-23 declare variables -> without giving them an initial value, so the variable name exists in the outer -> scope. At first glance, this might appear to be in conflict with Rust’s having -> no null values. However, if we try to use a variable before giving it a value, -> we’ll get a compile-time error, which shows that Rust indeed does not allow -> null values. +> توجه: مثال‌های لیستینگ‌های 10-16، 10-17، و 10-23 متغیرهایی را بدون مقداردهی اولیه اعلام می‌کنند، +> بنابراین نام متغیر در حوزه‌ی بیرونی وجود دارد. +> در نگاه اول، این ممکن است به‌نظر برسد که با عدم وجود مقدار null در Rust مغایرت دارد. +> اما اگر سعی کنیم متغیری را قبل از مقداردهی استفاده کنیم، +> خطای زمان کامپایل دریافت خواهیم کرد که نشان می‌دهد Rust واقعاً اجازه‌ی وجود مقدار null را نمی‌دهد. -The outer scope declares a variable named `r` with no initial value, and the -inner scope declares a variable named `x` with the initial value of `5`. Inside -the inner scope, we attempt to set the value of `r` as a reference to `x`. Then -the inner scope ends, and we attempt to print the value in `r`. This code won’t -compile because the value that `r` is referring to has gone out of scope before -we try to use it. Here is the error message: +محدوده خارجی یک متغیر به نام `r` را بدون مقدار اولیه اعلام می‌کند، و محدوده داخلی یک متغیر به نام `x` را با مقدار اولیه `5` اعلام می‌کند. در داخل محدوده داخلی، تلاش می‌کنیم مقدار `r` را به عنوان یک مرجع به `x` تنظیم کنیم. سپس محدوده داخلی به پایان می‌رسد و سعی می‌کنیم مقدار موجود در `r` را چاپ کنیم. این کد کامپایل نخواهد شد زیرا مقداری که `r` به آن اشاره می‌کند قبل از اینکه سعی کنیم از آن استفاده کنیم از محدوده خارج شده است. پیام خطای زیر را دریافت می‌کنیم: ```console {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-16/output.txt}} ``` -The error message says that the variable `x` “does not live long enough.” The -reason is that `x` will be out of scope when the inner scope ends on line 7. -But `r` is still valid for the outer scope; because its scope is larger, we say -that it “lives longer.” If Rust allowed this code to work, `r` would be -referencing memory that was deallocated when `x` went out of scope, and -anything we tried to do with `r` wouldn’t work correctly. So how does Rust -determine that this code is invalid? It uses a borrow checker. +پیام خطا می‌گوید که متغیر `x` "به اندازه کافی طول عمر ندارد." دلیل این است که `x` وقتی محدوده داخلی در خط ۷ پایان می‌یابد، از محدوده خارج می‌شود. اما `r` همچنان برای محدوده خارجی معتبر است؛ زیرا محدوده آن بزرگ‌تر است، می‌گوییم که "طول عمر بیشتری دارد." اگر Rust به این کد اجازه کار کردن می‌داد، `r` به حافظه‌ای اشاره می‌کرد که وقتی `x` از محدوده خارج شد آزاد شده است، و هر کاری که سعی می‌کردیم با `r` انجام دهیم به درستی کار نمی‌کرد. پس چگونه Rust تشخیص می‌دهد که این کد نامعتبر است؟ از یک بررسی‌کننده قرض (borrow checker) استفاده می‌کند. -### The Borrow Checker +### بررسی‌کننده قرض (Borrow Checker) -The Rust compiler has a _borrow checker_ that compares scopes to determine -whether all borrows are valid. Listing 10-17 shows the same code as Listing -10-16 but with annotations showing the lifetimes of the variables. +کامپایلر Rust دارای یک _بررسی‌کننده قرض_ است که محدوده‌ها را مقایسه می‌کند تا تعیین کند که آیا تمام قرض‌ها معتبر هستند یا خیر. لیست ۱۰-۱۷ همان کد لیست ۱۰-۱۶ را نشان می‌دهد اما با حاشیه‌نویسی‌هایی که طول عمر متغیرها را نشان می‌دهد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-17/src/main.rs}} @@ -75,17 +53,11 @@ whether all borrows are valid. Listing 10-17 shows the same code as Listing -Here, we’ve annotated the lifetime of `r` with `'a` and the lifetime of `x` -with `'b`. As you can see, the inner `'b` block is much smaller than the outer -`'a` lifetime block. At compile time, Rust compares the size of the two -lifetimes and sees that `r` has a lifetime of `'a` but that it refers to memory -with a lifetime of `'b`. The program is rejected because `'b` is shorter than -`'a`: the subject of the reference doesn’t live as long as the reference. +در اینجا، طول عمر `r` را با `'a` و طول عمر `x` را با `'b` حاشیه‌نویسی کرده‌ایم. همانطور که می‌بینید، بلوک داخلی `'b` بسیار کوچک‌تر از بلوک طول عمر خارجی `'a` است. در زمان کامپایل، Rust اندازه دو طول عمر را مقایسه می‌کند و می‌بیند که `r` دارای طول عمر `'a` است اما به حافظه‌ای اشاره می‌کند که طول عمر آن `'b` است. برنامه رد می‌شود زیرا `'b` کوتاه‌تر از `'a` است: موضوع مرجع به اندازه مرجع زنده نیست. -Listing 10-18 fixes the code so it doesn’t have a dangling reference and it -compiles without any errors. +لیست ۱۰-۱۸ کد را اصلاح می‌کند تا یک مرجع آویزان نداشته باشد و بدون هیچ خطایی کامپایل شود. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-18/src/main.rs}} @@ -93,22 +65,16 @@ compiles without any errors. -Here, `x` has the lifetime `'b`, which in this case is larger than `'a`. This -means `r` can reference `x` because Rust knows that the reference in `r` will -always be valid while `x` is valid. +در اینجا، `x` دارای طول عمر `'b` است که در این مورد بزرگ‌تر از `'a` است. این بدان معناست که `r` می‌تواند به `x` اشاره کند زیرا Rust می‌داند که مرجع در `r` همیشه در حالی که `x` معتبر است، معتبر خواهد بود. -Now that you know where the lifetimes of references are and how Rust analyzes -lifetimes to ensure references will always be valid, let’s explore generic -lifetimes of parameters and return values in the context of functions. +حال که می‌دانید lifetime رفرنس‌ها کجا هستند و Rust چگونه lifetimeها را تحلیل می‌کند تا اطمینان یابد رفرنس‌ها همیشه معتبر خواهند بود، +بیایید lifetimeهای generic پارامترها و مقادیر بازگشتی را در زمینه‌ی توابع بررسی کنیم. -### Generic Lifetimes in Functions +### طول عمرهای جنریک در توابع -We’ll write a function that returns the longer of two string slices. This -function will take two string slices and return a single string slice. After -we’ve implemented the `longest` function, the code in Listing 10-19 should -print `The longest string is abcd`. +ما تابعی خواهیم نوشت که طولانی‌ترین قطعه رشته (string slice) را بازمی‌گرداند. این تابع دو قطعه رشته می‌گیرد و یک قطعه رشته بازمی‌گرداند. پس از پیاده‌سازی تابع `longest`، کد در لیست ۱۰-۱۹ باید `The longest string is abcd` را چاپ کند. -+ ```rust,ignore {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-19/src/main.rs}} @@ -116,17 +82,14 @@ print `The longest string is abcd`. -Note that we want the function to take string slices, which are references, -rather than strings, because we don’t want the `longest` function to take -ownership of its parameters. Refer to [“String Slices as -Parameters”][string-slices-as-parameters] in Chapter 4 for more -discussion about why the parameters we use in Listing 10-19 are the ones we -want. +توجه کنید که ما می‌خواهیم تابع پارامترهایی از نوع string slice دریافت کند، +که رفرنس هستند، نه رشته‌های کامل، زیرا نمی‌خواهیم تابع `longest` مالک پارامترهایش باشد. +برای بحث بیشتر درباره‌ی دلیل انتخاب چنین پارامترهایی در لیستینگ 10-19، +به بخش [“String Slices به عنوان پارامتر”][string-slices-as-parameters] در فصل ۴ مراجعه کنید. -If we try to implement the `longest` function as shown in Listing 10-20, it -won’t compile. +اگر سعی کنیم تابع `longest` را همانطور که در لیست ۱۰-۲۰ نشان داده شده است پیاده‌سازی کنیم، کامپایل نمی‌شود. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-20/src/main.rs:here}} @@ -134,71 +97,39 @@ won’t compile. -Instead, we get the following error that talks about lifetimes: +به جای آن، خطای زیر را دریافت می‌کنیم که درباره طول عمرها صحبت می‌کند: ```console {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-20/output.txt}} ``` -The help text reveals that the return type needs a generic lifetime parameter -on it because Rust can’t tell whether the reference being returned refers to -`x` or `y`. Actually, we don’t know either, because the `if` block in the body -of this function returns a reference to `x` and the `else` block returns a -reference to `y`! - -When we’re defining this function, we don’t know the concrete values that will -be passed into this function, so we don’t know whether the `if` case or the -`else` case will execute. We also don’t know the concrete lifetimes of the -references that will be passed in, so we can’t look at the scopes as we did in -Listings 10-17 and 10-18 to determine whether the reference we return will -always be valid. The borrow checker can’t determine this either, because it -doesn’t know how the lifetimes of `x` and `y` relate to the lifetime of the -return value. To fix this error, we’ll add generic lifetime parameters that -define the relationship between the references so the borrow checker can -perform its analysis. - -### Lifetime Annotation Syntax - -Lifetime annotations don’t change how long any of the references live. Rather, -they describe the relationships of the lifetimes of multiple references to each -other without affecting the lifetimes. Just as functions can accept any type -when the signature specifies a generic type parameter, functions can accept -references with any lifetime by specifying a generic lifetime parameter. - -Lifetime annotations have a slightly unusual syntax: the names of lifetime -parameters must start with an apostrophe (`'`) and are usually all lowercase -and very short, like generic types. Most people use the name `'a` for the first -lifetime annotation. We place lifetime parameter annotations after the `&` of a -reference, using a space to separate the annotation from the reference’s type. - -Here are some examples: a reference to an `i32` without a lifetime parameter, a -reference to an `i32` that has a lifetime parameter named `'a`, and a mutable -reference to an `i32` that also has the lifetime `'a`. +متن کمکی نشان می‌دهد که نوع بازگشتی نیاز به یک پارامتر طول عمر جنریک دارد زیرا Rust نمی‌تواند تشخیص دهد که مرجع بازگردانده‌شده به `x` اشاره می‌کند یا به `y`. در واقع، ما هم نمی‌دانیم، زیرا بلوک `if` در بدنه این تابع یک مرجع به `x` بازمی‌گرداند و بلوک `else` یک مرجع به `y` بازمی‌گرداند! + +وقتی این تابع را تعریف می‌کنیم، مقادیر مشخصی که به این تابع پاس داده می‌شوند را نمی‌دانیم، بنابراین نمی‌دانیم که آیا حالت `if` یا حالت `else` اجرا خواهد شد. همچنین طول عمرهای مشخص مراجع پاس‌داده‌شده را نمی‌دانیم، بنابراین نمی‌توانیم به محدوده‌ها مانند لیست‌های ۱۰-۱۷ و ۱۰-۱۸ نگاه کنیم تا تعیین کنیم که مرجعی که بازمی‌گردانیم همیشه معتبر خواهد بود. بررسی‌کننده قرض هم نمی‌تواند این موضوع را تعیین کند زیرا نمی‌داند چگونه طول عمرهای `x` و `y` به طول عمر مقدار بازگشتی مرتبط هستند. برای رفع این خطا، پارامترهای طول عمر جنریک اضافه می‌کنیم که رابطه بین مراجع را تعریف می‌کنند تا بررسی‌کننده قرض بتواند تحلیل خود را انجام دهد. + +### نحو حاشیه‌نویسی طول عمر + +حاشیه‌نویسی طول عمر طول عمر هیچ‌یک از مراجع را تغییر نمی‌دهد. بلکه، آن‌ها روابط طول عمرهای چندین مرجع را بدون تأثیر بر طول عمرها توصیف می‌کنند. همانطور که توابع می‌توانند هر نوعی را بپذیرند وقتی امضا یک پارامتر نوع جنریک را مشخص می‌کند، توابع می‌توانند مراجع با هر طول عمری را بپذیرند با مشخص کردن یک پارامتر طول عمر جنریک. + +حاشیه‌نویسی طول عمر دارای نحو کمی غیرمعمول است: نام‌های پارامتر طول عمر باید با یک آپاستروف (`'`) شروع شوند و معمولاً همه حروف کوچک و بسیار کوتاه هستند، مانند نوع‌های جنریک. بیشتر افراد از نام `'a` برای اولین حاشیه‌نویسی طول عمر استفاده می‌کنند. ما حاشیه‌نویسی‌های پارامتر طول عمر را بعد از `&` یک مرجع قرار می‌دهیم و از یک فاصله برای جدا کردن حاشیه‌نویسی از نوع مرجع استفاده می‌کنیم. + +در اینجا چند مثال آورده شده است: یک مرجع به یک `i32` بدون پارامتر طول عمر، یک مرجع به یک `i32` که یک پارامتر طول عمر به نام `'a` دارد، و یک مرجع قابل تغییر به یک `i32` که همچنین طول عمر `'a` دارد: ```rust,ignore -&i32 // a reference -&'a i32 // a reference with an explicit lifetime -&'a mut i32 // a mutable reference with an explicit lifetime +&i32 // یک مرجع +&'a i32 // یک مرجع با طول عمر صریح +&'a mut i32 // یک مرجع قابل تغییر با طول عمر صریح ``` -One lifetime annotation by itself doesn’t have much meaning because the -annotations are meant to tell Rust how generic lifetime parameters of multiple -references relate to each other. Let’s examine how the lifetime annotations -relate to each other in the context of the `longest` function. +یک حاشیه‌نویسی طول عمر به تنهایی معنای زیادی ندارد زیرا حاشیه‌نویسی‌ها برای توضیح دادن به Rust هستند که پارامترهای طول عمر جنریک چندین مرجع چگونه به یکدیگر مرتبط هستند. بیایید بررسی کنیم که حاشیه‌نویسی‌های طول عمر چگونه در زمینه تابع `longest` به یکدیگر مرتبط هستند. -### Lifetime Annotations in Function Signatures +### حاشیه‌نویسی طول عمر در امضاهای توابع -To use lifetime annotations in function signatures, we need to declare the -generic _lifetime_ parameters inside angle brackets between the function name -and the parameter list, just as we did with generic _type_ parameters. +برای استفاده از حاشیه‌نویسی‌های طول عمر در امضاهای توابع، باید پارامترهای طول عمر جنریک را در داخل پرانتزهای زاویه‌ای بین نام تابع و لیست پارامتر اعلام کنیم، همانطور که با پارامترهای نوع جنریک انجام دادیم. -We want the signature to express the following constraint: the returned -reference will be valid as long as both the parameters are valid. This is the -relationship between lifetimes of the parameters and the return value. We’ll -name the lifetime `'a` and then add it to each reference, as shown in Listing -10-21. +ما می‌خواهیم امضا محدودیت زیر را بیان کند: مرجع بازگردانده‌شده تا زمانی که هر دو پارامتر معتبر هستند معتبر خواهد بود. این رابطه بین طول عمرهای پارامترها و مقدار بازگشتی است. طول عمر را به نام `'a` می‌نامیم و سپس آن را به هر مرجع اضافه می‌کنیم، همانطور که در لیست ۱۰-۲۱ نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-21/src/main.rs:here}} @@ -206,49 +137,20 @@ name the lifetime `'a` and then add it to each reference, as shown in Listing -This code should compile and produce the result we want when we use it with the -`main` function in Listing 10-19. - -The function signature now tells Rust that for some lifetime `'a`, the function -takes two parameters, both of which are string slices that live at least as -long as lifetime `'a`. The function signature also tells Rust that the string -slice returned from the function will live at least as long as lifetime `'a`. -In practice, it means that the lifetime of the reference returned by the -`longest` function is the same as the smaller of the lifetimes of the values -referred to by the function arguments. These relationships are what we want -Rust to use when analyzing this code. - -Remember, when we specify the lifetime parameters in this function signature, -we’re not changing the lifetimes of any values passed in or returned. Rather, -we’re specifying that the borrow checker should reject any values that don’t -adhere to these constraints. Note that the `longest` function doesn’t need to -know exactly how long `x` and `y` will live, only that some scope can be -substituted for `'a` that will satisfy this signature. - -When annotating lifetimes in functions, the annotations go in the function -signature, not in the function body. The lifetime annotations become part of -the contract of the function, much like the types in the signature. Having -function signatures contain the lifetime contract means the analysis the Rust -compiler does can be simpler. If there’s a problem with the way a function is -annotated or the way it is called, the compiler errors can point to the part of -our code and the constraints more precisely. If, instead, the Rust compiler -made more inferences about what we intended the relationships of the lifetimes -to be, the compiler might only be able to point to a use of our code many steps -away from the cause of the problem. - -When we pass concrete references to `longest`, the concrete lifetime that is -substituted for `'a` is the part of the scope of `x` that overlaps with the -scope of `y`. In other words, the generic lifetime `'a` will get the concrete -lifetime that is equal to the smaller of the lifetimes of `x` and `y`. Because -we’ve annotated the returned reference with the same lifetime parameter `'a`, -the returned reference will also be valid for the length of the smaller of the -lifetimes of `x` and `y`. - -Let’s look at how the lifetime annotations restrict the `longest` function by -passing in references that have different concrete lifetimes. Listing 10-22 is -a straightforward example. - -+این کد باید کامپایل شود و نتیجه مورد نظر ما را زمانی که با تابع `main` در لیست ۱۰-۱۹ استفاده می‌کنیم تولید کند. + +امضای تابع اکنون به Rust می‌گوید که برای برخی طول عمر `'a`، تابع دو پارامتر می‌گیرد که هر دو قطعه رشته‌هایی هستند که حداقل به مدت طول عمر `'a` زندگی می‌کنند. امضای تابع همچنین به Rust می‌گوید که قطعه رشته‌ای که از تابع بازگردانده می‌شود حداقل به مدت طول عمر `'a` زندگی می‌کند. در عمل، این بدان معناست که طول عمر مرجعی که توسط تابع `longest` بازگردانده می‌شود همان طول عمر کوچکتر مقادیر اشاره‌شده توسط آرگومان‌های تابع است. این روابط چیزی است که ما می‌خواهیم Rust هنگام تحلیل این کد از آن‌ها استفاده کند. + +به یاد داشته باشید، وقتی پارامترهای طول عمر را در این امضای تابع مشخص می‌کنیم، طول عمر هیچ‌یک از مقادیر پاس‌داده‌شده یا بازگردانده‌شده را تغییر نمی‌دهیم. بلکه، مشخص می‌کنیم که بررسی‌کننده قرض باید هر مقداری را که به این محدودیت‌ها پایبند نیست رد کند. توجه داشته باشید که تابع `longest` نیازی به دانستن دقیق اینکه `x` و `y` چقدر زنده خواهند ماند ندارد، تنها اینکه برخی محدوده‌ها می‌توانند جایگزین `'a` شوند که این امضا را برآورده کنند. + +هنگام حاشیه‌نویسی طول عمرها در توابع، حاشیه‌نویسی‌ها در امضای تابع قرار می‌گیرند، نه در بدنه تابع. حاشیه‌نویسی‌های طول عمر بخشی از قرارداد تابع می‌شوند، مشابه انواع موجود در امضا. داشتن امضای تابع که شامل قرارداد طول عمر است به این معنی است که تحلیلی که کامپایلر Rust انجام می‌دهد می‌تواند ساده‌تر باشد. اگر مشکلی در نحوه حاشیه‌نویسی یک تابع یا نحوه فراخوانی آن وجود داشته باشد، خطاهای کامپایلر می‌توانند به بخش مشخصی از کد ما و محدودیت‌ها اشاره کنند. اگر، به جای آن، کامپایلر Rust استنتاج بیشتری درباره آنچه که قصد داریم روابط طول عمرها باشند انجام دهد، کامپایلر ممکن است فقط بتواند به استفاده‌ای از کد ما اشاره کند که چندین مرحله دور از علت مشکل باشد. + +وقتی مراجع مشخصی را به `longest` پاس می‌دهیم، طول عمر مشخصی که برای `'a` جایگزین می‌شود بخشی از محدوده `x` است که با محدوده `y` هم‌پوشانی دارد. به عبارت دیگر، طول عمر جنریک `'a` طول عمر مشخصی را می‌گیرد که برابر با کوچک‌ترین طول عمرهای `x` و `y` است. از آنجا که مرجع بازگردانده‌شده را با همان پارامتر طول عمر `'a` حاشیه‌نویسی کرده‌ایم، مرجع بازگردانده‌شده نیز برای مدت کوچک‌ترین طول عمرهای `x` و `y` معتبر خواهد بود. + +بیایید ببینیم حاشیه‌نویسی طول عمرها چگونه تابع `longest` را محدود می‌کند با پاس دادن مراجع که طول عمرهای مشخص مختلفی دارند. لیست ۱۰-۲۲ یک مثال ساده است. + ++ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-22/src/main.rs:here}} @@ -256,21 +158,11 @@ a straightforward example. -In this example, `string1` is valid until the end of the outer scope, `string2` -is valid until the end of the inner scope, and `result` references something -that is valid until the end of the inner scope. Run this code and you’ll see -that the borrow checker approves; it will compile and print `The longest string -is long string is long`. +در این مثال، `string1` تا پایان محدوده خارجی معتبر است، `string2` تا پایان محدوده داخلی معتبر است، و `result` به چیزی اشاره می‌کند که تا پایان محدوده داخلی معتبر است. این کد را اجرا کنید و خواهید دید که بررسی‌کننده قرض آن را تأیید می‌کند؛ کد کامپایل می‌شود و `The longest string is long string is long` را چاپ می‌کند. -Next, let’s try an example that shows that the lifetime of the reference in -`result` must be the smaller lifetime of the two arguments. We’ll move the -declaration of the `result` variable outside the inner scope but leave the -assignment of the value to the `result` variable inside the scope with -`string2`. Then we’ll move the `println!` that uses `result` to outside the -inner scope, after the inner scope has ended. The code in Listing 10-23 will -not compile. +حال، بیایید مثالی را امتحان کنیم که نشان دهد طول عمر مرجع در `result` باید کوچک‌ترین طول عمر دو آرگومان باشد. اعلام متغیر `result` را به بیرون از محدوده داخلی منتقل می‌کنیم، اما مقداردهی به متغیر `result` را درون محدوده با `string2` نگه می‌داریم. سپس `println!` که از `result` استفاده می‌کند را به بیرون از محدوده داخلی، پس از پایان محدوده داخلی منتقل می‌کنیم. کد در لیست ۱۰-۲۳ کامپایل نمی‌شود. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-23/src/main.rs:here}} @@ -278,38 +170,21 @@ not compile. -When we try to compile this code, we get this error: +وقتی تلاش می‌کنیم این کد را کامپایل کنیم، خطای زیر را دریافت می‌کنیم: ```console {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-23/output.txt}} ``` -The error shows that for `result` to be valid for the `println!` statement, -`string2` would need to be valid until the end of the outer scope. Rust knows -this because we annotated the lifetimes of the function parameters and return -values using the same lifetime parameter `'a`. - -As humans, we can look at this code and see that `string1` is longer than -`string2`, and therefore, `result` will contain a reference to `string1`. -Because `string1` has not gone out of scope yet, a reference to `string1` will -still be valid for the `println!` statement. However, the compiler can’t see -that the reference is valid in this case. We’ve told Rust that the lifetime of -the reference returned by the `longest` function is the same as the smaller of -the lifetimes of the references passed in. Therefore, the borrow checker -disallows the code in Listing 10-23 as possibly having an invalid reference. - -Try designing more experiments that vary the values and lifetimes of the -references passed in to the `longest` function and how the returned reference -is used. Make hypotheses about whether or not your experiments will pass the -borrow checker before you compile; then check to see if you’re right! - -### Thinking in Terms of Lifetimes - -The way in which you need to specify lifetime parameters depends on what your -function is doing. For example, if we changed the implementation of the -`longest` function to always return the first parameter rather than the longest -string slice, we wouldn’t need to specify a lifetime on the `y` parameter. The -following code will compile: +این خطا نشان می‌دهد که برای اینکه `result` برای دستور `println!` معتبر باشد، `string2` باید تا پایان محدوده خارجی معتبر باشد. Rust این را می‌داند زیرا طول عمرهای پارامترهای تابع و مقادیر بازگشتی را با استفاده از همان پارامتر طول عمر `'a` حاشیه‌نویسی کرده‌ایم. + +به عنوان انسان، می‌توانیم به این کد نگاه کنیم و ببینیم که `string1` طولانی‌تر از `string2` است، و بنابراین، `result` یک مرجع به `string1` خواهد داشت. زیرا `string1` هنوز از محدوده خارج نشده است، یک مرجع به `string1` برای دستور `println!` همچنان معتبر خواهد بود. با این حال، کامپایلر نمی‌تواند ببیند که این مرجع در این مورد معتبر است. ما به Rust گفته‌ایم که طول عمر مرجع بازگردانده‌شده توسط تابع `longest` همان طول عمر کوچک‌ترین مرجع‌های پاس‌داده‌شده است. بنابراین، بررسی‌کننده قرض کد در لیست ۱۰-۲۳ را به عنوان داشتن یک مرجع نامعتبر احتمالی رد می‌کند. + +سعی کنید آزمایش‌های بیشتری طراحی کنید که مقادیر و طول عمر مراجع پاس‌داده‌شده به تابع `longest` و نحوه استفاده از مرجع بازگردانده‌شده را تغییر دهند. فرضیاتی درباره اینکه آیا آزمایش‌های شما بررسی‌کننده قرض را پاس می‌کنند یا نه ایجاد کنید؛ سپس بررسی کنید که آیا درست می‌گویید! + +### تفکر بر اساس طول عمرها + +نحوه نیاز شما به مشخص کردن پارامترهای طول عمر به آنچه که تابع شما انجام می‌دهد بستگی دارد. برای مثال، اگر پیاده‌سازی تابع `longest` را تغییر دهیم تا همیشه اولین پارامتر را به جای طولانی‌ترین قطعه رشته بازگرداند، نیازی به مشخص کردن طول عمر برای پارامتر `y` نخواهیم داشت. کد زیر کامپایل می‌شود: @@ -319,17 +194,9 @@ following code will compile: -We’ve specified a lifetime parameter `'a` for the parameter `x` and the return -type, but not for the parameter `y`, because the lifetime of `y` does not have -any relationship with the lifetime of `x` or the return value. +ما یک پارامتر طول عمر `'a` برای پارامتر `x` و نوع بازگشتی مشخص کرده‌ایم، اما برای پارامتر `y` نه، زیرا طول عمر `y` هیچ رابطه‌ای با طول عمر `x` یا مقدار بازگشتی ندارد. -When returning a reference from a function, the lifetime parameter for the -return type needs to match the lifetime parameter for one of the parameters. If -the reference returned does _not_ refer to one of the parameters, it must refer -to a value created within this function. However, this would be a dangling -reference because the value will go out of scope at the end of the function. -Consider this attempted implementation of the `longest` function that won’t -compile: +هنگام بازگرداندن یک مرجع از یک تابع، پارامتر طول عمر برای نوع بازگشتی باید با پارامتر طول عمر یکی از پارامترها مطابقت داشته باشد. اگر مرجع بازگردانده‌شده به یکی از پارامترها اشاره نکند، باید به مقداری که در این تابع ایجاد شده است اشاره کند. با این حال، این یک مرجع آویزان خواهد بود زیرا مقدار در پایان تابع از محدوده خارج می‌شود. به این پیاده‌سازی ناموفق تابع `longest` که کامپایل نمی‌شود توجه کنید: @@ -339,36 +206,21 @@ compile: -Here, even though we’ve specified a lifetime parameter `'a` for the return -type, this implementation will fail to compile because the return value -lifetime is not related to the lifetime of the parameters at all. Here is the -error message we get: +در اینجا، حتی اگر یک پارامتر طول عمر `'a` برای نوع بازگشتی مشخص کرده باشیم، این پیاده‌سازی کامپایل نمی‌شود زیرا طول عمر مقدار بازگشتی به هیچ وجه به طول عمر پارامترها مرتبط نیست. پیام خطایی که دریافت می‌کنیم به این شکل است: ```console {{#include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-09-unrelated-lifetime/output.txt}} ``` -The problem is that `result` goes out of scope and gets cleaned up at the end -of the `longest` function. We’re also trying to return a reference to `result` -from the function. There is no way we can specify lifetime parameters that -would change the dangling reference, and Rust won’t let us create a dangling -reference. In this case, the best fix would be to return an owned data type -rather than a reference so the calling function is then responsible for -cleaning up the value. +مشکل این است که `result` از محدوده خارج می‌شود و در پایان تابع `longest` پاک می‌شود. همچنین سعی می‌کنیم یک مرجع به `result` را از تابع بازگردانیم. هیچ راهی وجود ندارد که بتوانیم پارامترهای طول عمری مشخص کنیم که مرجع آویزان را تغییر دهد، و Rust به ما اجازه نمی‌دهد یک مرجع آویزان ایجاد کنیم. در این مورد، بهترین راه حل این است که یک نوع داده مالک (owned) به جای یک مرجع بازگردانیم تا تابع فراخوانی‌کننده مسئول پاک‌سازی مقدار باشد. -Ultimately, lifetime syntax is about connecting the lifetimes of various -parameters and return values of functions. Once they’re connected, Rust has -enough information to allow memory-safe operations and disallow operations that -would create dangling pointers or otherwise violate memory safety. +در نهایت، نحو طول عمرها درباره ارتباط دادن طول عمرهای پارامترها و مقادیر بازگشتی توابع است. وقتی این ارتباط برقرار شد، Rust اطلاعات کافی برای اجازه دادن به عملیات‌های ایمن از نظر حافظه و منع عملیات‌هایی که باعث ایجاد اشاره‌گر (Pointer)های آویزان یا نقض ایمنی حافظه می‌شوند، دارد. -### Lifetime Annotations in Struct Definitions +### حاشیه‌نویسی طول عمر در تعریف ساختارها -So far, the structs we’ve defined all hold owned types. We can define structs -to hold references, but in that case we would need to add a lifetime annotation -on every reference in the struct’s definition. Listing 10-24 has a struct named -`ImportantExcerpt` that holds a string slice. +تا کنون، ساختارهایی که تعریف کرده‌ایم همه دارای نوع‌های مالک بوده‌اند. می‌توانیم ساختارهایی را تعریف کنیم که مراجع نگه می‌دارند، اما در این صورت باید برای هر مرجعی در تعریف ساختار یک حاشیه‌نویسی طول عمر اضافه کنیم. لیست ۱۰-۲۴ یک ساختار به نام `ImportantExcerpt` دارد که یک قطعه رشته نگه می‌دارد. -+ ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-24/src/main.rs}} @@ -376,28 +228,16 @@ on every reference in the struct’s definition. Listing 10-24 has a struct name -This struct has the single field `part` that holds a string slice, which is a -reference. As with generic data types, we declare the name of the generic -lifetime parameter inside angle brackets after the name of the struct so we can -use the lifetime parameter in the body of the struct definition. This -annotation means an instance of `ImportantExcerpt` can’t outlive the reference -it holds in its `part` field. +این ساختار دارای یک فیلد به نام `part` است که یک قطعه رشته نگه می‌دارد، که یک مرجع است. همانند نوع‌های داده جنریک، ما نام پارامتر طول عمر جنریک را در داخل پرانتزهای زاویه‌ای بعد از نام ساختار اعلام می‌کنیم تا بتوانیم پارامتر طول عمر را در بدنه تعریف ساختار استفاده کنیم. این حاشیه‌نویسی به این معنی است که یک نمونه از `ImportantExcerpt` نمی‌تواند بیشتر از مرجعی که در فیلد `part` خود نگه می‌دارد زنده بماند. -The `main` function here creates an instance of the `ImportantExcerpt` struct -that holds a reference to the first sentence of the `String` owned by the -variable `novel`. The data in `novel` exists before the `ImportantExcerpt` -instance is created. In addition, `novel` doesn’t go out of scope until after -the `ImportantExcerpt` goes out of scope, so the reference in the -`ImportantExcerpt` instance is valid. +تابع `main` در اینجا یک نمونه از ساختار `ImportantExcerpt` ایجاد می‌کند که یک مرجع به اولین جمله از `String` که توسط متغیر `novel` نگه داشته می‌شود، نگه می‌دارد. داده‌های `novel` قبل از ایجاد نمونه `ImportantExcerpt` وجود دارند. علاوه بر این، `novel` تا بعد از خروج `ImportantExcerpt` از محدوده از محدوده خارج نمی‌شود، بنابراین مرجع در نمونه `ImportantExcerpt` معتبر است. -### Lifetime Elision +### حذف طول عمر (Lifetime Elision) -You’ve learned that every reference has a lifetime and that you need to specify -lifetime parameters for functions or structs that use references. However, we -had a function in Listing 4-9, shown again in Listing 10-25, that compiled -without lifetime annotations. +آموختید که هر مرجع دارای یک طول عمر است و شما باید برای توابع یا ساختارهایی که از مراجع استفاده می‌کنند پارامترهای طول عمر مشخص کنید. با این حال، ما تابعی در لیست ۴-۹ داشتیم که دوباره در لیست ۱۰-۲۵ نشان داده شده است، که بدون حاشیه‌نویسی طول عمر کامپایل شد. + + - ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-25/src/main.rs:here}} @@ -405,213 +245,123 @@ without lifetime annotations. -The reason this function compiles without lifetime annotations is historical: -in early versions (pre-1.0) of Rust, this code wouldn’t have compiled because -every reference needed an explicit lifetime. At that time, the function -signature would have been written like this: +دلیل اینکه این تابع بدون حاشیه‌نویسی طول عمر کامپایل می‌شود تاریخی است: در نسخه‌های اولیه (قبل از 1.0) از Rust، این کد کامپایل نمی‌شد زیرا هر مرجع نیاز به یک طول عمر صریح داشت. در آن زمان، امضای تابع به این صورت نوشته می‌شد: ```rust,ignore fn first_word<'a>(s: &'a str) -> &'a str { ``` -After writing a lot of Rust code, the Rust team found that Rust programmers -were entering the same lifetime annotations over and over in particular -situations. These situations were predictable and followed a few deterministic -patterns. The developers programmed these patterns into the compiler’s code so -the borrow checker could infer the lifetimes in these situations and wouldn’t -need explicit annotations. - -This piece of Rust history is relevant because it’s possible that more -deterministic patterns will emerge and be added to the compiler. In the future, -even fewer lifetime annotations might be required. - -The patterns programmed into Rust’s analysis of references are called the -_lifetime elision rules_. These aren’t rules for programmers to follow; they’re -a set of particular cases that the compiler will consider, and if your code -fits these cases, you don’t need to write the lifetimes explicitly. - -The elision rules don’t provide full inference. If there is still ambiguity -about what lifetimes the references have after Rust applies the rules, the -compiler won’t guess what the lifetime of the remaining references should be. -Instead of guessing, the compiler will give you an error that you can resolve by -adding the lifetime annotations. - -Lifetimes on function or method parameters are called _input lifetimes_, and -lifetimes on return values are called _output lifetimes_. - -The compiler uses three rules to figure out the lifetimes of the references -when there aren’t explicit annotations. The first rule applies to input -lifetimes, and the second and third rules apply to output lifetimes. If the -compiler gets to the end of the three rules and there are still references for -which it can’t figure out lifetimes, the compiler will stop with an error. -These rules apply to `fn` definitions as well as `impl` blocks. - -The first rule is that the compiler assigns a lifetime parameter to each -parameter that’s a reference. In other words, a function with one parameter -gets one lifetime parameter: `fn foo<'a>(x: &'a i32)`; a function with two -parameters gets two separate lifetime parameters: `fn foo<'a, 'b>(x: &'a i32, -y: &'b i32)`; and so on. - -The second rule is that, if there is exactly one input lifetime parameter, that -lifetime is assigned to all output lifetime parameters: `fn foo<'a>(x: &'a i32) --> &'a i32`. - -The third rule is that, if there are multiple input lifetime parameters, but -one of them is `&self` or `&mut self` because this is a method, the lifetime of -`self` is assigned to all output lifetime parameters. This third rule makes -methods much nicer to read and write because fewer symbols are necessary. - -Let’s pretend we’re the compiler. We’ll apply these rules to figure out the -lifetimes of the references in the signature of the `first_word` function in -Listing 10-25. The signature starts without any lifetimes associated with the -references: +پس از نوشتن مقدار زیادی کد Rust، تیم Rust متوجه شد که برنامه‌نویسان Rust در موقعیت‌های خاصی حاشیه‌نویسی‌های طول عمر یکسانی را بارها و بارها وارد می‌کردند. این موقعیت‌ها قابل پیش‌بینی بودند و از چند الگوی تعیین‌کننده پیروی می‌کردند. توسعه‌دهندگان این الگوها را در کد کامپایلر برنامه‌ریزی کردند تا بررسی‌کننده قرض بتواند طول عمرها را در این موقعیت‌ها استنتاج کند و نیازی به حاشیه‌نویسی صریح نباشد. + +این بخش از تاریخ Rust مرتبط است زیرا ممکن است الگوهای تعیین‌کننده بیشتری ظاهر شوند و به کامپایلر اضافه شوند. در آینده، حتی حاشیه‌نویسی‌های طول عمر کمتری ممکن است لازم باشد. + +الگوهایی که در تحلیل مراجع Rust برنامه‌ریزی شده‌اند _قوانین حذف طول عمر_ (lifetime elision rules) نامیده می‌شوند. این‌ها قوانینی نیستند که برنامه‌نویسان باید رعایت کنند؛ بلکه مجموعه‌ای از موارد خاص هستند که کامپایلر آن‌ها را در نظر می‌گیرد و اگر کد شما با این موارد مطابقت داشته باشد، نیازی به نوشتن طول عمرها به صورت صریح نخواهید داشت. + +قواعد elision نمی‌توانند inference کامل انجام دهند. +اگر پس از اعمال این قواعد همچنان ابهامی درباره‌ی lifetime رفرنس‌ها وجود داشته باشد، +کامپایلر حدس نمی‌زند که lifetime باقی‌مانده‌ها باید چه باشد. +به جای حدس زدن، کامپایلر خطایی نمایش می‌دهد که می‌توانید با اضافه کردن annotationهای lifetime آن را رفع کنید. + +طول عمرهای روی پارامترهای تابع یا متد _طول عمر ورودی_ (input lifetimes) نامیده می‌شوند، و طول عمرهای روی مقادیر بازگشتی _طول عمر خروجی_ (output lifetimes) نامیده می‌شوند. + +کامپایلر از سه قانون برای تشخیص طول عمر مراجع استفاده می‌کند وقتی که حاشیه‌نویسی‌های صریح وجود ندارند. قانون اول برای طول عمرهای ورودی اعمال می‌شود، و قانون دوم و سوم برای طول عمرهای خروجی. اگر کامپایلر به انتهای این سه قانون برسد و هنوز مراجع وجود داشته باشند که نتواند طول عمرهای آن‌ها را تشخیص دهد، کامپایلر با یک خطا متوقف می‌شود. این قوانین به تعاریف `fn` و همچنین بلوک‌های `impl` اعمال می‌شوند. + +- **قانون اول**: کامپایلر یک پارامتر طول عمر به هر پارامتر که یک مرجع است اختصاص می‌دهد. به عبارت دیگر، یک تابع با یک پارامتر یک پارامتر طول عمر می‌گیرد: `fn foo<'a>(x: &'a i32)`؛ یک تابع با دو پارامتر دو پارامتر طول عمر جداگانه می‌گیرد: `fn foo<'a, 'b>(x: &'a i32, y: &'b i32)`؛ و به همین ترتیب. +- **قانون دوم**: اگر دقیقاً یک پارامتر طول عمر ورودی وجود داشته باشد، آن طول عمر به تمام پارامترهای طول عمر خروجی اختصاص داده می‌شود: `fn foo<'a>(x: &'a i32) -> &'a i32`. +- **قانون سوم**: اگر چندین پارامتر طول عمر ورودی وجود داشته باشد، اما یکی از آن‌ها `&self` یا `&mut self` باشد زیرا این یک متد است، طول عمر `self` به تمام پارامترهای طول عمر خروجی اختصاص داده می‌شود. این قانون سوم خواندن و نوشتن متدها را بسیار آسان‌تر می‌کند زیرا نمادهای کمتری لازم است. + +بیایید وانمود کنیم که ما کامپایلر هستیم. این قوانین را برای تشخیص طول عمر مراجع در امضای تابع `first_word` در لیست ۱۰-۲۵ اعمال می‌کنیم. امضا بدون هیچ طول عمری که با مراجع مرتبط باشد شروع می‌شود: ```rust,ignore fn first_word(s: &str) -> &str { ``` -Then the compiler applies the first rule, which specifies that each parameter -gets its own lifetime. We’ll call it `'a` as usual, so now the signature is -this: +سپس کامپایلر قانون اول را اعمال می‌کند که مشخص می‌کند هر پارامتر طول عمر خاص خود را دریافت می‌کند. ما آن را طبق معمول `'a` می‌نامیم، بنابراین امضا اکنون به این صورت است: ```rust,ignore fn first_word<'a>(s: &'a str) -> &str { ``` -The second rule applies because there is exactly one input lifetime. The second -rule specifies that the lifetime of the one input parameter gets assigned to -the output lifetime, so the signature is now this: +قانون دوم اعمال می‌شود زیرا دقیقاً یک طول عمر ورودی وجود دارد. قانون دوم مشخص می‌کند که طول عمر یک پارامتر ورودی به طول عمر خروجی اختصاص داده می‌شود، بنابراین امضا اکنون به این صورت است: ```rust,ignore fn first_word<'a>(s: &'a str) -> &'a str { ``` -Now all the references in this function signature have lifetimes, and the -compiler can continue its analysis without needing the programmer to annotate -the lifetimes in this function signature. +حالا تمام مراجع در این امضای تابع طول عمر دارند و کامپایلر می‌تواند تحلیل خود را بدون نیاز به برنامه‌نویس برای حاشیه‌نویسی طول عمرها در این امضای تابع ادامه دهد. -Let’s look at another example, this time using the `longest` function that had -no lifetime parameters when we started working with it in Listing 10-20: +بیایید به یک مثال دیگر نگاه کنیم، این بار با استفاده از تابع `longest` که در ابتدا هیچ پارامتر طول عمری نداشت، همانطور که در لیست ۱۰-۲۰ کار خود را با آن شروع کردیم: ```rust,ignore fn longest(x: &str, y: &str) -> &str { ``` -Let’s apply the first rule: each parameter gets its own lifetime. This time we -have two parameters instead of one, so we have two lifetimes: +بیایید قانون اول را اعمال کنیم: هر پارامتر طول عمر خاص خود را دریافت می‌کند. این بار دو پارامتر داریم، بنابراین دو طول عمر داریم: ```rust,ignore fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str { ``` -You can see that the second rule doesn’t apply because there is more than one -input lifetime. The third rule doesn’t apply either, because `longest` is a -function rather than a method, so none of the parameters are `self`. After -working through all three rules, we still haven’t figured out what the return -type’s lifetime is. This is why we got an error trying to compile the code in -Listing 10-20: the compiler worked through the lifetime elision rules but still -couldn’t figure out all the lifetimes of the references in the signature. +می‌بینید که قانون دوم اعمال نمی‌شود زیرا بیش از یک طول عمر ورودی وجود دارد. قانون سوم نیز اعمال نمی‌شود زیرا `longest` یک تابع است و نه یک متد، بنابراین هیچ یک از پارامترها `self` نیستند. پس از عبور از تمام سه قانون، هنوز طول عمر نوع بازگشتی را تعیین نکرده‌ایم. به همین دلیل است که هنگام تلاش برای کامپایل کد در لیست ۱۰-۲۰ خطا گرفتیم: کامپایلر قوانین حذف طول عمر را مرور کرد اما همچنان نتوانست تمام طول عمرهای مراجع در امضا را تعیین کند. -Because the third rule really only applies in method signatures, we’ll look at -lifetimes in that context next to see why the third rule means we don’t have to -annotate lifetimes in method signatures very often. +از آنجا که قانون سوم واقعاً فقط در امضاهای متد اعمال می‌شود، به بررسی طول عمرها در آن زمینه می‌پردازیم تا ببینیم چرا قانون سوم باعث می‌شود که اغلب نیازی به حاشیه‌نویسی طول عمر در امضاهای متد نداشته باشیم. -### Lifetime Annotations in Method Definitions +### حاشیه‌نویسی طول عمر در تعریف متدها -When we implement methods on a struct with lifetimes, we use the same syntax as -that of generic type parameters, as shown in Listing 10-11. Where we declare and -use the lifetime parameters depends on whether they’re related to the struct -fields or the method parameters and return values. +وقتی متدهایی را روی یک `struct` دارای lifetime پیاده‌سازی می‌کنیم، +از همان سینتکسی استفاده می‌کنیم که برای پارامترهای نوع generic به کار می‌رود، +همان‌طور که در لیستینگ 10-11 نشان داده شده است. +مکان اعلام و استفاده از پارامترهای lifetime بستگی به این دارد که آیا این lifetimeها +مرتبط با فیلدهای `struct` هستند یا پارامترها و مقادیر بازگشتی متد. -Lifetime names for struct fields always need to be declared after the `impl` -keyword and then used after the struct’s name because those lifetimes are part -of the struct’s type. +نام‌های طول عمر برای فیلدهای ساختار همیشه باید بعد از کلمه کلیدی `impl` اعلام شوند و سپس بعد از نام ساختار استفاده شوند، زیرا این طول عمرها بخشی از نوع ساختار هستند. -In method signatures inside the `impl` block, references might be tied to the -lifetime of references in the struct’s fields, or they might be independent. In -addition, the lifetime elision rules often make it so that lifetime annotations -aren’t necessary in method signatures. Let’s look at some examples using the -struct named `ImportantExcerpt` that we defined in Listing 10-24. +در امضاهای متد در داخل بلوک `impl`، مراجع ممکن است به طول عمر مراجع در فیلدهای ساختار مرتبط باشند، یا ممکن است مستقل باشند. علاوه بر این، قوانین حذف طول عمر اغلب باعث می‌شوند که حاشیه‌نویسی طول عمر در امضاهای متد ضروری نباشد. بیایید به چند مثال با استفاده از ساختار `ImportantExcerpt` که در لیست ۱۰-۲۴ تعریف کردیم، نگاه کنیم. -First we’ll use a method named `level` whose only parameter is a reference to -`self` and whose return value is an `i32`, which is not a reference to anything: +ابتدا از متدی به نام `level` استفاده می‌کنیم که تنها پارامتر آن مرجعی به `self` است و مقدار بازگشتی آن یک `i32` است که به چیزی اشاره نمی‌کند: ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-10-lifetimes-on-methods/src/main.rs:1st}} ``` -The lifetime parameter declaration after `impl` and its use after the type name -are required, but we’re not required to annotate the lifetime of the reference -to `self` because of the first elision rule. +اعلام پارامتر طول عمر بعد از `impl` و استفاده از آن بعد از نام نوع الزامی است، اما ما نیازی به حاشیه‌نویسی طول عمر مرجع به `self` نداریم زیرا قانون اول حذف اعمال می‌شود. -Here is an example where the third lifetime elision rule applies: +در اینجا مثالی است که قانون سوم حذف طول عمر اعمال می‌شود: ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-10-lifetimes-on-methods/src/main.rs:3rd}} ``` -There are two input lifetimes, so Rust applies the first lifetime elision rule -and gives both `&self` and `announcement` their own lifetimes. Then, because -one of the parameters is `&self`, the return type gets the lifetime of `&self`, -and all lifetimes have been accounted for. +دو طول عمر ورودی وجود دارد، بنابراین Rust قانون اول حذف طول عمر را اعمال می‌کند و طول عمرهای جداگانه‌ای به `&self` و `announcement` می‌دهد. سپس، چون یکی از پارامترها `&self` است، نوع بازگشتی طول عمر `&self` را دریافت می‌کند، و تمام طول عمرها در نظر گرفته شده‌اند. -### The Static Lifetime +### طول عمر استاتیک -One special lifetime we need to discuss is `'static`, which denotes that the -affected reference _can_ live for the entire duration of the program. All -string literals have the `'static` lifetime, which we can annotate as follows: +یک طول عمر خاص که باید درباره آن صحبت کنیم `'static` است، که نشان می‌دهد مرجع مورد نظر _می‌تواند_ برای کل مدت اجرای برنامه زنده بماند. تمام رشته‌های لیتری دارای طول عمر `'static` هستند، که می‌توانیم آن را به این صورت حاشیه‌نویسی کنیم: ```rust let s: &'static str = "I have a static lifetime."; ``` -The text of this string is stored directly in the program’s binary, which is -always available. Therefore, the lifetime of all string literals is `'static`. +متن این رشته مستقیماً در باینری برنامه ذخیره می‌شود، که همیشه در دسترس است. بنابراین، طول عمر تمام رشته‌های لیتری `'static` است. -You might see suggestions in error messages to use the `'static` lifetime. But -before specifying `'static` as the lifetime for a reference, think about -whether the reference you have actually lives the entire lifetime of your -program or not, and whether you want it to. Most of the time, an error message -suggesting the `'static` lifetime results from attempting to create a dangling -reference or a mismatch of the available lifetimes. In such cases, the solution -is to fix those problems, not to specify the `'static` lifetime. +ممکن است در پیام‌های خطا پیشنهادهایی برای استفاده از طول عمر `'static` ببینید. اما قبل از مشخص کردن طول عمر `'static` برای یک مرجع، فکر کنید که آیا مرجعی که دارید واقعاً برای کل مدت اجرای برنامه زنده است یا خیر، و آیا می‌خواهید چنین باشد. بیشتر اوقات، یک پیام خطا که طول عمر `'static` را پیشنهاد می‌دهد نتیجه تلاش برای ایجاد یک مرجع آویزان یا ناسازگاری طول عمرهای موجود است. در چنین مواردی، راه حل این است که این مشکلات را برطرف کنید، نه اینکه طول عمر `'static` را مشخص کنید. -## Generic Type Parameters, Trait Bounds, and Lifetimes Together +## پارامترهای نوع جنریک، محدودیت ویژگی، و طول عمرها با هم -Let’s briefly look at the syntax of specifying generic type parameters, trait -bounds, and lifetimes all in one function! +بیایید به طور مختصر به نحو مشخص کردن پارامترهای نوع جنریک، محدودیت ویژگی، و طول عمرها در یک تابع نگاه کنیم! ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-11-generics-traits-and-lifetimes/src/main.rs:here}} ``` -This is the `longest` function from Listing 10-21 that returns the longer of -two string slices. But now it has an extra parameter named `ann` of the generic -type `T`, which can be filled in by any type that implements the `Display` -trait as specified by the `where` clause. This extra parameter will be printed -using `{}`, which is why the `Display` trait bound is necessary. Because -lifetimes are a type of generic, the declarations of the lifetime parameter -`'a` and the generic type parameter `T` go in the same list inside the angle -brackets after the function name. - -## Summary - -We covered a lot in this chapter! Now that you know about generic type -parameters, traits and trait bounds, and generic lifetime parameters, you’re -ready to write code without repetition that works in many different situations. -Generic type parameters let you apply the code to different types. Traits and -trait bounds ensure that even though the types are generic, they’ll have the -behavior the code needs. You learned how to use lifetime annotations to ensure -that this flexible code won’t have any dangling references. And all of this -analysis happens at compile time, which doesn’t affect runtime performance! - -Believe it or not, there is much more to learn on the topics we discussed in -this chapter: Chapter 18 discusses trait objects, which are another way to use -traits. There are also more complex scenarios involving lifetime annotations -that you will only need in very advanced scenarios; for those, you should read -the [Rust Reference][reference]. But next, you’ll learn how to write tests in -Rust so you can make sure your code is working the way it should. - -[references-and-borrowing]: ch04-02-references-and-borrowing.html#references-and-borrowing -[string-slices-as-parameters]: ch04-03-slices.html#string-slices-as-parameters -[reference]: ../reference/index.html +این تابع `longest` از لیست ۱۰-۲۱ است که طولانی‌ترین قطعه رشته را بازمی‌گرداند. اما اکنون یک پارامتر اضافی به نام `ann` دارد که از نوع جنریک `T` است، که می‌تواند با هر نوعی که ویژگی `Display` را پیاده‌سازی می‌کند، پر شود، همانطور که توسط بند `where` مشخص شده است. این پارامتر اضافی با استفاده از `{}` چاپ خواهد شد، به همین دلیل محدودیت ویژگی `Display` ضروری است. از آنجا که طول عمرها نوعی جنریک هستند، اعلام طول عمر `'a` و پارامتر نوع جنریک `T` در همان لیست داخل پرانتزهای زاویه‌ای بعد از نام تابع قرار می‌گیرند. + +## خلاصه + +در این فصل مطالب زیادی را پوشش دادیم! حالا که با پارامترهای نوع جنریک، ویژگی‌ها و محدودیت‌های ویژگی، و پارامترهای طول عمر جنریک آشنا شدید، آماده هستید تا کدی بدون تکرار بنویسید که در بسیاری از موقعیت‌های مختلف کار کند. پارامترهای نوع جنریک به شما اجازه می‌دهند که کد را روی انواع مختلف اعمال کنید. ویژگی‌ها و محدودیت‌های ویژگی اطمینان حاصل می‌کنند که حتی با اینکه نوع‌ها جنریک هستند، رفتار مورد نیاز کد را خواهند داشت. شما یاد گرفتید چگونه از حاشیه‌نویسی طول عمر استفاده کنید تا اطمینان حاصل شود که این کد انعطاف‌پذیر هیچ مرجع آویزانی نخواهد داشت. و تمام این تحلیل‌ها در زمان کامپایل انجام می‌شود، که بر عملکرد زمان اجرا تأثیری ندارد! + +باور کنید یا نه، مطالب بیشتری برای یادگیری در مورد موضوعاتی که در این فصل بحث شد وجود دارد: فصل ۱۸ به اشیاء ویژگی (trait objects) می‌پردازد، که راه دیگری برای استفاده از ویژگی‌ها است. همچنین سناریوهای پیچیده‌تری وجود دارد که شامل حاشیه‌نویسی طول عمر هستند و فقط در سناریوهای بسیار پیشرفته به آن‌ها نیاز خواهید داشت. برای این موارد، باید [مرجع Rust][reference] را مطالعه کنید. اما بعد از این، یاد خواهید گرفت که چگونه تست‌هایی در Rust بنویسید تا مطمئن شوید کد شما همانطور که باید کار می‌کند. + +[references-and-borrowing]: ch04-02-references-and-borrowing.html#references-and-borrowing +[string-slices-as-parameters]: ch04-03-slices.html#string-slices-as-parameters +[reference]: ../reference/index.html \ No newline at end of file diff --git a/src/ch11-00-testing.md b/src/ch11-00-testing.md index b227072ef3..158b9dadad 100644 --- a/src/ch11-00-testing.md +++ b/src/ch11-00-testing.md @@ -1,33 +1,12 @@ -# Writing Automated Tests +# نوشتن تست‌های خودکار -In his 1972 essay “The Humble Programmer,” Edsger W. Dijkstra said that “program -testing can be a very effective way to show the presence of bugs, but it is -hopelessly inadequate for showing their absence.” That doesn’t mean we shouldn’t -try to test as much as we can! +در مقاله‌ی خود در سال ۱۹۷۲ با عنوان «برنامه‌نویس فروتن»، اَدسگر و. دایکسترا بیان کرد که «تست برنامه می‌تواند راهی بسیار مؤثر برای نشان دادن وجود باگ‌ها باشد، اما برای اثبات عدم وجود آن‌ها کاملاً ناکافی است.» +این بدان معنا نیست که نباید تا حد امکان تلاش کنیم برنامه را تست کنیم! -Correctness in our programs is the extent to which our code does what we intend -it to do. Rust is designed with a high degree of concern about the correctness -of programs, but correctness is complex and not easy to prove. Rust’s type -system shoulders a huge part of this burden, but the type system cannot catch -everything. As such, Rust includes support for writing automated software tests. +درستی در برنامه‌های ما میزان انطباق کد ما با آنچه که قصد انجامش را داریم، است. Rust با نگرانی بالایی درباره درستی برنامه‌ها طراحی شده است، اما درستی پیچیده و اثبات آن آسان نیست. سیستم نوع Rust بخش عظیمی از این بار را به دوش می‌کشد، اما سیستم نوع نمی‌تواند همه چیز را پوشش دهد. به همین دلیل، Rust شامل پشتیبانی برای نوشتن تست‌های خودکار نرم‌افزار است. -Say we write a function `add_two` that adds 2 to whatever number is passed to -it. This function’s signature accepts an integer as a parameter and returns an -integer as a result. When we implement and compile that function, Rust does all -the type checking and borrow checking that you’ve learned so far to ensure -that, for instance, we aren’t passing a `String` value or an invalid reference -to this function. But Rust _can’t_ check that this function will do precisely -what we intend, which is return the parameter plus 2 rather than, say, the -parameter plus 10 or the parameter minus 50! That’s where tests come in. +فرض کنید یک تابع به نام `add_two` می‌نویسیم که ۲ را به هر عددی که به آن پاس داده شود اضافه می‌کند. امضای این تابع یک عدد صحیح به عنوان پارامتر می‌پذیرد و یک عدد صحیح به عنوان نتیجه بازمی‌گرداند. هنگامی که این تابع را پیاده‌سازی و کامپایل می‌کنیم، Rust تمام بررسی‌های نوع و قرض‌گیری را که تا کنون آموخته‌اید انجام می‌دهد تا اطمینان حاصل شود که، به عنوان مثال، ما یک مقدار `String` یا یک مرجع نامعتبر را به این تابع پاس نمی‌دهیم. اما Rust _نمی‌تواند_ بررسی کند که این تابع دقیقاً همان کاری را که ما قصد داریم انجام دهد، که بازگرداندن پارامتر به علاوه ۲ است نه مثلاً پارامتر به علاوه ۱۰ یا پارامتر منهای ۵۰! اینجا جایی است که تست‌ها وارد می‌شوند. -We can write tests that assert, for example, that when we pass `3` to the -`add_two` function, the returned value is `5`. We can run these tests whenever -we make changes to our code to make sure any existing correct behavior has not -changed. +ما می‌توانیم تست‌هایی بنویسیم که، به عنوان مثال، تأیید می‌کنند که وقتی `3` را به تابع `add_two` پاس می‌دهیم، مقدار بازگردانده شده `5` است. می‌توانیم این تست‌ها را هر زمان که تغییری در کد خود ایجاد می‌کنیم اجرا کنیم تا مطمئن شویم که هر رفتار درستی که وجود داشته تغییر نکرده است. -Testing is a complex skill: although we can’t cover in one chapter every detail -about how to write good tests, in this chapter we will discuss the mechanics of -Rust’s testing facilities. We’ll talk about the annotations and macros -available to you when writing your tests, the default behavior and options -provided for running your tests, and how to organize tests into unit tests and -integration tests. +تست‌نویسی یک مهارت پیچیده است: اگرچه نمی‌توانیم در یک فصل تمام جزئیات مربوط به نحوه نوشتن تست‌های خوب را پوشش دهیم، در این فصل درباره مکانیک تسهیلات تست Rust بحث خواهیم کرد. درباره حاشیه‌نویسی‌ها و ماکروهایی که هنگام نوشتن تست‌ها در اختیار دارید صحبت خواهیم کرد، رفتار پیش‌فرض و گزینه‌های ارائه‌شده برای اجرای تست‌ها را بررسی خواهیم کرد، و نحوه سازماندهی تست‌ها به تست‌های واحد و تست‌های یکپارچه را یاد خواهیم گرفت. diff --git a/src/ch11-01-writing-tests.md b/src/ch11-01-writing-tests.md index 26d411d33b..3d544272e0 100644 --- a/src/ch11-01-writing-tests.md +++ b/src/ch11-01-writing-tests.md @@ -1,38 +1,22 @@ -## How to Write Tests +## چگونه تست بنویسیم -Tests are Rust functions that verify that the non-test code is functioning in -the expected manner. The bodies of test functions typically perform these three -actions: +تست‌ها توابعی در Rust هستند که بررسی می‌کنند کد غیرتستی به شکل مورد انتظار کار می‌کند. بدنه توابع تست معمولاً این سه عمل را انجام می‌دهد: -- Set up any needed data or state. -- Run the code you want to test. -- Assert that the results are what you expect. +- تنظیم هر داده یا وضعیت مورد نیاز. +- اجرای کدی که می‌خواهید تست کنید. +- تأیید اینکه نتایج همان چیزی است که انتظار دارید. -Let’s look at the features Rust provides specifically for writing tests that -take these actions, which include the `test` attribute, a few macros, and the -`should_panic` attribute. +بیایید به ویژگی‌هایی که Rust به طور خاص برای نوشتن تست‌هایی که این اقدامات را انجام می‌دهند فراهم کرده است نگاهی بیندازیم. این ویژگی‌ها شامل ویژگی `test`، چند ماکرو و ویژگی `should_panic` هستند. -### The Anatomy of a Test Function +### آناتومی یک تابع تست -At its simplest, a test in Rust is a function that’s annotated with the `test` -attribute. Attributes are metadata about pieces of Rust code; one example is -the `derive` attribute we used with structs in Chapter 5. To change a function -into a test function, add `#[test]` on the line before `fn`. When you run your -tests with the `cargo test` command, Rust builds a test runner binary that runs -the annotated functions and reports on whether each test function passes or -fails. +در ساده‌ترین حالت، یک تست در Rust یک تابع است که با ویژگی `test` حاشیه‌نویسی شده است. ویژگی‌ها متاداده‌هایی درباره بخش‌های کد Rust هستند؛ یک مثال ویژگی `derive` است که در فصل ۵ با ساختارها استفاده کردیم. برای تغییر یک تابع به یک تابع تست، `#[test]` را به خط قبل از `fn` اضافه کنید. وقتی تست‌های خود را با فرمان `cargo test` اجرا می‌کنید، Rust یک باینری تست رانر ایجاد می‌کند که توابع حاشیه‌نویسی‌شده را اجرا می‌کند و گزارش می‌دهد که آیا هر تابع تست موفق یا ناموفق بوده است. -Whenever we make a new library project with Cargo, a test module with a test -function in it is automatically generated for us. This module gives you a -template for writing your tests so you don’t have to look up the exact -structure and syntax every time you start a new project. You can add as many -additional test functions and as many test modules as you want! +هر زمان که یک پروژه کتابخانه‌ای جدید با Cargo ایجاد می‌کنیم، یک ماژول تست با یک تابع تست در آن به صورت خودکار برای ما تولید می‌شود. این ماژول یک قالب برای نوشتن تست‌های شما فراهم می‌کند تا نیازی به جستجوی ساختار و نحو دقیق هر بار که یک پروژه جدید شروع می‌کنید نداشته باشید. می‌توانید هر تعداد تابع تست اضافی و هر تعداد ماژول تست اضافی که می‌خواهید اضافه کنید! -We’ll explore some aspects of how tests work by experimenting with the template -test before we actually test any code. Then we’ll write some real-world tests -that call some code that we’ve written and assert that its behavior is correct. +ما برخی از جنبه‌های نحوه عملکرد تست‌ها را با آزمایش قالب تست قبل از اینکه واقعاً کدی را تست کنیم بررسی خواهیم کرد. سپس تست‌هایی در دنیای واقعی می‌نویسیم که برخی کدهایی که نوشته‌ایم را فراخوانی می‌کنند و تأیید می‌کنند که رفتار آن صحیح است. -Let’s create a new library project called `adder` that will add two numbers: +بیایید یک پروژه کتابخانه‌ای جدید به نام `adder` ایجاد کنیم که دو عدد را با هم جمع کند: ```console $ cargo new adder --lib @@ -40,10 +24,9 @@ $ cargo new adder --lib $ cd adder ``` -The contents of the _src/lib.rs_ file in your `adder` library should look like -Listing 11-1. +محتویات فایل _src/lib.rs_ در کتابخانه `adder` شما باید شبیه به لیست ۱۱-۱ باشد. -+ section later in this chapter. Because we -haven’t done that here, the summary shows `0 ignored`. We can also pass an -argument to the `cargo test` command to run only tests whose name matches a -string; this is called _filtering_ and we’ll cover it in the [“Running a -Subset of Tests by Name”][subset] section. Here we haven’t -filtered the tests being run, so the end of the summary shows `0 filtered out`. - -The `0 measured` statistic is for benchmark tests that measure performance. -Benchmark tests are, as of this writing, only available in nightly Rust. See -[the documentation about benchmark tests][bench] to learn more. - -The next part of the test output starting at `Doc-tests adder` is for the -results of any documentation tests. We don’t have any documentation tests yet, -but Rust can compile any code examples that appear in our API documentation. -This feature helps keep your docs and your code in sync! We’ll discuss how to -write documentation tests in the [“Documentation Comments as -Tests”][doc-comments] section of Chapter 14. For now, we’ll -ignore the `Doc-tests` output. - -Let’s start to customize the test to our own needs. First, change the name of -the `it_works` function to a different name, such as `exploration`, like so: +Cargo تست را کامپایل و اجرا کرد. خط `running 1 test` را می‌بینیم. خط بعدی نام تابع تست تولیدشده را نشان می‌دهد، که `tests::it_works` نام دارد، و نتیجه اجرای آن تست `ok` است. خلاصه کلی `test result: ok.` نشان می‌دهد که تمام تست‌ها پاس شده‌اند، و بخشی که `1 passed; 0 failed` را می‌خواند تعداد تست‌هایی که پاس شده‌اند یا ناموفق بوده‌اند را نشان می‌دهد. + +امکان علامت‌گذاری یک تست به‌عنوان ignored وجود دارد تا در یک اجرای خاص اجرا نشود؛ +ما این موضوع را در بخش [“نادیده گرفتن برخی تست‌ها مگر در صورت درخواست خاص”][ignoring] +در ادامه‌ی این فصل بررسی خواهیم کرد. +چون در اینجا این کار را انجام نداده‌ایم، خلاصه نشان‌دهنده‌ی `0 ignored` است. +همچنین می‌توانیم آرگومانی به دستور `cargo test` بدهیم تا فقط تست‌هایی اجرا شوند که نامشان با رشته‌ای مطابقت دارد؛ +این کار _filtering_ نامیده می‌شود و در بخش [“اجرای زیرمجموعه‌ای از تست‌ها بر اساس نام”][subset] بررسی خواهد شد. +در اینجا تست‌ها فیلتر نشده‌اند، بنابراین انتهای خلاصه `0 filtered out` را نشان می‌دهد. + +آمار `0 measured` برای تست‌های بنچمارک است که عملکرد را اندازه‌گیری می‌کنند. تست‌های بنچمارک، در زمان نوشتن این متن، فقط در نسخه شبانه Rust موجود هستند. برای اطلاعات بیشتر مستندات مربوط به [تست‌های بنچمارک][bench] را ببینید. + +قسمت بعدی خروجی تست که از `Doc-tests adder` شروع می‌شود، مربوط به نتایج تست‌های مستندات است. +فعلاً تست مستنداتی نداریم، اما Rust می‌تواند هر نمونه کدی که در مستندات API ما آمده است را کامپایل کند. +این ویژگی به هماهنگ نگه‌داشتن مستندات و کد شما کمک می‌کند! +در بخش [“نظرات مستندات به‌عنوان تست”][doc-comments] در فصل ۱۴، نحوه‌ی نوشتن تست‌های مستندات را بررسی خواهیم کرد. +فعلاً خروجی `Doc-tests` را نادیده می‌گیریم. + +بیایید تست را مطابق نیازهای خود شخصی‌سازی کنیم. ابتدا نام تابع `it_works` را به یک نام دیگر، مانند `exploration` تغییر دهید، به این صورت: Filename: src/lib.rs @@ -123,21 +88,15 @@ the `it_works` function to a different name, such as `exploration`, like so: {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-01-changing-test-name/src/lib.rs}} ``` -Then run `cargo test` again. The output now shows `exploration` instead of -`it_works`: +سپس دوباره `cargo test` را اجرا کنید. خروجی اکنون به جای `it_works` نام `exploration` را نشان می‌دهد: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-01-changing-test-name/output.txt}} ``` -Now we’ll add another test, but this time we’ll make a test that fails! Tests -fail when something in the test function panics. Each test is run in a new -thread, and when the main thread sees that a test thread has died, the test is -marked as failed. In Chapter 9, we talked about how the simplest way to panic -is to call the `panic!` macro. Enter the new test as a function named -`another`, so your _src/lib.rs_ file looks like Listing 11-3. +حالا یک تست دیگر اضافه می‌کنیم، اما این بار تستی می‌نویسیم که شکست بخورد! تست‌ها زمانی شکست می‌خورند که چیزی در تابع تست باعث ایجاد panic شود. هر تست در یک نخ (thread) جدید اجرا می‌شود، و وقتی نخ اصلی می‌بیند که یک نخ تست متوقف شده است، تست به عنوان شکست‌خورده علامت‌گذاری می‌شود. در فصل ۹، درباره اینکه ساده‌ترین راه برای panic کردن فراخوانی ماکروی `panic!` است صحبت کردیم. تابع جدیدی به نام `another` وارد کنید تا فایل _src/lib.rs_ شما شبیه به لیست ۱۱-۳ شود. -+ ```rust,panics,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-03/src/lib.rs}} @@ -145,10 +104,9 @@ is to call the `panic!` macro. Enter the new test as a function named -Run the tests again using `cargo test`. The output should look like Listing -11-4, which shows that our `exploration` test passed and `another` failed. +دوباره تست‌ها را با استفاده از `cargo test` اجرا کنید. خروجی باید شبیه به لیست ۱۱-۴ باشد، که نشان می‌دهد تست `exploration` موفق شده است و `another` شکست خورده است. -+ ```console {{#include ../listings/ch11-writing-automated-tests/listing-11-03/output.txt}} @@ -156,42 +114,25 @@ Run the tests again using `cargo test`. The output should look like Listing - - -Instead of `ok`, the line `test tests::another` shows `FAILED`. Two new -sections appear between the individual results and the summary: the first -displays the detailed reason for each test failure. In this case, we get the -details that `tests::another` failed because it panicked with the message `Make -this test fail` on line 17 in the _src/lib.rs_ file. The next section lists -just the names of all the failing tests, which is useful when there are lots of -tests and lots of detailed failing test output. We can use the name of a -failing test to run just that test to more easily debug it; we’ll talk more -about ways to run tests in the [“Controlling How Tests Are -Run”][controlling-how-tests-are-run] section. - -The summary line displays at the end: overall, our test result is `FAILED`. We -had one test pass and one test fail. - -Now that you’ve seen what the test results look like in different scenarios, -let’s look at some macros other than `panic!` that are useful in tests. - -### Checking Results with the `assert!` Macro - -The `assert!` macro, provided by the standard library, is useful when you want -to ensure that some condition in a test evaluates to `true`. We give the -`assert!` macro an argument that evaluates to a Boolean. If the value is -`true`, nothing happens and the test passes. If the value is `false`, the -`assert!` macro calls `panic!` to cause the test to fail. Using the `assert!` -macro helps us check that our code is functioning in the way we intend. - -In Chapter 5, Listing 5-15, we used a `Rectangle` struct and a `can_hold` -method, which are repeated here in Listing 11-5. Let’s put this code in the -_src/lib.rs_ file, then write some tests for it using the `assert!` macro. - -+به جای `ok`، در خط `test tests::another` عبارت `FAILED` نمایش داده می‌شود. +دو بخش جدید بین نتایج فردی و خلاصه ظاهر می‌شوند: +اولی دلیل دقیق هر شکست تست را نمایش می‌دهد. +در این مورد، جزئیات نشان می‌دهد که `tests::another` شکست خورده زیرا در خط ۱۷ فایل *src/lib.rs* +با پیام `Make this test fail` دچار panic شده است. +بخش بعدی فقط نام تمام تست‌های شکست‌خورده را فهرست می‌کند، +که وقتی تعداد تست‌ها زیاد و خروجی شکست تست‌ها مفصل است، مفید است. +می‌توانیم از نام تست شکست‌خورده استفاده کنیم تا فقط آن تست را اجرا کنیم و راحت‌تر آن را اشکال‌زدایی کنیم؛ +در بخش [“کنترل نحوه‌ی اجرای تست‌ها”][controlling-how-tests-are-run] بیشتر درباره‌ی روش‌های اجرای تست صحبت خواهیم کرد. + +حالا که دیدید نتایج تست در سناریوهای مختلف چگونه به نظر می‌رسند، بیایید به برخی از ماکروهای دیگر به جز `panic!` که در تست‌ها مفید هستند نگاهی بیندازیم. + +### بررسی نتایج با ماکروی `assert!` + +ماکروی `assert!` که توسط کتابخانه استاندارد ارائه شده است، زمانی مفید است که بخواهید اطمینان حاصل کنید که یک شرط در یک تست به `true` ارزیابی می‌شود. ماکروی `assert!` یک آرگومان می‌گیرد که به یک مقدار بولی ارزیابی می‌شود. اگر مقدار `true` باشد، هیچ اتفاقی نمی‌افتد و تست پاس می‌شود. اگر مقدار `false` باشد، ماکروی `assert!` فراخوانی `panic!` را انجام می‌دهد تا باعث شکست تست شود. استفاده از ماکروی `assert!` به ما کمک می‌کند تا بررسی کنیم که کد ما همانطور که قصد داریم عمل می‌کند. + +در فصل ۵، لیست ۵-۱۵، از یک ساختار `Rectangle` و یک متد `can_hold` استفاده کردیم، که در لیست ۱۱-۵ دوباره تکرار شده است. این کد را در فایل _src/lib.rs_ قرار دهید، سپس با استفاده از ماکروی `assert!` چند تست برای آن بنویسید. + + ```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-05/src/lib.rs}} @@ -199,13 +140,9 @@ _src/lib.rs_ file, then write some tests for it using the `assert!` macro. -The `can_hold` method returns a Boolean, which means it’s a perfect use case -for the `assert!` macro. In Listing 11-6, we write a test that exercises the -`can_hold` method by creating a `Rectangle` instance that has a width of 8 and -a height of 7 and asserting that it can hold another `Rectangle` instance that -has a width of 5 and a height of 1. +متد `can_hold` یک مقدار بولی بازمی‌گرداند، که به این معنی است که یک مورد استفاده عالی برای ماکروی `assert!` است. در لیست ۱۱-۶، ما تستی می‌نویسیم که متد `can_hold` را با ایجاد یک نمونه از `Rectangle` که عرض ۸ و ارتفاع ۷ دارد آزمایش می‌کند و تأیید می‌کند که می‌تواند نمونه دیگری از `Rectangle` که عرض ۵ و ارتفاع ۱ دارد را در خود جای دهد. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-06/src/lib.rs:here}} @@ -213,26 +150,15 @@ has a width of 5 and a height of 1. -Note the `use super::*;` line inside the `tests` module. The `tests` module is -a regular module that follows the usual visibility rules we covered in Chapter -7 in the [“Paths for Referring to an Item in the Module -Tree”][paths-for-referring-to-an-item-in-the-module-tree] -section. Because the `tests` module is an inner module, we need to bring the -code under test in the outer module into the scope of the inner module. We use -a glob here, so anything we define in the outer module is available to this -`tests` module. +به خط `use super::*;` در داخل ماژول `tests` توجه کنید. ماژول `tests` یک ماژول معمولی است که از قوانین دیدپذیری معمولی که در فصل ۷ در بخش [“مسیرها برای اشاره به یک مورد در درخت ماژول”][paths-for-referring-to-an-item-in-the-module-tree] پوشش دادیم پیروی می‌کند. از آنجا که ماژول `tests` یک ماژول داخلی است، باید کدی که در ماژول خارجی است را به دامنه ماژول داخلی بیاوریم. در اینجا از یک glob استفاده می‌کنیم، بنابراین هر چیزی که در ماژول خارجی تعریف کنیم برای این ماژول `tests` در دسترس است. -We’ve named our test `larger_can_hold_smaller`, and we’ve created the two -`Rectangle` instances that we need. Then we called the `assert!` macro and -passed it the result of calling `larger.can_hold(&smaller)`. This expression is -supposed to return `true`, so our test should pass. Let’s find out! +تست خود را `larger_can_hold_smaller` نام‌گذاری کرده‌ایم، و دو نمونه `Rectangle` که نیاز داشتیم را ایجاد کرده‌ایم. سپس ماکروی `assert!` را فراخوانی کردیم و نتیجه فراخوانی `larger.can_hold(&smaller)` را به آن پاس دادیم. این عبارت قرار است `true` بازگرداند، بنابراین تست ما باید پاس شود. بیایید ببینیم چه اتفاقی می‌افتد! ```console {{#include ../listings/ch11-writing-automated-tests/listing-11-06/output.txt}} ``` -It does pass! Let’s add another test, this time asserting that a smaller -rectangle cannot hold a larger rectangle: +پاس شد! حالا یک تست دیگر اضافه کنیم، این بار تأیید می‌کنیم که یک مستطیل کوچک‌تر نمی‌تواند یک مستطیل بزرگ‌تر را در خود جای دهد: Filename: src/lib.rs @@ -240,50 +166,33 @@ rectangle cannot hold a larger rectangle: {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-02-adding-another-rectangle-test/src/lib.rs:here}} ``` -Because the correct result of the `can_hold` function in this case is `false`, -we need to negate that result before we pass it to the `assert!` macro. As a -result, our test will pass if `can_hold` returns `false`: +از آنجا که نتیجه صحیح تابع `can_hold` در این مورد `false` است، باید آن نتیجه را قبل از پاس دادن به ماکروی `assert!` منفی کنیم. به این ترتیب، تست ما زمانی پاس می‌شود که `can_hold` مقدار `false` را بازگرداند: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-02-adding-another-rectangle-test/output.txt}} ``` -Two tests that pass! Now let’s see what happens to our test results when we -introduce a bug in our code. We’ll change the implementation of the `can_hold` -method by replacing the greater-than sign with a less-than sign when it -compares the widths: +دو تست که پاس می‌شوند! حالا بیایید ببینیم وقتی باگی به کد خود وارد می‌کنیم چه اتفاقی برای نتایج تست ما می‌افتد. پیاده‌سازی متد `can_hold` را با جایگزینی علامت بزرگتر (`>`) با علامت کوچکتر (`<`) هنگام مقایسه عرض‌ها تغییر می‌دهیم: ```rust,not_desired_behavior,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/src/lib.rs:here}} ``` -Running the tests now produces the following: +اجرای تست‌ها اکنون خروجی زیر را تولید می‌کند: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-03-introducing-a-bug/output.txt}} ``` -Our tests caught the bug! Because `larger.width` is `8` and `smaller.width` is -`5`, the comparison of the widths in `can_hold` now returns `false`: 8 is not -less than 5. +تست‌های ما باگ را پیدا کردند! از آنجا که `larger.width` مقدار `8` و `smaller.width` مقدار `5` دارد، مقایسه عرض‌ها در `can_hold` اکنون `false` بازمی‌گرداند: ۸ کمتر از ۵ نیست. -### Testing Equality with the `assert_eq!` and `assert_ne!` Macros +### تست برابری با ماکروهای `assert_eq!` و `assert_ne!` -A common way to verify functionality is to test for equality between the result -of the code under test and the value you expect the code to return. You could -do this by using the `assert!` macro and passing it an expression using the -`==` operator. However, this is such a common test that the standard library -provides a pair of macros—`assert_eq!` and `assert_ne!`—to perform this test -more conveniently. These macros compare two arguments for equality or -inequality, respectively. They’ll also print the two values if the assertion -fails, which makes it easier to see _why_ the test failed; conversely, the -`assert!` macro only indicates that it got a `false` value for the `==` -expression, without printing the values that led to the `false` value. +یک روش معمول برای بررسی عملکرد، تست برابری بین نتیجه کد تحت تست و مقدار مورد انتظار است. می‌توانید این کار را با استفاده از ماکروی `assert!` و پاس دادن یک عبارت با استفاده از عملگر `==` انجام دهید. با این حال، این یک تست بسیار معمول است که کتابخانه استاندارد یک جفت ماکرو—`assert_eq!` و `assert_ne!`—برای انجام این تست به صورت راحت‌تر فراهم کرده است. این ماکروها به ترتیب دو آرگومان را برای برابری یا نابرابری مقایسه می‌کنند. اگر ادعا شکست بخورد، این ماکروها دو مقدار را نیز چاپ می‌کنند، که مشاهده دلیل شکست تست را آسان‌تر می‌کند. در مقابل، ماکروی `assert!` فقط نشان می‌دهد که یک مقدار `false` برای عبارت `==` دریافت کرده است، بدون چاپ مقادیری که منجر به مقدار `false` شده‌اند. -In Listing 11-7, we write a function named `add_two` that adds `2` to its -parameter, then we test this function using the `assert_eq!` macro. +در لیست ۱۱-۷، تابعی به نام `add_two` می‌نویسیم که `۲` را به پارامتر خود اضافه می‌کند، سپس این تابع را با استفاده از ماکروی `assert_eq!` تست می‌کنیم. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-07/src/lib.rs}} @@ -291,80 +200,53 @@ parameter, then we test this function using the `assert_eq!` macro. -Let’s check that it passes! +بیایید بررسی کنیم که آیا پاس می‌شود! ```console {{#include ../listings/ch11-writing-automated-tests/listing-11-07/output.txt}} ``` -We create a variable named `result` that holds the result of calling -`add_two(2)`. Then we pass `result` and `4` as the arguments to the -`assert_eq!` macro. The output line for this test is `test tests::it_adds_two -... ok`, and the `ok` text indicates that our test passed! - -Let’s introduce a bug into our code to see what `assert_eq!` looks like when it -fails. Change the implementation of the `add_two` function to instead add `3`: +ما متغیری به نام `result` ایجاد می‌کنیم که نتیجه‌ی فراخوانی `add_two(2)` را نگه می‌دارد. +سپس `result` و عدد `4` را به‌عنوان آرگومان به ماکروی `assert_eq!` می‌دهیم. +خط خروجی این تست به شکل `test tests::it_adds_two ... ok` است، +و متن `ok` نشان می‌دهد که تست ما با موفقیت گذشت! ```rust,not_desired_behavior,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/src/lib.rs:here}} ``` -Run the tests again: +تست‌ها را دوباره اجرا کنید: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-04-bug-in-add-two/output.txt}} ``` -Our test caught the bug! The `tests::it_adds_two` test failed, and the message -tells us that the assertion that failed was `left == right` and what the `left` -and `right` values are. This message helps us start debugging: the `left` -argument, where we had the result of calling `add_two(2)`, was `5` but the -`right` argument was `4`. You can imagine that this would be especially helpful -when we have a lot of tests going on. - -Note that in some languages and test frameworks, the parameters to equality -assertion functions are called `expected` and `actual`, and the order in which -we specify the arguments matters. However, in Rust, they’re called `left` and -`right`, and the order in which we specify the value we expect and the value -the code produces doesn’t matter. We could write the assertion in this test as -`assert_eq!(4, result)`, which would result in the same failure message that -displays `` assertion `left == right` failed``. - -The `assert_ne!` macro will pass if the two values we give it are not equal and -fail if they’re equal. This macro is most useful for cases when we’re not sure -what a value _will_ be, but we know what the value definitely _shouldn’t_ be. -For example, if we’re testing a function that is guaranteed to change its input -in some way, but the way in which the input is changed depends on the day of -the week that we run our tests, the best thing to assert might be that the -output of the function is not equal to the input. - -Under the surface, the `assert_eq!` and `assert_ne!` macros use the operators -`==` and `!=`, respectively. When the assertions fail, these macros print their -arguments using debug formatting, which means the values being compared must -implement the `PartialEq` and `Debug` traits. All primitive types and most of -the standard library types implement these traits. For structs and enums that -you define yourself, you’ll need to implement `PartialEq` to assert equality of -those types. You’ll also need to implement `Debug` to print the values when the -assertion fails. Because both traits are derivable traits, as mentioned in -Listing 5-12 in Chapter 5, this is usually as straightforward as adding the -`#[derive(PartialEq, Debug)]` annotation to your struct or enum definition. See -Appendix C, [“Derivable Traits,”][derivable-traits] for more -details about these and other derivable traits. - -### Adding Custom Failure Messages - -You can also add a custom message to be printed with the failure message as -optional arguments to the `assert!`, `assert_eq!`, and `assert_ne!` macros. Any -arguments specified after the required arguments are passed along to the -`format!` macro (discussed in [“Concatenation with the `+` Operator or the -`format!` Macro”][concatenation-with-the--operator-or-the-format-macro] in Chapter 8), so you can pass a format string that contains `{}` -placeholders and values to go in those placeholders. Custom messages are useful -for documenting what an assertion means; when a test fails, you’ll have a better -idea of what the problem is with the code. - -For example, let’s say we have a function that greets people by name and we -want to test that the name we pass into the function appears in the output: +تست ما باگ را پیدا کرد! تست `tests::it_adds_two` شکست خورد، +و پیام نشان می‌دهد که ادعای ناموفق `left == right` بوده است و مقدارهای `left` و `right` چیستند. +این پیام به ما کمک می‌کند تا فرآیند اشکال‌زدایی را شروع کنیم: +آرگومان `left` که نتیجه‌ی فراخوانی `add_two(2)` بود، مقدار `5` داشت، +اما آرگومان `right` مقدار `4` بود. +می‌توانید تصور کنید که این موضوع زمانی که تعداد زیادی تست اجرا می‌شود، چقدر مفید است. + +توجه کنید که در برخی زبان‌ها و فریم‌ورک‌های تست، پارامترهای تابع ادعای برابری (assertion) +به نام‌های `expected` و `actual` شناخته می‌شوند و ترتیب آرگومان‌ها اهمیت دارد. +اما در Rust، این پارامترها `left` و `right` نامیده می‌شوند و ترتیب مقدار مورد انتظار و مقدار تولید شده توسط کد اهمیت ندارد. +می‌توانیم ادعای این تست را به صورت `assert_eq!(4, result)` نیز بنویسیم، +که نتیجه‌ی همان پیام شکست با عنوان `` assertion `left == right` failed`` را خواهد داشت. + +ماکروی `assert_ne!` زمانی پاس می‌شود که دو مقداری که به آن می‌دهیم برابر نباشند و شکست می‌خورد اگر برابر باشند. این ماکرو برای مواردی مفید است که مطمئن نیستیم یک مقدار _چه خواهد بود_، اما می‌دانیم که مقدار به طور قطع _چه نباید باشد_. برای مثال، اگر تابعی را تست می‌کنیم که تضمین شده است ورودی خود را به نوعی تغییر دهد، اما نحوه تغییر ورودی به روز هفته‌ای که تست‌های خود را اجرا می‌کنیم بستگی دارد، بهترین چیزی که می‌توانیم تأیید کنیم این است که خروجی تابع برابر با ورودی نیست. + +در پس‌زمینه، ماکروهای `assert_eq!` و `assert_ne!` به ترتیب از عملگرهای `==` و `!=` استفاده می‌کنند. وقتی ادعا شکست می‌خورد، این ماکروها آرگومان‌های خود را با استفاده از قالب‌بندی دیباگ چاپ می‌کنند، که به این معنی است که مقادیر مقایسه‌شده باید ویژگی‌های `PartialEq` و `Debug` را پیاده‌سازی کنند. تمام نوع‌های اولیه و بیشتر نوع‌های کتابخانه استاندارد این ویژگی‌ها را پیاده‌سازی می‌کنند. برای ساختارها و انوم‌هایی که خودتان تعریف می‌کنید، باید `PartialEq` را برای تأیید برابری این نوع‌ها پیاده‌سازی کنید. همچنین باید `Debug` را برای چاپ مقادیر زمانی که ادعا شکست می‌خورد پیاده‌سازی کنید. از آنجا که هر دو ویژگی قابل اشتقاق هستند، همانطور که در لیست ۵-۱۲ فصل ۵ اشاره شد، این معمولاً به سادگی افزودن حاشیه‌نویسی `#[derive(PartialEq, Debug)]` به تعریف ساختار یا انوم شما است. برای جزئیات بیشتر در مورد این ویژگی‌ها و سایر ویژگی‌های قابل اشتقاق، به ضمیمه ج، [“ویژگی‌های قابل اشتقاق”][derivable-traits] مراجعه کنید. + +### افزودن پیام‌های شکست سفارشی + +شما همچنین می‌توانید یک پیام سفارشی را به‌عنوان آرگومان‌های اختیاری به ماکروهای `assert!`، `assert_eq!` و `assert_ne!` اضافه کنید تا همراه با پیام شکست چاپ شود. +هر آرگومانی که پس از آرگومان‌های ضروری وارد شود، به ماکرو `format!` (که در بخش [“ادغام با عملگر `+` یا ماکرو `format!`”][concatenation-with-the--operator-or-the-format-macro] در فصل ۸ توضیح داده شده) ارسال می‌شود، +پس می‌توانید یک رشته‌ی قالب (format string) حاوی جای‌نگهدارهای `{}` و مقادیری برای جای‌گذاری در آن‌ها ارسال کنید. +پیام‌های سفارشی برای مستندسازی معنای یک assertion مفید هستند؛ +وقتی تست شکست می‌خورد، درک بهتری از مشکل کد خواهید داشت. + +برای مثال، فرض کنید تابعی داریم که افراد را با نامشان خوشامد می‌گوید و می‌خواهیم تست کنیم که نامی که به تابع پاس می‌دهیم در خروجی ظاهر می‌شود: Filename: src/lib.rs @@ -372,62 +254,43 @@ want to test that the name we pass into the function appears in the output: {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-05-greeter/src/lib.rs}} ``` -The requirements for this program haven’t been agreed upon yet, and we’re -pretty sure the `Hello` text at the beginning of the greeting will change. We -decided we don’t want to have to update the test when the requirements change, -so instead of checking for exact equality to the value returned from the -`greeting` function, we’ll just assert that the output contains the text of the -input parameter. +نیازمندی‌های این برنامه هنوز مورد توافق قرار نگرفته‌اند، و ما تقریباً مطمئن هستیم که متن `Hello` در ابتدای پیام خوشامد تغییر خواهد کرد. تصمیم گرفتیم که نمی‌خواهیم وقتی نیازمندی‌ها تغییر می‌کنند، تست را به‌روزرسانی کنیم، بنابراین به جای بررسی برابری دقیق با مقدار بازگشتی از تابع `greeting`، فقط تأیید می‌کنیم که خروجی شامل متن پارامتر ورودی است. -Now let’s introduce a bug into this code by changing `greeting` to exclude -`name` to see what the default test failure looks like: +حالا بیایید یک باگ به این کد وارد کنیم با تغییر `greeting` به‌طوری که `name` را شامل نشود تا ببینیم پیام شکست تست پیش‌فرض چگونه است: ```rust,not_desired_behavior,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/src/lib.rs:here}} ``` -Running this test produces the following: +اجرای این تست خروجی زیر را تولید می‌کند: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-06-greeter-with-bug/output.txt}} ``` -This result just indicates that the assertion failed and which line the -assertion is on. A more useful failure message would print the value from the -`greeting` function. Let’s add a custom failure message composed of a format -string with a placeholder filled in with the actual value we got from the -`greeting` function: +این نتیجه فقط نشان می‌دهد که ادعا شکست خورده است و خطی که ادعا در آن قرار دارد کدام است. یک پیام شکست مفیدتر مقدار بازگشتی از تابع `greeting` را چاپ می‌کرد. بیایید یک پیام شکست سفارشی اضافه کنیم که از یک رشته قالب با یک نگهدارنده که با مقدار واقعی بازگشتی از تابع `greeting` پر شده است، تشکیل شده باشد: ```rust,ignore {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/src/lib.rs:here}} ``` -Now when we run the test, we’ll get a more informative error message: +حالا وقتی تست را اجرا می‌کنیم، یک پیام خطای اطلاع‌رسان‌تر دریافت خواهیم کرد: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-07-custom-failure-message/output.txt}} ``` -We can see the value we actually got in the test output, which would help us -debug what happened instead of what we were expecting to happen. +ما می‌توانیم مقدار واقعی‌ای که در خروجی تست دریافت کردیم را ببینیم، که به ما کمک می‌کند تا اشکال‌زدایی کنیم که چه اتفاقی افتاد به جای آنچه که انتظار داشتیم اتفاق بیفتد. -### Checking for Panics with `should_panic` +### بررسی پانیک با `should_panic` -In addition to checking return values, it’s important to check that our code -handles error conditions as we expect. For example, consider the `Guess` type -that we created in Chapter 9, Listing 9-13. Other code that uses `Guess` -depends on the guarantee that `Guess` instances will contain only values -between 1 and 100. We can write a test that ensures that attempting to create a -`Guess` instance with a value outside that range panics. +علاوه بر بررسی مقادیر بازگشتی، مهم است که بررسی کنیم کد ما شرایط خطا را همانطور که انتظار داریم مدیریت می‌کند. برای مثال، نوع `Guess` را که در فصل ۹، لیست ۹-۱۳ ایجاد کردیم در نظر بگیرید. سایر کدهایی که از `Guess` استفاده می‌کنند به این تضمین وابسته هستند که نمونه‌های `Guess` فقط مقادیر بین ۱ و ۱۰۰ را شامل می‌شوند. می‌توانیم تستی بنویسیم که اطمینان حاصل کند که تلاش برای ایجاد یک نمونه `Guess` با مقداری خارج از این بازه منجر به پانیک می‌شود. -We do this by adding the attribute `should_panic` to our test function. The -test passes if the code inside the function panics; the test fails if the code -inside the function doesn’t panic. +این کار را با افزودن ویژگی `should_panic` به تابع تست خود انجام می‌دهیم. اگر کد داخل تابع پانیک کند، تست پاس می‌شود؛ اگر کد داخل تابع پانیک نکند، تست شکست می‌خورد. -Listing 11-8 shows a test that checks that the error conditions of `Guess::new` -happen when we expect them to. +لیست ۱۱-۸ یک تست را نشان می‌دهد که بررسی می‌کند شرایط خطای `Guess::new` زمانی که انتظار داریم رخ می‌دهند. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-08/src/lib.rs}} @@ -435,41 +298,29 @@ happen when we expect them to. -We place the `#[should_panic]` attribute after the `#[test]` attribute and -before the test function it applies to. Let’s look at the result when this test -passes: +ما ویژگی `#[should_panic]` را بعد از ویژگی `#[test]` و قبل از تابع تستی که به آن اعمال می‌شود قرار می‌دهیم. بیایید به نتیجه‌ای که وقتی این تست پاس می‌شود نگاه کنیم: ```console {{#include ../listings/ch11-writing-automated-tests/listing-11-08/output.txt}} ``` -Looks good! Now let’s introduce a bug in our code by removing the condition -that the `new` function will panic if the value is greater than 100: +به نظر خوب می‌آید! حالا بیایید یک باگ در کد خود وارد کنیم با حذف شرطی که تابع `new` را مجبور می‌کند اگر مقدار بیشتر از ۱۰۰ باشد پانیک کند: ```rust,not_desired_behavior,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-08-guess-with-bug/src/lib.rs:here}} ``` -When we run the test in Listing 11-8, it will fail: +وقتی تست در لیست ۱۱-۸ را اجرا می‌کنیم، شکست می‌خورد: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-08-guess-with-bug/output.txt}} ``` -We don’t get a very helpful message in this case, but when we look at the test -function, we see that it’s annotated with `#[should_panic]`. The failure we got -means that the code in the test function did not cause a panic. +در این مورد پیام خیلی مفیدی دریافت نمی‌کنیم، اما وقتی به تابع تست نگاه می‌کنیم، می‌بینیم که با `#[should_panic]` حاشیه‌نویسی شده است. شکست به این معناست که کدی که در تابع تست قرار دارد باعث یک پانیک نشده است. -Tests that use `should_panic` can be imprecise. A `should_panic` test would -pass even if the test panics for a different reason from the one we were -expecting. To make `should_panic` tests more precise, we can add an optional -`expected` parameter to the `should_panic` attribute. The test harness will -make sure that the failure message contains the provided text. For example, -consider the modified code for `Guess` in Listing 11-9 where the `new` function -panics with different messages depending on whether the value is too small or -too large. +تست‌هایی که از `should_panic` استفاده می‌کنند می‌توانند دقیق نباشند. یک تست `should_panic` حتی اگر تست برای دلیلی غیر از آنچه انتظار داشتیم پانیک کند، پاس می‌شود. برای دقیق‌تر کردن تست‌های `should_panic`، می‌توانیم یک پارامتر اختیاری `expected` به ویژگی `should_panic` اضافه کنیم. تست رانر اطمینان حاصل می‌کند که پیام شکست شامل متن ارائه‌شده است. برای مثال، کد تغییر داده‌شده برای `Guess` در لیست ۱۱-۹ را در نظر بگیرید که تابع `new` با پیام‌های مختلف بسته به اینکه مقدار خیلی کوچک یا خیلی بزرگ باشد پانیک می‌کند. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-09/src/lib.rs:here}} @@ -477,68 +328,43 @@ too large. -This test will pass because the value we put in the `should_panic` attribute’s -`expected` parameter is a substring of the message that the `Guess::new` -function panics with. We could have specified the entire panic message that we -expect, which in this case would be `Guess value must be less than or equal to -100, got 200`. What you choose to specify depends on how much of the panic -message is unique or dynamic and how precise you want your test to be. In this -case, a substring of the panic message is enough to ensure that the code in the -test function executes the `else if value > 100` case. +این تست پاس می‌شود زیرا مقداری که در پارامتر `expected` ویژگی `should_panic` قرار داده‌ایم یک زیررشته از پیامی است که تابع `Guess::new` با آن پانیک می‌کند. می‌توانستیم کل پیام پانیکی که انتظار داریم را مشخص کنیم، که در این مورد می‌شد `Guess value must be less than or equal to 100, got 200`. آنچه انتخاب می‌کنید بستگی به این دارد که چه مقدار از پیام پانیک منحصر به فرد یا پویا است و چقدر می‌خواهید تست شما دقیق باشد. در این مورد، یک زیررشته از پیام پانیک کافی است تا اطمینان حاصل شود که کد در تابع تست مورد `else if value > 100` را اجرا می‌کند. -To see what happens when a `should_panic` test with an `expected` message -fails, let’s again introduce a bug into our code by swapping the bodies of the -`if value < 1` and the `else if value > 100` blocks: +برای دیدن اینکه وقتی یک تست `should_panic` با یک پیام `expected` شکست می‌خورد چه اتفاقی می‌افتد، بیایید دوباره یک باگ به کد خود وارد کنیم با جابه‌جا کردن بدنه‌های بلوک‌های `if value < 1` و `else if value > 100`: ```rust,ignore,not_desired_behavior {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/src/lib.rs:here}} ``` -This time when we run the `should_panic` test, it will fail: +این بار وقتی تست `should_panic` را اجرا می‌کنیم، شکست خواهد خورد: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-09-guess-with-panic-msg-bug/output.txt}} ``` -The failure message indicates that this test did indeed panic as we expected, -but the panic message did not include the expected string `less than or equal -to 100`. The panic message that we did get in this case was `Guess value must -be greater than or equal to 1, got 200.` Now we can start figuring out where -our bug is! +پیام شکست نشان می‌دهد که این تست همانطور که انتظار داشتیم پانیک کرد، اما پیام پانیک شامل رشته مورد انتظار `less than or equal to 100` نبود. پیام پانیکی که در این مورد دریافت کردیم `Guess value must be greater than or equal to 1, got 200.` بود. حالا می‌توانیم شروع به پیدا کردن محل باگ کنیم! -### Using `Result` in Tests +### استفاده از `Result` در تست‌ها -Our tests so far all panic when they fail. We can also write tests that use -`Result`! Here’s the test from Listing 11-1, rewritten to use `Result` and return an `Err` instead of panicking: +تست‌های ما تا اینجا همه زمانی که شکست می‌خورند پانیک می‌کنند. همچنین می‌توانیم تست‌هایی بنویسیم که از `Result` استفاده کنند! در اینجا تست لیست ۱۱-۱ را بازنویسی کرده‌ایم تا از `Result` استفاده کند و به جای پانیک کردن، یک `Err` بازگرداند: ```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-10-result-in-tests/src/lib.rs:here}} ``` -The `it_works` function now has the `Result<(), String>` return type. In the -body of the function, rather than calling the `assert_eq!` macro, we return -`Ok(())` when the test passes and an `Err` with a `String` inside when the test -fails. - -Writing tests so they return a `Result` enables you to use the question -mark operator in the body of tests, which can be a convenient way to write -tests that should fail if any operation within them returns an `Err` variant. - -You can’t use the `#[should_panic]` annotation on tests that use `Result`. To assert that an operation returns an `Err` variant, _don’t_ use the -question mark operator on the `Result` value. Instead, use -`assert!(value.is_err())`. - -Now that you know several ways to write tests, let’s look at what is happening -when we run our tests and explore the different options we can use with `cargo -test`. - -[concatenation-with-the--operator-or-the-format-macro]: ch08-02-strings.html#concatenation-with-the--operator-or-the-format-macro -[bench]: ../unstable-book/library-features/test.html -[ignoring]: ch11-02-running-tests.html#ignoring-some-tests-unless-specifically-requested -[subset]: ch11-02-running-tests.html#running-a-subset-of-tests-by-name -[controlling-how-tests-are-run]: ch11-02-running-tests.html#controlling-how-tests-are-run -[derivable-traits]: appendix-03-derivable-traits.html -[doc-comments]: ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests +تابع `it_works` اکنون نوع بازگشتی `Result<(), String>` دارد. در بدنه تابع، به جای فراخوانی ماکروی `assert_eq!`، وقتی تست پاس می‌شود `Ok(())` و وقتی تست شکست می‌خورد یک `Err` با یک `String` داخل آن بازمی‌گردانیم. + +نوشتن تست‌هایی که یک `Result` بازمی‌گردانند به شما اجازه می‌دهد از عملگر سوالی `?` در بدنه تست‌ها استفاده کنید، که می‌تواند راهی راحت برای نوشتن تست‌هایی باشد که اگر هر عملیاتی در آن‌ها یک واریانت `Err` بازگرداند، شکست بخورند. + +شما نمی‌توانید از حاشیه‌نویسی `#[should_panic]` در تست‌هایی که از `Result` استفاده می‌کنند استفاده کنید. برای تأیید اینکه یک عملیات یک واریانت `Err` بازمی‌گرداند، _از_ عملگر سوالی روی مقدار `Result` استفاده نکنید. در عوض، از `assert!(value.is_err())` استفاده کنید. + +حالا که چندین روش برای نوشتن تست‌ها را یاد گرفتید، بیایید نگاهی به آنچه هنگام اجرای تست‌ها اتفاق می‌افتد بیندازیم و گزینه‌های مختلفی را که می‌توانیم با `cargo test` استفاده کنیم بررسی کنیم. + +[concatenation-with-the--operator-or-the-format-macro]: ch08-02-strings.html#concatenation-with-the--operator-or-the-format-macro +[bench]: ../unstable-book/library-features/test.html +[ignoring]: ch11-02-running-tests.html#ignoring-some-tests-unless-specifically-requested +[subset]: ch11-02-running-tests.html#running-a-subset-of-tests-by-name +[controlling-how-tests-are-run]: ch11-02-running-tests.html#controlling-how-tests-are-run +[derivable-traits]: appendix-03-derivable-traits.html +[doc-comments]: ch14-02-publishing-to-crates-io.html#documentation-comments-as-tests [paths-for-referring-to-an-item-in-the-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html diff --git a/src/ch11-02-running-tests.md b/src/ch11-02-running-tests.md index dc4afc2af6..bc5cfee4cc 100644 --- a/src/ch11-02-running-tests.md +++ b/src/ch11-02-running-tests.md @@ -1,68 +1,33 @@ -## Controlling How Tests Are Run - -Just as `cargo run` compiles your code and then runs the resultant binary, -`cargo test` compiles your code in test mode and runs the resultant test -binary. The default behavior of the binary produced by `cargo test` is to run -all the tests in parallel and capture output generated during test runs, -preventing the output from being displayed and making it easier to read the -output related to the test results. You can, however, specify command line -options to change this default behavior. - -Some command line options go to `cargo test`, and some go to the resultant test -binary. To separate these two types of arguments, you list the arguments that -go to `cargo test` followed by the separator `--` and then the ones that go to -the test binary. Running `cargo test --help` displays the options you can use -with `cargo test`, and running `cargo test -- --help` displays the options you -can use after the separator. Those options are also documented in [the “Tests” -section][tests] of the [the rustc book][rustc]. - -[tests]: https://doc.rust-lang.org/rustc/tests/index.html -[rustc]: https://doc.rust-lang.org/rustc/index.html - -### Running Tests in Parallel or Consecutively - -When you run multiple tests, by default they run in parallel using threads, -meaning they finish running faster and you get feedback quicker. Because the -tests are running at the same time, you must make sure your tests don’t depend -on each other or on any shared state, including a shared environment, such as -the current working directory or environment variables. - -For example, say each of your tests runs some code that creates a file on disk -named _test-output.txt_ and writes some data to that file. Then each test reads -the data in that file and asserts that the file contains a particular value, -which is different in each test. Because the tests run at the same time, one -test might overwrite the file in the time between another test writing and -reading the file. The second test will then fail, not because the code is -incorrect but because the tests have interfered with each other while running -in parallel. One solution is to make sure each test writes to a different file; -another solution is to run the tests one at a time. - -If you don’t want to run the tests in parallel or if you want more fine-grained -control over the number of threads used, you can send the `--test-threads` flag -and the number of threads you want to use to the test binary. Take a look at -the following example: +## کنترل نحوه اجرای تست‌ها + +دقیقاً همانطور که `cargo run` کد شما را کامپایل کرده و باینری حاصل را اجرا می‌کند، `cargo test` کد شما را در حالت تست کامپایل کرده و باینری تست حاصل را اجرا می‌کند. رفتار پیش‌فرض باینری تولیدشده توسط `cargo test` این است که تمام تست‌ها را به صورت موازی اجرا کرده و خروجی تولید شده در طول اجرای تست‌ها را ضبط کند. این کار از نمایش خروجی جلوگیری کرده و خواندن خروجی مرتبط با نتایج تست را آسان‌تر می‌کند. با این حال، می‌توانید با مشخص کردن گزینه‌های خط فرمان این رفتار پیش‌فرض را تغییر دهید. + +برخی گزینه‌های خط فرمان به `cargo test` می‌روند و برخی دیگر به باینری تست حاصل ارسال می‌شوند. برای جدا کردن این دو نوع آرگومان، آرگومان‌هایی که به `cargo test` می‌روند را ذکر کنید و سپس جداکننده `--` و آرگومان‌هایی که به باینری تست می‌روند را بیاورید. اجرای `cargo test --help` گزینه‌هایی را نمایش می‌دهد که می‌توانید با `cargo test` استفاده کنید، و اجرای `cargo test -- --help` گزینه‌هایی را که می‌توانید پس از جداکننده استفاده کنید نمایش می‌دهد. این گزینه‌ها همچنین در [بخش "تست‌ها"][tests] از [کتاب rustc][rustc] مستند شده‌اند. + +[tests]: https://doc.rust-lang.org/rustc/tests/index.html +[rustc]: https://doc.rust-lang.org/rustc/index.html + +### اجرای تست‌ها به صورت موازی یا متوالی + +وقتی چندین تست را اجرا می‌کنید، به طور پیش‌فرض این تست‌ها به صورت موازی با استفاده از نخ‌ها (threads) اجرا می‌شوند، به این معنی که سریع‌تر به پایان می‌رسند و بازخورد سریع‌تری دریافت می‌کنید. از آنجا که تست‌ها به صورت هم‌زمان اجرا می‌شوند، باید اطمینان حاصل کنید که تست‌های شما به یکدیگر یا به هیچ حالت مشترکی، از جمله یک محیط مشترک مانند دایرکتوری کاری جاری یا متغیرهای محیطی، وابسته نیستند. + +برای مثال، فرض کنید هر یک از تست‌های شما کدی را اجرا می‌کند که یک فایل به نام _test-output.txt_ روی دیسک ایجاد کرده و داده‌هایی در آن فایل می‌نویسد. سپس هر تست داده‌های موجود در آن فایل را خوانده و تأیید می‌کند که فایل شامل یک مقدار خاص است، که در هر تست متفاوت است. چون تست‌ها به طور هم‌زمان اجرا می‌شوند، ممکن است یک تست فایل را در زمانی که تست دیگری در حال نوشتن و خواندن فایل است، بازنویسی کند. در این صورت، تست دوم شکست خواهد خورد، نه به این دلیل که کد اشتباه است بلکه به این دلیل که تست‌ها در هنگام اجرای موازی با یکدیگر تداخل پیدا کرده‌اند. یک راه‌حل این است که مطمئن شوید هر تست به یک فایل متفاوت می‌نویسد؛ راه‌حل دیگر این است که تست‌ها را یکی یکی اجرا کنید. + +اگر نمی‌خواهید تست‌ها به صورت موازی اجرا شوند یا اگر می‌خواهید کنترل بیشتری بر تعداد نخ‌های استفاده‌شده داشته باشید، می‌توانید فلگ `--test-threads` و تعداد نخ‌هایی که می‌خواهید استفاده کنید را به باینری تست ارسال کنید. به مثال زیر توجه کنید: ```console $ cargo test -- --test-threads=1 ``` -We set the number of test threads to `1`, telling the program not to use any -parallelism. Running the tests using one thread will take longer than running -them in parallel, but the tests won’t interfere with each other if they share -state. +ما تعداد نخ‌های تست را به `1` تنظیم کردیم، به برنامه می‌گوییم از هیچ موازی‌سازی استفاده نکند. اجرای تست‌ها با یک نخ بیشتر از اجرای آن‌ها به صورت موازی طول می‌کشد، اما تست‌ها در صورتی که حالت مشترکی داشته باشند با یکدیگر تداخل پیدا نمی‌کنند. -### Showing Function Output +### نمایش خروجی توابع -By default, if a test passes, Rust’s test library captures anything printed to -standard output. For example, if we call `println!` in a test and the test -passes, we won’t see the `println!` output in the terminal; we’ll see only the -line that indicates the test passed. If a test fails, we’ll see whatever was -printed to standard output with the rest of the failure message. +به طور پیش‌فرض، اگر یک تست پاس شود، کتابخانه تست Rust هر چیزی که به خروجی استاندارد چاپ شده را ضبط می‌کند. برای مثال، اگر در یک تست از `println!` استفاده کنیم و تست پاس شود، خروجی `println!` را در ترمینال نخواهیم دید؛ فقط خطی که نشان می‌دهد تست پاس شده است را خواهیم دید. اگر یک تست شکست بخورد، هر چیزی که به خروجی استاندارد چاپ شده باشد را همراه با پیام شکست خواهیم دید. -As an example, Listing 11-10 has a silly function that prints the value of its -parameter and returns 10, as well as a test that passes and a test that fails. +برای مثال، لیست ۱۱-۱۰ یک تابع ساده دارد که مقدار پارامتر خود را چاپ کرده و مقدار ۱۰ را بازمی‌گرداند، همچنین یک تست که پاس می‌شود و یک تست که شکست می‌خورد. -+ ```rust,panics,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-10/src/lib.rs}} @@ -70,42 +35,33 @@ parameter and returns 10, as well as a test that passes and a test that fails. -When we run these tests with `cargo test`, we’ll see the following output: +وقتی این تست‌ها را با `cargo test` اجرا می‌کنیم، خروجی زیر را خواهیم دید: ```console {{#include ../listings/ch11-writing-automated-tests/listing-11-10/output.txt}} ``` -Note that nowhere in this output do we see `I got the value 4`, which is -printed when the test that passes runs. That output has been captured. The -output from the test that failed, `I got the value 8`, appears in the section -of the test summary output, which also shows the cause of the test failure. +توجه کنید که در هیچ جای این خروجی `I got the value 4` که هنگام اجرای تست پاس‌شده چاپ می‌شود، نمی‌بینیم. این خروجی ضبط شده است. خروجی تست شکست‌خورده، `I got the value 8`، در بخش خلاصه خروجی تست ظاهر می‌شود که علت شکست تست را نیز نشان می‌دهد. -If we want to see printed values for passing tests as well, we can tell Rust to -also show the output of successful tests with `--show-output`: +اگر بخواهیم مقادیر چاپ‌شده برای تست‌های پاس‌شده را نیز ببینیم، می‌توانیم به Rust بگوییم که خروجی تست‌های موفق را با استفاده از `--show-output` نیز نمایش دهد: ```console $ cargo test -- --show-output ``` -When we run the tests in Listing 11-10 again with the `--show-output` flag, we -see the following output: +وقتی تست‌های لیست ۱۱-۱۰ را دوباره با فلگ `--show-output` اجرا می‌کنیم، خروجی زیر را خواهیم دید: ```console {{#include ../listings/ch11-writing-automated-tests/output-only-01-show-output/output.txt}} ``` -### Running a Subset of Tests by Name +### اجرای زیرمجموعه‌ای از تست‌ها با نام -Sometimes, running a full test suite can take a long time. If you’re working on -code in a particular area, you might want to run only the tests pertaining to -that code. You can choose which tests to run by passing `cargo test` the name -or names of the test(s) you want to run as an argument. +گاهی اوقات، اجرای یک مجموعه کامل از تست‌ها می‌تواند زمان زیادی ببرد. اگر در حال کار روی کدی در یک بخش خاص هستید، ممکن است بخواهید فقط تست‌های مربوط به آن کد را اجرا کنید. می‌توانید با پاس دادن نام یا نام‌های تست‌هایی که می‌خواهید اجرا کنید به `cargo test`، انتخاب کنید که کدام تست‌ها اجرا شوند. -To demonstrate how to run a subset of tests, we’ll first create three tests for -our `add_two` function, as shown in Listing 11-11, and choose which ones to run. +برای نشان دادن نحوه اجرای یک زیرمجموعه از تست‌ها، ابتدا سه تست برای تابع `add_two` خود ایجاد می‌کنیم، همانطور که در لیست ۱۱-۱۱ نشان داده شده است، و انتخاب می‌کنیم کدام‌یک را اجرا کنیم. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-11/src/lib.rs}} @@ -113,50 +69,37 @@ our `add_two` function, as shown in Listing 11-11, and choose which ones to run. -If we run the tests without passing any arguments, as we saw earlier, all the -tests will run in parallel: +اگر تست‌ها را بدون پاس دادن هیچ آرگومانی اجرا کنیم، همانطور که قبلاً دیدیم، تمام تست‌ها به صورت موازی اجرا می‌شوند: ```console {{#include ../listings/ch11-writing-automated-tests/listing-11-11/output.txt}} ``` -#### Running Single Tests +#### اجرای تست‌های منفرد -We can pass the name of any test function to `cargo test` to run only that test: +می‌توانیم نام هر تابع تست را به `cargo test` پاس دهیم تا فقط همان تست اجرا شود: ```console {{#include ../listings/ch11-writing-automated-tests/output-only-02-single-test/output.txt}} ``` -Only the test with the name `one_hundred` ran; the other two tests didn’t match -that name. The test output lets us know we had more tests that didn’t run by -displaying `2 filtered out` at the end. +فقط تستی با نام `one_hundred` اجرا شد؛ دو تست دیگر با این نام مطابقت نداشتند. خروجی تست به ما اطلاع می‌دهد که تست‌های بیشتری وجود داشته‌اند که اجرا نشده‌اند و در انتها `2 filtered out` را نمایش می‌دهد. -We can’t specify the names of multiple tests in this way; only the first value -given to `cargo test` will be used. But there is a way to run multiple tests. +نمی‌توانیم به این روش نام چندین تست را مشخص کنیم؛ فقط اولین مقداری که به `cargo test` داده می‌شود استفاده خواهد شد. اما راهی برای اجرای چندین تست وجود دارد. -#### Filtering to Run Multiple Tests +#### فیلتر کردن برای اجرای چندین تست -We can specify part of a test name, and any test whose name matches that value -will be run. For example, because two of our tests’ names contain `add`, we can -run those two by running `cargo test add`: +می‌توانیم بخشی از یک نام تست را مشخص کنیم، و هر تستی که نامش با آن مقدار مطابقت داشته باشد اجرا خواهد شد. برای مثال، چون دو تا از نام‌های تست‌های ما شامل `add` هستند، می‌توانیم آن دو را با اجرای `cargo test add` اجرا کنیم: ```console {{#include ../listings/ch11-writing-automated-tests/output-only-03-multiple-tests/output.txt}} ``` -This command ran all tests with `add` in the name and filtered out the test -named `one_hundred`. Also note that the module in which a test appears becomes -part of the test’s name, so we can run all the tests in a module by filtering -on the module’s name. +این فرمان تمام تست‌هایی که `add` در نامشان دارند را اجرا کرد و تستی با نام `one_hundred` را فیلتر کرد. همچنین توجه داشته باشید که ماژولی که یک تست در آن ظاهر می‌شود بخشی از نام تست می‌شود، بنابراین می‌توانیم تمام تست‌های یک ماژول را با فیلتر کردن روی نام ماژول اجرا کنیم. -### Ignoring Some Tests Unless Specifically Requested +### نادیده گرفتن برخی تست‌ها مگر اینکه صریحاً درخواست شوند -Sometimes a few specific tests can be very time-consuming to execute, so you -might want to exclude them during most runs of `cargo test`. Rather than -listing as arguments all tests you do want to run, you can instead annotate the -time-consuming tests using the `ignore` attribute to exclude them, as shown -here: +گاهی اوقات چند تست خاص می‌توانند بسیار وقت‌گیر باشند، بنابراین ممکن است بخواهید آن‌ها را در اکثر اجراهای `cargo test` حذف کنید. به جای لیست کردن تمام تست‌هایی که می‌خواهید اجرا کنید، می‌توانید تست‌های وقت‌گیر را با استفاده از ویژگی `ignore` حاشیه‌نویسی کنید تا آن‌ها را حذف کنید، همانطور که در اینجا نشان داده شده است: Filename: src/lib.rs @@ -164,22 +107,16 @@ here: {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-11-ignore-a-test/src/lib.rs:here}} ``` -After `#[test]`, we add the `#[ignore]` line to the test we want to exclude. -Now when we run our tests, `it_works` runs, but `expensive_test` doesn’t: +بعد از `#[test]`، خط `#[ignore]` را به تستی که می‌خواهیم حذف کنیم اضافه می‌کنیم. حالا وقتی تست‌های خود را اجرا می‌کنیم، `it_works` اجرا می‌شود، اما `expensive_test` اجرا نمی‌شود: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-11-ignore-a-test/output.txt}} ``` -The `expensive_test` function is listed as `ignored`. If we want to run only -the ignored tests, we can use `cargo test -- --ignored`: +تابع `expensive_test` به عنوان `ignored` فهرست شده است. اگر بخواهیم فقط تست‌های نادیده‌گرفته‌شده را اجرا کنیم، می‌توانیم از `cargo test -- --ignored` استفاده کنیم: ```console {{#include ../listings/ch11-writing-automated-tests/output-only-04-running-ignored/output.txt}} ``` -By controlling which tests run, you can make sure your `cargo test` results -will be returned quickly. When you’re at a point where it makes sense to check -the results of the `ignored` tests and you have time to wait for the results, -you can run `cargo test -- --ignored` instead. If you want to run all tests -whether they’re ignored or not, you can run `cargo test -- --include-ignored`. +با کنترل اینکه کدام تست‌ها اجرا می‌شوند، می‌توانید مطمئن شوید که نتایج `cargo test` شما به سرعت بازگردانده می‌شوند. وقتی در نقطه‌ای هستید که منطقی است نتایج تست‌های `ignored` را بررسی کنید و زمان برای انتظار نتایج دارید، می‌توانید به جای آن `cargo test -- --ignored` را اجرا کنید. اگر می‌خواهید تمام تست‌ها را اجرا کنید، چه نادیده‌گرفته‌شده و چه نشده، می‌توانید `cargo test -- --include-ignored` را اجرا کنید. diff --git a/src/ch11-03-test-organization.md b/src/ch11-03-test-organization.md index 411c41b7b4..389a941db0 100644 --- a/src/ch11-03-test-organization.md +++ b/src/ch11-03-test-organization.md @@ -1,39 +1,18 @@ -## Test Organization - -As mentioned at the start of the chapter, testing is a complex discipline, and -different people use different terminology and organization. The Rust community -thinks about tests in terms of two main categories: unit tests and integration -tests. _Unit tests_ are small and more focused, testing one module in isolation -at a time, and can test private interfaces. _Integration tests_ are entirely -external to your library and use your code in the same way any other external -code would, using only the public interface and potentially exercising multiple -modules per test. - -Writing both kinds of tests is important to ensure that the pieces of your -library are doing what you expect them to, separately and together. - -### Unit Tests - -The purpose of unit tests is to test each unit of code in isolation from the -rest of the code to quickly pinpoint where code is and isn’t working as -expected. You’ll put unit tests in the _src_ directory in each file with the -code that they’re testing. The convention is to create a module named `tests` -in each file to contain the test functions and to annotate the module with -`cfg(test)`. - -#### The Tests Module and `#[cfg(test)]` - -The `#[cfg(test)]` annotation on the `tests` module tells Rust to compile and -run the test code only when you run `cargo test`, not when you run `cargo -build`. This saves compile time when you only want to build the library and -saves space in the resultant compiled artifact because the tests are not -included. You’ll see that because integration tests go in a different -directory, they don’t need the `#[cfg(test)]` annotation. However, because unit -tests go in the same files as the code, you’ll use `#[cfg(test)]` to specify -that they shouldn’t be included in the compiled result. - -Recall that when we generated the new `adder` project in the first section of -this chapter, Cargo generated this code for us: +## سازماندهی تست‌ها + +همانطور که در ابتدای فصل ذکر شد، تست‌نویسی یک رشته پیچیده است، و افراد مختلف از اصطلاحات و سازماندهی متفاوتی استفاده می‌کنند. جامعه Rust تست‌ها را به دو دسته اصلی تقسیم می‌کند: تست‌های واحد و تست‌های یکپارچه. _تست‌های واحد_ کوچک و متمرکزتر هستند، یک ماژول را به طور جداگانه در یک زمان تست می‌کنند و می‌توانند رابط‌های خصوصی را تست کنند. _تست‌های یکپارچه_ کاملاً خارجی نسبت به کتابخانه شما هستند و از کد شما همانطور که هر کد خارجی دیگری استفاده می‌کند، تنها از طریق رابط عمومی استفاده می‌کنند و ممکن است چندین ماژول را در هر تست بررسی کنند. + +نوشتن هر دو نوع تست برای اطمینان از اینکه قطعات کتابخانه شما به صورت جداگانه و با هم کار می‌کنند، مهم است. + +### تست‌های واحد + +هدف تست‌های واحد این است که هر واحد کد را به طور جداگانه از سایر کدها تست کنند تا به سرعت مشخص شود که کد کجا به درستی کار می‌کند و کجا نه. تست‌های واحد را در دایرکتوری _src_ در هر فایل با کدی که تست می‌کنند قرار می‌دهید. کنوانسیون این است که یک ماژول به نام `tests` در هر فایل ایجاد کنید تا توابع تست را در آن قرار دهید و ماژول را با `cfg(test)` حاشیه‌نویسی کنید. + +#### ماژول تست‌ها و `#[cfg(test)]` + +حاشیه‌نویسی `#[cfg(test)]` روی ماژول `tests` به Rust می‌گوید که کد تست فقط وقتی که `cargo test` اجرا شود کامپایل و اجرا شود، نه وقتی که `cargo build` اجرا شود. این باعث صرفه‌جویی در زمان کامپایل وقتی فقط می‌خواهید کتابخانه را بسازید می‌شود و فضای کمتری در نتیجه کامپایل‌شده می‌گیرد زیرا تست‌ها شامل نمی‌شوند. مشاهده خواهید کرد که چون تست‌های یکپارچه در یک دایرکتوری جداگانه قرار می‌گیرند، نیازی به حاشیه‌نویسی `#[cfg(test)]` ندارند. با این حال، چون تست‌های واحد در همان فایل‌هایی که کد قرار دارد قرار می‌گیرند، از `#[cfg(test)]` استفاده می‌کنید تا مشخص کنید که نباید در نتیجه کامپایل‌شده قرار گیرند. + +به یاد بیاورید وقتی پروژه جدید `adder` را در بخش اول این فصل تولید کردیم، Cargo این کد را برای ما تولید کرد: Filename: src/lib.rs @@ -41,23 +20,13 @@ this chapter, Cargo generated this code for us: {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs}} ``` -On the automatically generated `tests` module, the attribute `cfg` stands for -_configuration_ and tells Rust that the following item should only be included -given a certain configuration option. In this case, the configuration option is -`test`, which is provided by Rust for compiling and running tests. By using the -`cfg` attribute, Cargo compiles our test code only if we actively run the tests -with `cargo test`. This includes any helper functions that might be within this -module, in addition to the functions annotated with `#[test]`. +روی ماژول `tests` که به طور خودکار تولید شده است، ویژگی `cfg` مخفف _پیکربندی_ است و به Rust می‌گوید که آیتم زیر فقط در صورت وجود یک گزینه پیکربندی مشخص گنجانده شود. در این مورد، گزینه پیکربندی `test` است، که توسط Rust برای کامپایل و اجرای تست‌ها ارائه می‌شود. با استفاده از ویژگی `cfg`، Cargo کد تست ما را فقط در صورتی که تست‌ها را به طور فعال با `cargo test` اجرا کنیم، کامپایل می‌کند. این شامل هر تابع کمکی که ممکن است در این ماژول باشد نیز می‌شود، علاوه بر توابعی که با `#[test]` حاشیه‌نویسی شده‌اند. -#### Testing Private Functions +#### تست توابع خصوصی -There’s debate within the testing community about whether or not private -functions should be tested directly, and other languages make it difficult or -impossible to test private functions. Regardless of which testing ideology you -adhere to, Rust’s privacy rules do allow you to test private functions. -Consider the code in Listing 11-12 with the private function `internal_adder`. +در جامعه تست‌نویسی بحث‌هایی درباره اینکه آیا توابع خصوصی باید مستقیماً تست شوند یا نه وجود دارد، و برخی زبان‌ها تست کردن توابع خصوصی را دشوار یا غیرممکن می‌کنند. صرف نظر از اینکه از کدام ایدئولوژی تست‌نویسی پیروی می‌کنید، قوانین خصوصی‌سازی Rust به شما اجازه می‌دهند توابع خصوصی را تست کنید. کدی که در لیست ۱۱-۱۲ با تابع خصوصی `internal_adder` ارائه شده است را در نظر بگیرید. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-12/src/lib.rs}} @@ -65,35 +34,23 @@ Consider the code in Listing 11-12 with the private function `internal_adder`. -Note that the `internal_adder` function is not marked as `pub`. Tests are just -Rust code, and the `tests` module is just another module. As we discussed in -[“Paths for Referring to an Item in the Module Tree”][paths], -items in child modules can use the items in their ancestor modules. In this -test, we bring all of the `tests` module’s parent’s items into scope with `use -super::*`, and then the test can call `internal_adder`. If you don’t think -private functions should be tested, there’s nothing in Rust that will compel you -to do so. +توجه داشته باشید که تابع `internal_adder` به‌صورت `pub` علامت‌گذاری نشده است. +تست‌ها فقط کد Rust هستند و ماژول `tests` نیز یک ماژول عادی دیگر است. +همان‌طور که در بخش [“مسیرها برای ارجاع به یک آیتم در درخت ماژول”][paths] بحث کردیم، +آیتم‌های ماژول‌های فرزند می‌توانند از آیتم‌های ماژول‌های والد خود استفاده کنند. +در این تست، با استفاده از `use super::*` تمام آیتم‌های ماژول والد `tests` وارد حوزه می‌شوند، +و سپس تست می‌تواند تابع `internal_adder` را فراخوانی کند. +اگر فکر می‌کنید توابع خصوصی نباید تست شوند، در Rust هیچ اجباری برای انجام این کار وجود ندارد. -### Integration Tests +### تست‌های یکپارچه -In Rust, integration tests are entirely external to your library. They use your -library in the same way any other code would, which means they can only call -functions that are part of your library’s public API. Their purpose is to test -whether many parts of your library work together correctly. Units of code that -work correctly on their own could have problems when integrated, so test -coverage of the integrated code is important as well. To create integration -tests, you first need a _tests_ directory. +در Rust، تست‌های یکپارچه کاملاً خارجی نسبت به کتابخانه شما هستند. آن‌ها از کتابخانه شما همانطور که هر کد دیگری استفاده می‌کند استفاده می‌کنند، که به این معنی است که فقط می‌توانند توابعی را که بخشی از رابط عمومی کتابخانه شما هستند فراخوانی کنند. هدف آن‌ها این است که بررسی کنند آیا قسمت‌های مختلف کتابخانه شما با یکدیگر به درستی کار می‌کنند یا نه. واحدهای کدی که به تنهایی به درستی کار می‌کنند می‌توانند هنگام یکپارچه‌سازی مشکل داشته باشند، بنابراین پوشش تست کد یکپارچه نیز مهم است. برای ایجاد تست‌های یکپارچه، ابتدا به یک دایرکتوری به نام _tests_ نیاز دارید. -#### The _tests_ Directory +#### دایرکتوری _tests_ -We create a _tests_ directory at the top level of our project directory, next -to _src_. Cargo knows to look for integration test files in this directory. We -can then make as many test files as we want, and Cargo will compile each of the -files as an individual crate. +ما یک دایرکتوری به نام _tests_ در سطح بالای دایرکتوری پروژه خود، در کنار _src_ ایجاد می‌کنیم. Cargo می‌داند که باید به دنبال فایل‌های تست یکپارچه در این دایرکتوری بگردد. سپس می‌توانیم به هر تعداد فایل تست که می‌خواهیم ایجاد کنیم، و Cargo هر یک از فایل‌ها را به عنوان یک crate جداگانه کامپایل می‌کند. -Let’s create an integration test. With the code in Listing 11-12 still in the -_src/lib.rs_ file, make a _tests_ directory, and create a new file named -_tests/integration_test.rs_. Your directory structure should look like this: +بیایید یک تست یکپارچه ایجاد کنیم. با کدی که هنوز در فایل _src/lib.rs_ از لیست ۱۱-۱۲ قرار دارد، یک دایرکتوری _tests_ ایجاد کنید و یک فایل جدید به نام _tests/integration_test.rs_ بسازید. ساختار دایرکتوری شما باید به این صورت باشد: ```text adder @@ -105,9 +62,9 @@ adder └── integration_test.rs ``` -Enter the code in Listing 11-13 into the _tests/integration_test.rs_ file. +کد موجود در لیست ۱۱-۱۳ را در فایل _tests/integration_test.rs_ وارد کنید. -+ ```rust,ignore {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-13/tests/integration_test.rs}} @@ -115,65 +72,35 @@ Enter the code in Listing 11-13 into the _tests/integration_test.rs_ file. -Each file in the _tests_ directory is a separate crate, so we need to bring our -library into each test crate’s scope. For that reason we add `use -adder::add_two;` at the top of the code, which we didn’t need in the unit tests. +هر فایل در دایرکتوری _tests_ یک crate جداگانه است، بنابراین باید کتابخانه خود را به دامنه هر crate تست وارد کنیم. به همین دلیل، در بالای کد `use adder::add_two;` را اضافه می‌کنیم، که در تست‌های واحد نیازی به آن نداشتیم. -We don’t need to annotate any code in _tests/integration_test.rs_ with -`#[cfg(test)]`. Cargo treats the _tests_ directory specially and compiles files -in this directory only when we run `cargo test`. Run `cargo test` now: +نیازی نیست هیچ کدی در فایل _tests/integration_test.rs_ را با `#[cfg(test)]` علامت‌گذاری کنیم. Cargo دایرکتوری _tests_ را به طور خاص مدیریت می‌کند و فایل‌های موجود در این دایرکتوری را فقط زمانی که `cargo test` اجرا کنیم کامپایل می‌کند. اکنون `cargo test` را اجرا کنید: ```console {{#include ../listings/ch11-writing-automated-tests/listing-11-13/output.txt}} ``` -The three sections of output include the unit tests, the integration test, and -the doc tests. Note that if any test in a section fails, the following sections -will not be run. For example, if a unit test fails, there won’t be any output -for integration and doc tests because those tests will only be run if all unit -tests are passing. +سه بخش خروجی شامل تست‌های واحد، تست یکپارچه، و تست‌های مستندات هستند. توجه داشته باشید که اگر هر تستی در یک بخش شکست بخورد، بخش‌های بعدی اجرا نخواهند شد. برای مثال، اگر یک تست واحد شکست بخورد، هیچ خروجی‌ای برای تست‌های یکپارچه و مستندات وجود نخواهد داشت زیرا آن تست‌ها فقط در صورتی اجرا می‌شوند که تمام تست‌های واحد پاس شوند. -The first section for the unit tests is the same as we’ve been seeing: one line -for each unit test (one named `internal` that we added in Listing 11-12) and -then a summary line for the unit tests. +بخش اول برای تست‌های واحد همان چیزی است که قبلاً دیده‌ایم: یک خط برای هر تست واحد (یکی به نام `internal` که در لیست ۱۱-۱۲ اضافه کردیم) و سپس یک خط خلاصه برای تست‌های واحد. -The integration tests section starts with the line `Running -tests/integration_test.rs`. Next, there is a line for each test function in -that integration test and a summary line for the results of the integration -test just before the `Doc-tests adder` section starts. +بخش تست‌های یکپارچه با خط `Running tests/integration_test.rs` شروع می‌شود. سپس یک خط برای هر تابع تست در آن تست یکپارچه و یک خط خلاصه برای نتایج تست یکپارچه دقیقاً قبل از شروع بخش `Doc-tests adder` وجود دارد. -Each integration test file has its own section, so if we add more files in the -_tests_ directory, there will be more integration test sections. +هر فایل تست یکپارچه بخش خاص خود را دارد، بنابراین اگر فایل‌های بیشتری در دایرکتوری _tests_ اضافه کنیم، بخش‌های بیشتری برای تست‌های یکپارچه خواهیم داشت. -We can still run a particular integration test function by specifying the test -function’s name as an argument to `cargo test`. To run all the tests in a -particular integration test file, use the `--test` argument of `cargo test` -followed by the name of the file: +ما هنوز می‌توانیم یک تابع تست خاص در یکپارچه را با مشخص کردن نام تابع تست به عنوان یک آرگومان برای `cargo test` اجرا کنیم. برای اجرای تمام تست‌های یک فایل تست یکپارچه خاص، از آرگومان `--test` برای `cargo test` به همراه نام فایل استفاده کنید: ```console {{#include ../listings/ch11-writing-automated-tests/output-only-05-single-integration/output.txt}} ``` -This command runs only the tests in the _tests/integration_test.rs_ file. +این فرمان فقط تست‌های موجود در فایل _tests/integration_test.rs_ را اجرا می‌کند. -#### Submodules in Integration Tests +#### زیرماژول‌ها در تست‌های یکپارچه -As you add more integration tests, you might want to make more files in the -_tests_ directory to help organize them; for example, you can group the test -functions by the functionality they’re testing. As mentioned earlier, each file -in the _tests_ directory is compiled as its own separate crate, which is useful -for creating separate scopes to more closely imitate the way end users will be -using your crate. However, this means files in the _tests_ directory don’t -share the same behavior as files in _src_ do, as you learned in Chapter 7 -regarding how to separate code into modules and files. +با اضافه کردن تست‌های یکپارچه بیشتر، ممکن است بخواهید فایل‌های بیشتری در دایرکتوری _tests_ برای کمک به سازماندهی آن‌ها ایجاد کنید؛ برای مثال، می‌توانید توابع تست را بر اساس عملکردی که تست می‌کنند گروه‌بندی کنید. همانطور که قبلاً ذکر شد، هر فایل در دایرکتوری _tests_ به عنوان یک crate جداگانه کامپایل می‌شود، که برای ایجاد دامنه‌های جداگانه مفید است تا بیشتر شبیه نحوه استفاده کاربران نهایی از crate شما باشد. با این حال، این به این معنی است که فایل‌های موجود در دایرکتوری _tests_ رفتار یکسانی با فایل‌های موجود در _src_ ندارند، همانطور که در فصل ۷ درباره جدا کردن کد به ماژول‌ها و فایل‌ها آموختید. -The different behavior of _tests_ directory files is most noticeable when you -have a set of helper functions to use in multiple integration test files and -you try to follow the steps in the [“Separating Modules into Different -Files”][separating-modules-into-files] section of Chapter 7 to -extract them into a common module. For example, if we create _tests/common.rs_ -and place a function named `setup` in it, we can add some code to `setup` that -we want to call from multiple test functions in multiple test files: +این رفتار متفاوت فایل‌های دایرکتوری _tests_ بیشترین توجه را زمانی جلب می‌کند که مجموعه‌ای از توابع کمکی برای استفاده در چندین فایل تست یکپارچه دارید و سعی می‌کنید مراحل بخش [“جدا کردن ماژول‌ها به فایل‌های مختلف”][separating-modules-into-files] در فصل ۷ را برای استخراج آن‌ها به یک ماژول مشترک دنبال کنید. برای مثال، اگر _tests/common.rs_ ایجاد کنیم و یک تابع به نام `setup` در آن قرار دهیم، می‌توانیم کدی به `setup` اضافه کنیم که می‌خواهیم از چندین تابع تست در چندین فایل تست فراخوانی کنیم: Filename: tests/common.rs @@ -181,19 +108,13 @@ we want to call from multiple test functions in multiple test files: {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-12-shared-test-code-problem/tests/common.rs}} ``` -When we run the tests again, we’ll see a new section in the test output for the -_common.rs_ file, even though this file doesn’t contain any test functions nor -did we call the `setup` function from anywhere: +وقتی دوباره تست‌ها را اجرا می‌کنیم، یک بخش جدید در خروجی تست برای فایل _common.rs_ خواهیم دید، حتی اگر این فایل هیچ تابع تستی ندارد و تابع `setup` را از هیچ جایی فراخوانی نکرده‌ایم: ```console {{#include ../listings/ch11-writing-automated-tests/no-listing-12-shared-test-code-problem/output.txt}} ``` -Having `common` appear in the test results with `running 0 tests` displayed for -it is not what we wanted. We just wanted to share some code with the other -integration test files. To avoid having `common` appear in the test output, -instead of creating _tests/common.rs_, we’ll create _tests/common/mod.rs_. The -project directory now looks like this: +داشتن `common` در نتایج تست با `running 0 tests` نمایش داده شده برای آن، چیزی نبود که می‌خواستیم. ما فقط می‌خواستیم برخی کدها را با دیگر فایل‌های تست یکپارچه به اشتراک بگذاریم. برای جلوگیری از نمایش `common` در خروجی تست، به جای ایجاد _tests/common.rs_، فایل _tests/common/mod.rs_ را ایجاد می‌کنیم. اکنون ساختار دایرکتوری پروژه به این شکل است: ```text ├── Cargo.lock @@ -206,17 +127,14 @@ project directory now looks like this: └── integration_test.rs ``` -This is the older naming convention that Rust also understands that we mentioned -in [“Alternate File Paths”][alt-paths] in Chapter 7. Naming the -file this way tells Rust not to treat the `common` module as an integration test -file. When we move the `setup` function code into _tests/common/mod.rs_ and -delete the _tests/common.rs_ file, the section in the test output will no longer -appear. Files in subdirectories of the _tests_ directory don’t get compiled as -separate crates or have sections in the test output. +این روش نام‌گذاری قدیمی‌تر است که Rust نیز آن را می‌شناسد و در بخش [“مسیرهای فایل جایگزین”][alt-paths] در فصل ۷ به آن اشاره کردیم. +نام‌گذاری فایل به این صورت به Rust می‌گوید که ماژول `common` را به عنوان یک فایل تست یکپارچه (integration test) در نظر نگیرد. +وقتی کد تابع `setup` را به فایل _tests/common/mod.rs_ منتقل کنیم و فایل _tests/common.rs_ را حذف کنیم، +بخش مربوطه در خروجی تست دیگر ظاهر نخواهد شد. +فایل‌های موجود در زیرشاخه‌های دایرکتوری _tests_ به صورت crateهای جداگانه کامپایل نمی‌شوند +و در خروجی تست نیز بخش مجزایی ندارند. -After we’ve created _tests/common/mod.rs_, we can use it from any of the -integration test files as a module. Here’s an example of calling the `setup` -function from the `it_adds_two` test in _tests/integration_test.rs_: +پس از ایجاد _tests/common/mod.rs_، می‌توانیم از آن به عنوان یک ماژول در هر یک از فایل‌های تست یکپارچه استفاده کنیم. در اینجا یک مثال از فراخوانی تابع `setup` از تست `it_adds_two` در _tests/integration_test.rs_ آمده است: Filename: tests/integration_test.rs @@ -224,39 +142,20 @@ function from the `it_adds_two` test in _tests/integration_test.rs_: {{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-13-fix-shared-test-code-problem/tests/integration_test.rs}} ``` -Note that the `mod common;` declaration is the same as the module declaration -we demonstrated in Listing 7-21. Then, in the test function, we can call the -`common::setup()` function. - -#### Integration Tests for Binary Crates - -If our project is a binary crate that only contains a _src/main.rs_ file and -doesn’t have a _src/lib.rs_ file, we can’t create integration tests in the -_tests_ directory and bring functions defined in the _src/main.rs_ file into -scope with a `use` statement. Only library crates expose functions that other -crates can use; binary crates are meant to be run on their own. - -This is one of the reasons Rust projects that provide a binary have a -straightforward _src/main.rs_ file that calls logic that lives in the -_src/lib.rs_ file. Using that structure, integration tests _can_ test the -library crate with `use` to make the important functionality available. If the -important functionality works, the small amount of code in the _src/main.rs_ -file will work as well, and that small amount of code doesn’t need to be tested. - -## Summary - -Rust’s testing features provide a way to specify how code should function to -ensure it continues to work as you expect, even as you make changes. Unit tests -exercise different parts of a library separately and can test private -implementation details. Integration tests check that many parts of the library -work together correctly, and they use the library’s public API to test the code -in the same way external code will use it. Even though Rust’s type system and -ownership rules help prevent some kinds of bugs, tests are still important to -reduce logic bugs having to do with how your code is expected to behave. - -Let’s combine the knowledge you learned in this chapter and in previous -chapters to work on a project! - -[paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html -[separating-modules-into-files]: ch07-05-separating-modules-into-different-files.html -[alt-paths]: ch07-05-separating-modules-into-different-files.html#alternate-file-paths +توجه داشته باشید که اعلان `mod common;` مشابه اعلان ماژولی است که در لیست ۷-۲۱ نشان دادیم. سپس، در تابع تست، می‌توانیم تابع `common::setup()` را فراخوانی کنیم. + +#### تست‌های یکپارچه برای crate‌های دودویی + +اگر پروژه ما یک crate دودویی باشد که فقط شامل یک فایل _src/main.rs_ است و فایل _src/lib.rs_ ندارد، نمی‌توانیم تست‌های یکپارچه را در دایرکتوری _tests_ ایجاد کنیم و توابع تعریف‌شده در فایل _src/main.rs_ را با یک عبارت `use` به دامنه وارد کنیم. فقط crate‌های کتابخانه‌ای توابعی را که سایر crate‌ها می‌توانند استفاده کنند در معرض قرار می‌دهند؛ crate‌های دودویی برای اجرای مستقل طراحی شده‌اند. + +این یکی از دلایلی است که پروژه‌های Rust که یک دودویی ارائه می‌دهند، معمولاً یک فایل _src/main.rs_ ساده دارند که به منطق موجود در فایل _src/lib.rs_ فراخوانی می‌کند. با استفاده از این ساختار، تست‌های یکپارچه می‌توانند crate کتابخانه‌ای را با `use` تست کنند تا قابلیت مهم را در دسترس قرار دهند. اگر قابلیت مهم کار کند، مقدار کمی کد در فایل _src/main.rs_ نیز کار خواهد کرد، و نیازی به تست آن مقدار کم از کد نیست. + +## خلاصه + +ویژگی‌های تست‌نویسی در Rust راهی برای مشخص کردن نحوه عملکرد کد فراهم می‌کنند تا اطمینان حاصل شود که کد همانطور که انتظار می‌رود کار می‌کند، حتی زمانی که تغییراتی در آن ایجاد می‌کنید. تست‌های واحد بخش‌های مختلف یک کتابخانه را به طور جداگانه آزمایش می‌کنند و می‌توانند جزئیات پیاده‌سازی خصوصی را تست کنند. تست‌های یکپارچه بررسی می‌کنند که آیا بخش‌های مختلف کتابخانه به درستی با یکدیگر کار می‌کنند یا نه، و از رابط عمومی کتابخانه برای تست کد به همان روشی که کد خارجی از آن استفاده می‌کند، استفاده می‌کنند. حتی با وجود اینکه سیستم نوع‌ها و قوانین مالکیت در Rust به جلوگیری از برخی انواع باگ‌ها کمک می‌کند، تست‌ها همچنان برای کاهش باگ‌های منطقی که به نحوه عملکرد مورد انتظار کد مربوط می‌شوند، مهم هستند. + +بیایید دانش خود را که در این فصل و فصل‌های قبلی یاد گرفتید، ترکیب کرده و روی یک پروژه کار کنیم! + +[paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html +[separating-modules-into-files]: ch07-05-separating-modules-into-different-files.html +[alt-paths]: ch07-05-separating-modules-into-different-files.html#alternate-file-paths diff --git a/src/ch12-00-an-io-project.md b/src/ch12-00-an-io-project.md index e421cc12f0..1aae490137 100644 --- a/src/ch12-00-an-io-project.md +++ b/src/ch12-00-an-io-project.md @@ -1,48 +1,27 @@ -# An I/O Project: Building a Command Line Program - -This chapter is a recap of the many skills you’ve learned so far and an -exploration of a few more standard library features. We’ll build a command line -tool that interacts with file and command line input/output to practice some of -the Rust concepts you now have under your belt. - -Rust’s speed, safety, single binary output, and cross-platform support make it -an ideal language for creating command line tools, so for our project, we’ll -make our own version of the classic command line search tool `grep` -(**g**lobally search a **r**egular **e**xpression and **p**rint). In the -simplest use case, `grep` searches a specified file for a specified string. To -do so, `grep` takes as its arguments a file path and a string. Then it reads -the file, finds lines in that file that contain the string argument, and prints -those lines. - -Along the way, we’ll show how to make our command line tool use the terminal -features that many other command line tools use. We’ll read the value of an -environment variable to allow the user to configure the behavior of our tool. -We’ll also print error messages to the standard error console stream (`stderr`) -instead of standard output (`stdout`) so that, for example, the user can -redirect successful output to a file while still seeing error messages onscreen. - -One Rust community member, Andrew Gallant, has already created a fully -featured, very fast version of `grep`, called `ripgrep`. By comparison, our -version will be fairly simple, but this chapter will give you some of the -background knowledge you need to understand a real-world project such as -`ripgrep`. - -Our `grep` project will combine a number of concepts you’ve learned so far: - -- Organizing code ([Chapter 7][ch7]) -- Using vectors and strings ([Chapter 8][ch8]) -- Handling errors ([Chapter 9][ch9]) -- Using traits and lifetimes where appropriate ([Chapter 10][ch10]) -- Writing tests ([Chapter 11][ch11]) - -We’ll also briefly introduce closures, iterators, and trait objects, which -[Chapter 13][ch13] and [Chapter 18][ch18] will -cover in detail. - -[ch7]: ch07-00-managing-growing-projects-with-packages-crates-and-modules.html -[ch8]: ch08-00-common-collections.html -[ch9]: ch09-00-error-handling.html -[ch10]: ch10-00-generics.html -[ch11]: ch11-00-testing.html -[ch13]: ch13-00-functional-features.html -[ch18]: ch18-00-oop.html +# یک پروژه ورودی/خروجی: ساخت یک برنامه خط فرمان + +این فصل مروری بر بسیاری از مهارت‌هایی است که تا کنون آموخته‌اید و همچنین بررسی چند ویژگی دیگر از کتابخانه استاندارد. ما یک ابزار خط فرمان خواهیم ساخت که با ورودی/خروجی فایل و خط فرمان تعامل می‌کند تا برخی از مفاهیم Rust را که اکنون در اختیار دارید تمرین کنیم. + +سرعت، ایمنی، خروجی تک‌باینری و پشتیبانی چند‌پلتفرمی Rust، آن را به زبانی ایده‌آل برای ایجاد ابزارهای خط فرمان تبدیل می‌کند. بنابراین برای پروژه خود، نسخه‌ای از ابزار جستجوی خط فرمان کلاسیک `grep` (**g**lobally search a **r**egular **e**xpression and **p**rint) را خواهیم ساخت. در ساده‌ترین حالت، `grep` یک فایل مشخص را برای یک رشته مشخص جستجو می‌کند. برای انجام این کار، `grep` به عنوان آرگومان‌های خود مسیر فایل و یک رشته را دریافت می‌کند. سپس فایل را می‌خواند، خطوطی که شامل آرگومان رشته هستند را پیدا می‌کند و آن خطوط را چاپ می‌کند. + +در طول مسیر، نشان خواهیم داد که چگونه ابزار خط فرمان ما از ویژگی‌های ترمینال استفاده کند که بسیاری از ابزارهای خط فرمان دیگر از آن‌ها استفاده می‌کنند. مقدار یک متغیر محیطی را برای اجازه به کاربر برای پیکربندی رفتار ابزار خود می‌خوانیم. همچنین پیام‌های خطا را به جریان کنسول خطای استاندارد (`stderr`) به جای خروجی استاندارد (`stdout`) چاپ می‌کنیم تا مثلاً کاربر بتواند خروجی موفقیت‌آمیز را به یک فایل هدایت کند در حالی که هنوز پیام‌های خطا را روی صفحه مشاهده می‌کند. + +یکی از اعضای جامعه Rust، Andrew Gallant، نسخه‌ای کامل، بسیار سریع از `grep` به نام `ripgrep` ایجاد کرده است. در مقایسه، نسخه ما نسبتاً ساده خواهد بود، اما این فصل به شما برخی از دانش‌های پایه‌ای که برای درک پروژه‌های واقعی مانند `ripgrep` نیاز دارید را خواهد داد. + +پروژه `grep` ما ترکیبی از تعدادی مفاهیمی است که تاکنون آموخته‌اید: + +- سازماندهی کد ([فصل ۷][ch7]) +- استفاده از بردارها و رشته‌ها ([فصل ۸][ch8]) +- مدیریت خطاها ([فصل ۹][ch9]) +- استفاده از صفات و طول عمرها در موارد مناسب ([فصل ۱۰][ch10]) +- نوشتن تست‌ها ([فصل ۱۱][ch11]) + +همچنین به طور مختصر به معرفی closures، iterators، و trait objects می‌پردازیم که به طور کامل در [فصل ۱۳][ch13] و [فصل ۱۸][ch18] پوشش داده خواهند شد. + +[ch7]: ch07-00-managing-growing-projects-with-packages-crates-and-modules.html +[ch8]: ch08-00-common-collections.html +[ch9]: ch09-00-error-handling.html +[ch10]: ch10-00-generics.html +[ch11]: ch11-00-testing.html +[ch13]: ch13-00-functional-features.html +[ch18]: ch18-00-oop.html diff --git a/src/ch12-01-accepting-command-line-arguments.md b/src/ch12-01-accepting-command-line-arguments.md index 34e4504249..81f23bd29a 100644 --- a/src/ch12-01-accepting-command-line-arguments.md +++ b/src/ch12-01-accepting-command-line-arguments.md @@ -1,8 +1,6 @@ -## Accepting Command Line Arguments +## پذیرش آرگومان‌های خط فرمان -Let’s create a new project with, as always, `cargo new`. We’ll call our project -`minigrep` to distinguish it from the `grep` tool that you might already have -on your system. +بیایید با استفاده از `cargo new` یک پروژه جدید ایجاد کنیم. پروژه خود را `minigrep` می‌نامیم تا آن را از ابزار `grep` که ممکن است در سیستم شما وجود داشته باشد متمایز کنیم. ```console $ cargo new minigrep @@ -10,36 +8,21 @@ $ cargo new minigrep $ cd minigrep ``` -The first task is to make `minigrep` accept its two command line arguments: the -file path and a string to search for. That is, we want to be able to run our -program with `cargo run`, two hyphens to indicate the following arguments are -for our program rather than for `cargo`, a string to search for, and a path to -a file to search in, like so: +اولین کار این است که `minigrep` آرگومان‌های خط فرمان خود، شامل مسیر فایل و رشته‌ای برای جستجو، را بپذیرد. به عبارت دیگر، می‌خواهیم بتوانیم برنامه خود را با `cargo run`، دو خط تیره برای نشان دادن اینکه آرگومان‌های بعدی برای برنامه ما هستند و نه برای `cargo`، یک رشته برای جستجو و یک مسیر فایل برای جستجو اجرا کنیم، مانند زیر: ```console $ cargo run -- searchstring example-filename.txt ``` -Right now, the program generated by `cargo new` cannot process arguments we -give it. Some existing libraries on [crates.io](https://crates.io/) can help -with writing a program that accepts command line arguments, but because you’re -just learning this concept, let’s implement this capability ourselves. +در حال حاضر، برنامه‌ای که توسط `cargo new` تولید شده است نمی‌تواند آرگومان‌هایی که به آن می‌دهیم را پردازش کند. برخی کتابخانه‌های موجود در [crates.io](https://crates.io/) می‌توانند برای نوشتن برنامه‌ای که آرگومان‌های خط فرمان را بپذیرد کمک کنند، اما چون شما تازه با این مفهوم آشنا می‌شوید، بیایید این قابلیت را خودمان پیاده‌سازی کنیم. -### Reading the Argument Values +### خواندن مقادیر آرگومان‌ها -To enable `minigrep` to read the values of command line arguments we pass to -it, we’ll need the `std::env::args` function provided in Rust’s standard -library. This function returns an iterator of the command line arguments passed -to `minigrep`. We’ll cover iterators fully in [Chapter 13][ch13]. For now, you only need to know two details about iterators: iterators -produce a series of values, and we can call the `collect` method on an iterator -to turn it into a collection, such as a vector, that contains all the elements -the iterator produces. +برای اینکه `minigrep` بتواند مقادیر آرگومان‌های خط فرمان را که به آن می‌دهیم بخواند، به تابع `std::env::args` که در کتابخانه استاندارد Rust ارائه شده است نیاز خواهیم داشت. این تابع یک iterator از آرگومان‌های خط فرمانی که به `minigrep` داده شده است بازمی‌گرداند. ما در [فصل ۱۳][ch13] به طور کامل iteratorها را پوشش خواهیم داد. در حال حاضر، فقط باید دو نکته درباره iteratorها بدانید: iteratorها یک سری مقادیر تولید می‌کنند و ما می‌توانیم تابع `collect` را روی یک iterator فراخوانی کنیم تا آن را به یک collection، مانند یک بردار، که شامل تمام عناصر تولیدشده توسط iterator است، تبدیل کنیم. -The code in Listing 12-1 allows your `minigrep` program to read any command -line arguments passed to it, and then collect the values into a vector. +کد موجود در لیست ۱۲-۱ به برنامه `minigrep` شما اجازه می‌دهد تا هر آرگومان خط فرمانی که به آن داده شده را بخواند و سپس مقادیر را به یک بردار جمع‌آوری کند. -+ ```rust {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-01/src/main.rs}} @@ -47,35 +30,15 @@ line arguments passed to it, and then collect the values into a vector. -First we bring the `std::env` module into scope with a `use` statement so we -can use its `args` function. Notice that the `std::env::args` function is -nested in two levels of modules. As we discussed in [Chapter -7][ch7-idiomatic-use], in cases where the desired function is -nested in more than one module, we’ve chosen to bring the parent module into -scope rather than the function. By doing so, we can easily use other functions -from `std::env`. It’s also less ambiguous than adding `use std::env::args` and -then calling the function with just `args`, because `args` might easily be -mistaken for a function that’s defined in the current module. - -> ### The `args` Function and Invalid Unicode +ابتدا ماژول `std::env` را با یک دستور `use` به دامنه وارد می‌کنیم تا بتوانیم از تابع `args` آن استفاده کنیم. توجه کنید که تابع `std::env::args` در دو سطح ماژول تو در تو قرار دارد. همانطور که در [فصل ۷][ch7-idiomatic-use] بحث کردیم، در مواردی که تابع موردنظر در بیش از یک ماژول تو در تو قرار دارد، ترجیح می‌دهیم ماژول والد را به دامنه وارد کنیم نه تابع. با این کار، می‌توانیم به راحتی از توابع دیگر `std::env` استفاده کنیم. همچنین این روش کمتر مبهم است نسبت به اضافه کردن `use std::env::args` و سپس فراخوانی تابع با فقط `args`، چون `args` ممکن است به راحتی با یک تابع تعریف‌شده در ماژول جاری اشتباه گرفته شود. + +> ### تابع `args` و یونیکد نامعتبر > -> Note that `std::env::args` will panic if any argument contains invalid -> Unicode. If your program needs to accept arguments containing invalid -> Unicode, use `std::env::args_os` instead. That function returns an iterator -> that produces `OsString` values instead of `String` values. We’ve chosen to -> use `std::env::args` here for simplicity because `OsString` values differ per -> platform and are more complex to work with than `String` values. - -On the first line of `main`, we call `env::args`, and we immediately use -`collect` to turn the iterator into a vector containing all the values produced -by the iterator. We can use the `collect` function to create many kinds of -collections, so we explicitly annotate the type of `args` to specify that we -want a vector of strings. Although you very rarely need to annotate types in -Rust, `collect` is one function you do often need to annotate because Rust -isn’t able to infer the kind of collection you want. - -Finally, we print the vector using the debug macro. Let’s try running the code -first with no arguments and then with two arguments: +> توجه داشته باشید که `std::env::args` اگر هر آرگومانی شامل یونیکد نامعتبر باشد، پانیک خواهد کرد. اگر برنامه شما نیاز به پذیرش آرگومان‌هایی با یونیکد نامعتبر دارد، به جای آن از `std::env::args_os` استفاده کنید. این تابع یک iterator بازمی‌گرداند که مقادیر `OsString` به جای `String` تولید می‌کند. ما برای سادگی `std::env::args` را اینجا انتخاب کرده‌ایم زیرا مقادیر `OsString` بسته به پلتفرم متفاوت هستند و کار با آن‌ها پیچیده‌تر از مقادیر `String` است. + +در اولین خط از تابع `main`، ما تابع `env::args` را فراخوانی می‌کنیم و بلافاصله از تابع `collect` استفاده می‌کنیم تا iterator را به یک بردار که شامل تمام مقادیر تولید‌شده توسط iterator است، تبدیل کنیم. می‌توانیم از تابع `collect` برای ایجاد انواع مختلفی از collectionها استفاده کنیم، بنابراین نوع `args` را به طور صریح با ذکر می‌کنیم که می‌خواهیم یک بردار از رشته‌ها داشته باشیم. با اینکه به ندرت نیاز به ذکر نوع‌ها در Rust دارید، تابع `collect` یکی از تابع‌هایی است که اغلب باید نوع آن را ذکر کنید، زیرا Rust نمی‌تواند نوع collection مورد نظر شما را استنباط کند. + +در نهایت، بردار را با استفاده از ماکروی debug چاپ می‌کنیم. بیایید ابتدا کد را بدون آرگومان اجرا کنیم و سپس با دو آرگومان: ```console {{#include ../listings/ch12-an-io-project/listing-12-01/output.txt}} @@ -85,22 +48,13 @@ first with no arguments and then with two arguments: {{#include ../listings/ch12-an-io-project/output-only-01-with-args/output.txt}} ``` -Notice that the first value in the vector is `"target/debug/minigrep"`, which -is the name of our binary. This matches the behavior of the arguments list in -C, letting programs use the name by which they were invoked in their execution. -It’s often convenient to have access to the program name in case you want to -print it in messages or change the behavior of the program based on what -command line alias was used to invoke the program. But for the purposes of this -chapter, we’ll ignore it and save only the two arguments we need. +توجه کنید که اولین مقدار در بردار `"target/debug/minigrep"` است، که نام باینری ما است. این رفتار با لیست آرگومان‌ها در زبان C مطابقت دارد و به برنامه‌ها اجازه می‌دهد از نامی که با آن اجرا شده‌اند، در اجرای خود استفاده کنند. دسترسی به نام برنامه اغلب مفید است، مثلاً برای چاپ آن در پیام‌ها یا تغییر رفتار برنامه بر اساس نام مستعار خط فرمانی که برای اجرای برنامه استفاده شده است. اما برای اهداف این فصل، آن را نادیده می‌گیریم و فقط دو آرگومان مورد نیاز را ذخیره می‌کنیم. -### Saving the Argument Values in Variables +### ذخیره مقادیر آرگومان‌ها در متغیرها -The program is currently able to access the values specified as command line -arguments. Now we need to save the values of the two arguments in variables so -we can use the values throughout the rest of the program. We do that in Listing -12-2. +در حال حاضر، برنامه قادر به دسترسی به مقادیر مشخص‌شده به عنوان آرگومان‌های خط فرمان است. اکنون نیاز داریم مقادیر دو آرگومان را در متغیرهایی ذخیره کنیم تا بتوانیم از آن‌ها در بقیه برنامه استفاده کنیم. این کار را در لیست ۱۲-۲ انجام می‌دهیم. -+ ```rust,should_panic,noplayground {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-02/src/main.rs}} @@ -108,26 +62,15 @@ we can use the values throughout the rest of the program. We do that in Listing -As we saw when we printed the vector, the program’s name takes up the first -value in the vector at `args[0]`, so we’re starting arguments at index 1. The -first argument `minigrep` takes is the string we’re searching for, so we put a -reference to the first argument in the variable `query`. The second argument -will be the file path, so we put a reference to the second argument in the -variable `file_path`. +همانطور که هنگام چاپ بردار مشاهده کردیم، نام برنامه اولین مقدار در بردار را در `args[0]` اشغال می‌کند، بنابراین آرگومان‌ها را از اندیس (index)۱ شروع می‌کنیم. اولین آرگومان که `minigrep` دریافت می‌کند، رشته‌ای است که می‌خواهیم جستجو کنیم، بنابراین یک مرجع به اولین آرگومان را در متغیر `query` قرار می‌دهیم. آرگومان دوم مسیر فایل خواهد بود، بنابراین یک مرجع به آرگومان دوم را در متغیر `file_path` قرار می‌دهیم. -We temporarily print the values of these variables to prove that the code is -working as we intend. Let’s run this program again with the arguments `test` -and `sample.txt`: +ما به طور موقت مقادیر این متغیرها را چاپ می‌کنیم تا اثبات کنیم که کد همانطور که می‌خواهیم کار می‌کند. بیایید دوباره این برنامه را با آرگومان‌های `test` و `sample.txt` اجرا کنیم: ```console {{#include ../listings/ch12-an-io-project/listing-12-02/output.txt}} ``` -Great, the program is working! The values of the arguments we need are being -saved into the right variables. Later we’ll add some error handling to deal -with certain potential erroneous situations, such as when the user provides no -arguments; for now, we’ll ignore that situation and work on adding file-reading -capabilities instead. +عالی است، برنامه کار می‌کند! مقادیر آرگومان‌های مورد نیاز ما در متغیرهای درست ذخیره می‌شوند. بعداً برخی از خطاها را مدیریت خواهیم کرد، مثل وقتی که کاربر هیچ آرگومانی ارائه نمی‌دهد؛ فعلاً، آن شرایط را نادیده می‌گیریم و روی افزودن قابلیت خواندن فایل تمرکز می‌کنیم. -[ch13]: ch13-00-functional-features.html -[ch7-idiomatic-use]: ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths +[ch13]: ch13-00-functional-features.html +[ch7-idiomatic-use]: ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths \ No newline at end of file diff --git a/src/ch12-02-reading-a-file.md b/src/ch12-02-reading-a-file.md index bc6d79139b..c3a9891c1a 100644 --- a/src/ch12-02-reading-a-file.md +++ b/src/ch12-02-reading-a-file.md @@ -1,13 +1,8 @@ -## Reading a File +## خواندن یک فایل -Now we’ll add functionality to read the file specified in the `file_path` -argument. First we need a sample file to test it with: we’ll use a file with a -small amount of text over multiple lines with some repeated words. Listing 12-3 -has an Emily Dickinson poem that will work well! Create a file called -_poem.txt_ at the root level of your project, and enter the poem “I’m Nobody! -Who are you?” +اکنون قابلیت خواندن فایل مشخص‌شده در آرگومان `file_path` را اضافه می‌کنیم. ابتدا به یک فایل نمونه برای تست نیاز داریم: از یک فایل با مقدار کمی متن در چندین خط که برخی کلمات در آن تکرار شده‌اند استفاده می‌کنیم. لیست ۱۲-۳ شامل شعری از امیلی دیکینسون است که به خوبی برای این منظور مناسب است! یک فایل به نام _poem.txt_ در سطح اصلی پروژه خود ایجاد کنید و شعر "I’m Nobody! Who are you?" را وارد کنید. -+ ```text {{#include ../listings/ch12-an-io-project/listing-12-03/poem.txt}} @@ -15,10 +10,9 @@ Who are you?” -With the text in place, edit _src/main.rs_ and add code to read the file, as -shown in Listing 12-4. +با متن در جای خود، فایل _src/main.rs_ را ویرایش کرده و کدی برای خواندن فایل اضافه کنید، همانطور که در لیست ۱۲-۴ نشان داده شده است. -+ ```rust,should_panic,noplayground {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-04/src/main.rs:here}} @@ -26,31 +20,16 @@ shown in Listing 12-4. -First we bring in a relevant part of the standard library with a `use` -statement: we need `std::fs` to handle files. +ابتدا بخشی مرتبط از کتابخانه استاندارد را با یک دستور `use` وارد می‌کنیم: برای مدیریت فایل‌ها به `std::fs` نیاز داریم. -In `main`, the new statement `fs::read_to_string` takes the `file_path`, opens -that file, and returns a value of type `std::io::Result` that contains -the file’s contents. +در تابع `main`، دستور جدید `fs::read_to_string` مقدار `file_path` را می‌گیرد، آن فایل را باز می‌کند و مقداری از نوع `std::io::Result` را که شامل محتوای فایل است، بازمی‌گرداند. -After that, we again add a temporary `println!` statement that prints the value -of `contents` after the file is read, so we can check that the program is -working so far. +پس از آن، دوباره یک دستور موقت `println!` اضافه می‌کنیم که مقدار `contents` را پس از خواندن فایل چاپ می‌کند تا مطمئن شویم برنامه تا اینجا کار می‌کند. -Let’s run this code with any string as the first command line argument (because -we haven’t implemented the searching part yet) and the _poem.txt_ file as the -second argument: +بیایید این کد را با هر رشته‌ای به عنوان اولین آرگومان خط فرمان (چون هنوز بخش جستجو را پیاده‌سازی نکرده‌ایم) و فایل _poem.txt_ به عنوان آرگومان دوم اجرا کنیم: ```console {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-04/output.txt}} ``` -Great! The code read and then printed the contents of the file. But the code -has a few flaws. At the moment, the `main` function has multiple -responsibilities: generally, functions are clearer and easier to maintain if -each function is responsible for only one idea. The other problem is that we’re -not handling errors as well as we could. The program is still small, so these -flaws aren’t a big problem, but as the program grows, it will be harder to fix -them cleanly. It’s a good practice to begin refactoring early on when -developing a program because it’s much easier to refactor smaller amounts of -code. We’ll do that next. +عالی است! کد محتوای فایل را خواند و سپس چاپ کرد. اما کد چند نقص دارد. در حال حاضر، تابع `main` چندین مسئولیت دارد: به طور کلی، توابع واضح‌تر و آسان‌تر برای نگهداری هستند اگر هر تابع فقط مسئول یک ایده باشد. مشکل دیگر این است که ما خطاها را به خوبی مدیریت نمی‌کنیم. برنامه هنوز کوچک است، بنابراین این مشکلات مشکل بزرگی نیستند، اما با رشد برنامه، رفع آن‌ها به صورت تمیز سخت‌تر خواهد شد. بهتر است که زودتر در فرایند توسعه برنامه شروع به بازسازی کنیم، زیرا بازسازی کدهای کمتر بسیار آسان‌تر است. در مرحله بعد این کار را انجام خواهیم داد. diff --git a/src/ch12-03-improving-error-handling-and-modularity.md b/src/ch12-03-improving-error-handling-and-modularity.md index 39833691d4..29b4348e59 100644 --- a/src/ch12-03-improving-error-handling-and-modularity.md +++ b/src/ch12-03-improving-error-handling-and-modularity.md @@ -36,17 +36,17 @@ Let’s address these four problems by refactoring our project. ### Separation of Concerns for Binary Projects The organizational problem of allocating responsibility for multiple tasks to -the `main` function is common to many binary projects. As a result, many Rust -programmers find it useful to split up the separate concerns of a binary -program when the `main` function starts getting large. This process has the -following steps: +the `main` function is common to many binary projects. As a result, the Rust +community has developed guidelines for splitting the separate concerns of a +binary program when `main` starts getting large. This process has the following +steps: - Split your program into a _main.rs_ file and a _lib.rs_ file and move your program’s logic to _lib.rs_. - As long as your command line parsing logic is small, it can remain in - the `main` function. + _main.rs_. - When the command line parsing logic starts getting complicated, extract it - from the `main` function into other functions or types. + from _main.rs_ and move it to _lib.rs_. The responsibilities that remain in the `main` function after this process should be limited to the following: @@ -59,17 +59,18 @@ should be limited to the following: This pattern is about separating concerns: _main.rs_ handles running the program and _lib.rs_ handles all the logic of the task at hand. Because you can’t test the `main` function directly, this structure lets you test all of -your program’s logic by moving it out of the `main` function. The code that -remains in the `main` function will be small enough to verify its correctness -by reading it. Let’s rework our program by following this process. +your program’s logic by moving it into functions in _lib.rs_. The code that +remains in _main.rs_ will be small enough to verify its correctness by reading +it. Let’s rework our program by following this process. #### Extracting the Argument Parser We’ll extract the functionality for parsing arguments into a function that -`main` will call. Listing 12-5 shows the new start of the `main` function that -calls a new function `parse_config`, which we’ll define in _src/main.rs_. +`main` will call to prepare for moving the command line parsing logic to +_src/lib.rs_. Listing 12-5 shows the new start of `main` that calls a new +function `parse_config`, which we’ll define in _src/main.rs_ for the moment. -+ ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-05/src/main.rs:here}} @@ -77,40 +78,19 @@ calls a new function `parse_config`, which we’ll define in _src/main.rs_. -We’re still collecting the command line arguments into a vector, but instead of -assigning the argument value at index 1 to the variable `query` and the -argument value at index 2 to the variable `file_path` within the `main` -function, we pass the whole vector to the `parse_config` function. The -`parse_config` function then holds the logic that determines which argument -goes in which variable and passes the values back to `main`. We still create -the `query` and `file_path` variables in `main`, but `main` no longer has the -responsibility of determining how the command line arguments and variables -correspond. - -This rework may seem like overkill for our small program, but we’re refactoring -in small, incremental steps. After making this change, run the program again to -verify that the argument parsing still works. It’s good to check your progress -often, to help identify the cause of problems when they occur. - -#### Grouping Configuration Values - -We can take another small step to improve the `parse_config` function further. -At the moment, we’re returning a tuple, but then we immediately break that -tuple into individual parts again. This is a sign that perhaps we don’t have -the right abstraction yet. - -Another indicator that shows there’s room for improvement is the `config` part -of `parse_config`, which implies that the two values we return are related and -are both part of one configuration value. We’re not currently conveying this -meaning in the structure of the data other than by grouping the two values into -a tuple; we’ll instead put the two values into one struct and give each of the -struct fields a meaningful name. Doing so will make it easier for future -maintainers of this code to understand how the different values relate to each -other and what their purpose is. - -Listing 12-6 shows the improvements to the `parse_config` function. - -+ما همچنان آرگومان‌های خط فرمان را به یک بردار جمع‌آوری می‌کنیم، اما به جای اینکه مقدار آرگومان در اندیس (index)۱ را به متغیر `query` و مقدار آرگومان در اندیس (index)۲ را به متغیر `file_path` در تابع `main` اختصاص دهیم، کل بردار را به تابع `parse_config` ارسال می‌کنیم. تابع `parse_config` سپس منطق مشخص می‌کند که کدام آرگومان در کدام متغیر قرار می‌گیرد و مقادیر را به تابع `main` بازمی‌گرداند. ما همچنان متغیرهای `query` و `file_path` را در `main` ایجاد می‌کنیم، اما `main` دیگر مسئول تعیین ارتباط آرگومان‌های خط فرمان و متغیرها نیست. + +این تغییر ممکن است برای برنامه کوچک ما زیاده‌روی به نظر برسد، اما ما در حال بازسازی کد به صورت گام‌های کوچک و تدریجی هستیم. پس از اعمال این تغییر، دوباره برنامه را اجرا کنید تا اطمینان حاصل کنید که تجزیه آرگومان همچنان کار می‌کند. بررسی مداوم پیشرفت کد کمک می‌کند تا در صورت بروز مشکلات، علت آن‌ها را سریع‌تر شناسایی کنید. + +#### گروه‌بندی مقادیر پیکربندی + +می‌توانیم یک گام کوچک دیگر برای بهبود بیشتر تابع `parse_config` برداریم. در حال حاضر، ما یک tuple بازمی‌گردانیم، اما بلافاصله آن tuple را به قسمت‌های جداگانه تقسیم می‌کنیم. این نشانه‌ای است که شاید هنوز انتزاع درستی نداریم. + +نشانه دیگری که نشان می‌دهد جا برای بهبود وجود دارد، قسمت `config` در `parse_config` است، که نشان می‌دهد دو مقداری که بازمی‌گردانیم به هم مرتبط هستند و هر دو بخشی از یک مقدار پیکربندی هستند. ما در حال حاضر این معنا را در ساختار داده‌ها به جز با گروه‌بندی دو مقدار در یک tuple منتقل نمی‌کنیم؛ در عوض، این دو مقدار را در یک struct قرار می‌دهیم و به هر یک از فیلدهای struct نامی معنادار می‌دهیم. انجام این کار درک نحوه ارتباط مقادیر مختلف و هدف آن‌ها را برای نگهداری‌کنندگان آینده این کد آسان‌تر می‌کند. + +لیست ۱۲-۶ بهبودهای تابع `parse_config` را نشان می‌دهد. + + ```rust,should_panic,noplayground {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-06/src/main.rs:here}} @@ -118,65 +98,27 @@ Listing 12-6 shows the improvements to the `parse_config` function. -We’ve added a struct named `Config` defined to have fields named `query` and -`file_path`. The signature of `parse_config` now indicates that it returns a -`Config` value. In the body of `parse_config`, where we used to return -string slices that reference `String` values in `args`, we now define `Config` -to contain owned `String` values. The `args` variable in `main` is the owner of -the argument values and is only letting the `parse_config` function borrow -them, which means we’d violate Rust’s borrowing rules if `Config` tried to take -ownership of the values in `args`. - -There are a number of ways we could manage the `String` data; the easiest, -though somewhat inefficient, route is to call the `clone` method on the values. -This will make a full copy of the data for the `Config` instance to own, which -takes more time and memory than storing a reference to the string data. -However, cloning the data also makes our code very straightforward because we -don’t have to manage the lifetimes of the references; in this circumstance, -giving up a little performance to gain simplicity is a worthwhile trade-off. - -> ### The Trade-Offs of Using `clone` +ما یک ساختار جدید به نام `Config` تعریف کرده‌ایم که دارای فیلدهایی با نام‌های `query` و `file_path` است. امضای تابع `parse_config` اکنون نشان می‌دهد که این تابع یک مقدار `Config` را بازمی‌گرداند. در بدنه تابع `parse_config`، جایی که قبلاً اسلایس‌های رشته‌ای را که به مقادیر `String` در `args` اشاره می‌کردند بازمی‌گرداندیم، اکنون `Config` را طوری تعریف می‌کنیم که دارای مقادیر `String` متعلق به خود باشد. + +متغیر `args` در تابع `main` مالک مقادیر آرگومان است و فقط به تابع `parse_config` اجازه قرض گرفتن آن‌ها را می‌دهد، به این معنی که اگر `Config` بخواهد مالک مقادیر در `args` شود، قوانین قرض‌گیری Rust را نقض می‌کنیم. + +چندین روش برای مدیریت داده‌های `String` وجود دارد؛ ساده‌ترین و شاید ناکارآمدترین روش، فراخوانی متد `clone` روی مقادیر است. این کار یک کپی کامل از داده‌ها برای نمونه `Config` ایجاد می‌کند که مالک آن است. این روش زمان و حافظه بیشتری نسبت به ذخیره یک مرجع به داده‌ها نیاز دارد. با این حال، کپی کردن داده‌ها باعث می‌شود که کد ما بسیار ساده شود زیرا نیازی به مدیریت طول عمر مراجع نداریم؛ در این شرایط، از دست دادن کمی کارایی برای دستیابی به سادگی ارزشمند است. + +> ### هزینه‌ها و مزایای استفاده از `clone` > -> There’s a tendency among many Rustaceans to avoid using `clone` to fix -> ownership problems because of its runtime cost. In -> [Chapter 13][ch13], you’ll learn how to use more efficient -> methods in this type of situation. But for now, it’s okay to copy a few -> strings to continue making progress because you’ll make these copies only -> once and your file path and query string are very small. It’s better to have -> a working program that’s a bit inefficient than to try to hyperoptimize code -> on your first pass. As you become more experienced with Rust, it’ll be -> easier to start with the most efficient solution, but for now, it’s -> perfectly acceptable to call `clone`. - -We’ve updated `main` so it places the instance of `Config` returned by -`parse_config` into a variable named `config`, and we updated the code that -previously used the separate `query` and `file_path` variables so it now uses -the fields on the `Config` struct instead. - -Now our code more clearly conveys that `query` and `file_path` are related and -that their purpose is to configure how the program will work. Any code that -uses these values knows to find them in the `config` instance in the fields -named for their purpose. - -#### Creating a Constructor for `Config` - -So far, we’ve extracted the logic responsible for parsing the command line -arguments from `main` and placed it in the `parse_config` function. Doing so -helped us see that the `query` and `file_path` values were related, and that -relationship should be conveyed in our code. We then added a `Config` struct to -name the related purpose of `query` and `file_path` and to be able to return the -values’ names as struct field names from the `parse_config` function. - -So now that the purpose of the `parse_config` function is to create a `Config` -instance, we can change `parse_config` from a plain function to a function -named `new` that is associated with the `Config` struct. Making this change -will make the code more idiomatic. We can create instances of types in the -standard library, such as `String`, by calling `String::new`. Similarly, by -changing `parse_config` into a `new` function associated with `Config`, we’ll -be able to create instances of `Config` by calling `Config::new`. Listing 12-7 -shows the changes we need to make. - -+> در بین بسیاری از برنامه‌نویسان Rust، تمایلی به استفاده از `clone` برای رفع مشکلات مالکیت به دلیل هزینه اجرای آن وجود دارد. در [فصل ۱۳][ch13]، یاد خواهید گرفت که چگونه در این نوع موقعیت‌ها از روش‌های کارآمدتر استفاده کنید. اما در حال حاضر، کپی کردن چند رشته برای ادامه پیشرفت اشکالی ندارد زیرا این کپی‌ها فقط یک‌بار انجام می‌شوند و مسیر فایل و رشته جستجوی شما بسیار کوچک هستند. بهتر است یک برنامه کارا که کمی ناکارآمد است داشته باشید تا اینکه در اولین تلاش خود برای نوشتن کد، بهینه‌سازی بیش از حد انجام دهید. با تجربه بیشتر در Rust، شروع با راه‌حل کارآمدتر آسان‌تر خواهد بود، اما در حال حاضر استفاده از `clone` کاملاً قابل قبول است. + +ما تابع `main` را به‌روزرسانی کردیم تا نمونه‌ای از `Config` که توسط `parse_config` بازگردانده می‌شود را در یک متغیر به نام `config` قرار دهد، و کدی که قبلاً از متغیرهای جداگانه `query` و `file_path` استفاده می‌کرد، اکنون از فیلدهای موجود در struct `Config` استفاده می‌کند. + +اکنون کد ما به‌وضوح نشان می‌دهد که `query` و `file_path` به هم مرتبط هستند و هدف آن‌ها تنظیم نحوه کار برنامه است. هر کدی که از این مقادیر استفاده می‌کند می‌داند که باید آن‌ها را در نمونه `config` در فیلدهایی که نام آن‌ها برای هدفشان انتخاب شده است، پیدا کند. + +#### ایجاد سازنده برای `Config` + +تا اینجا، منطق مسئول تجزیه آرگومان‌های خط فرمان را از `main` استخراج کرده و در تابع `parse_config` قرار داده‌ایم. این کار به ما کمک کرد ببینیم که مقادیر `query` و `file_path` به هم مرتبط هستند و این رابطه باید در کد ما منتقل شود. سپس یک struct به نام `Config` اضافه کردیم تا هدف مشترک `query` و `file_path` را نام‌گذاری کنیم و بتوانیم نام مقادیر را به‌عنوان فیلدهای struct از تابع `parse_config` بازگردانیم. + +حالا که هدف تابع `parse_config` ایجاد یک نمونه از `Config` است، می‌توانیم `parse_config` را از یک تابع معمولی به یک تابع با نام `new` تغییر دهیم که به struct `Config` مرتبط است. این تغییر کد را به‌صورت idiomatic‌تر می‌کند. ما می‌توانیم نمونه‌هایی از انواع موجود در کتابخانه استاندارد، مانند `String`، را با فراخوانی `String::new` ایجاد کنیم. به همین ترتیب، با تغییر `parse_config` به تابع `new` مرتبط با `Config`، می‌توانیم نمونه‌هایی از `Config` را با فراخوانی `Config::new` ایجاد کنیم. لیست ۱۲-۷ تغییرات لازم را نشان می‌دهد. + + ```rust,should_panic,noplayground {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-07/src/main.rs:here}} @@ -184,33 +126,23 @@ shows the changes we need to make. -We’ve updated `main` where we were calling `parse_config` to instead call -`Config::new`. We’ve changed the name of `parse_config` to `new` and moved it -within an `impl` block, which associates the `new` function with `Config`. Try -compiling this code again to make sure it works. +ما تابع `main` را که در آن `parse_config` را فراخوانی می‌کردیم به‌روزرسانی کرده‌ایم تا به‌جای آن `Config::new` را فراخوانی کند. نام `parse_config` را به `new` تغییر داده و آن را در یک بلوک `impl` قرار داده‌ایم که تابع `new` را به `Config` مرتبط می‌کند. کد را دوباره کامپایل کنید تا مطمئن شوید که کار می‌کند. -### Fixing the Error Handling +### رفع مشکلات مدیریت خطا -Now we’ll work on fixing our error handling. Recall that attempting to access -the values in the `args` vector at index 1 or index 2 will cause the program to -panic if the vector contains fewer than three items. Try running the program -without any arguments; it will look like this: +حالا روی رفع مشکلات مدیریت خطا کار می‌کنیم. به خاطر بیاورید که تلاش برای دسترسی به مقادیر موجود در بردار `args` در اندیس (index)۱ یا ۲ باعث می‌شود برنامه در صورت داشتن کمتر از سه آیتم، دچار وحشت شود. برنامه را بدون هیچ آرگومانی اجرا کنید؛ این حالت به شکل زیر خواهد بود: ```console {{#include ../listings/ch12-an-io-project/listing-12-07/output.txt}} ``` -The line `index out of bounds: the len is 1 but the index is 1` is an error -message intended for programmers. It won’t help our end users understand what -they should do instead. Let’s fix that now. +خط `index out of bounds: the len is 1 but the index is 1` یک پیام خطا است که برای برنامه‌نویسان در نظر گرفته شده است. این پیام به کاربران نهایی کمکی نمی‌کند تا بفهمند باید چه کار کنند. حالا این مشکل را رفع می‌کنیم. -#### Improving the Error Message +#### بهبود پیام خطا -In Listing 12-8, we add a check in the `new` function that will verify that the -slice is long enough before accessing index 1 and index 2. If the slice isn’t -long enough, the program panics and displays a better error message. +در لیست ۱۲-۸، یک بررسی در تابع `new` اضافه می‌کنیم که بررسی می‌کند آیا آرایه به‌اندازه کافی طولانی است تا بتوان به اندیس‌های ۱ و ۲ دسترسی داشت. اگر طول آرایه کافی نباشد، برنامه دچار وحشت می‌شود و یک پیام خطای بهتر نمایش می‌دهد. -+ ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-08/src/main.rs:here}} @@ -218,50 +150,25 @@ long enough, the program panics and displays a better error message. -This code is similar to [the `Guess::new` function we wrote in Listing -9-13][ch9-custom-types], where we called `panic!` when the -`value` argument was out of the range of valid values. Instead of checking for -a range of values here, we’re checking that the length of `args` is at least -`3` and the rest of the function can operate under the assumption that this -condition has been met. If `args` has fewer than three items, this condition -will be `true`, and we call the `panic!` macro to end the program immediately. +این کد شبیه به [تابع `Guess::new` که در لیست ۹-۱۳ نوشتیم][ch9-custom-types] است، جایی که وقتی آرگومان `value` خارج از محدوده مقادیر معتبر بود، `panic!` فراخوانی کردیم. به جای بررسی محدوده مقادیر، در اینجا بررسی می‌کنیم که طول `args` حداقل برابر با `3` باشد و بقیه تابع می‌تواند با فرض اینکه این شرط برقرار شده است، عمل کند. اگر `args` کمتر از سه آیتم داشته باشد، این شرط `true` خواهد بود و ما ماکرو `panic!` را برای خاتمه برنامه بلافاصله فراخوانی می‌کنیم. -With these extra few lines of code in `new`, let’s run the program without any -arguments again to see what the error looks like now: +با این چند خط اضافی در `new`، بیایید دوباره برنامه را بدون هیچ آرگومانی اجرا کنیم تا ببینیم اکنون پیام خطا چگونه است: ```console {{#include ../listings/ch12-an-io-project/listing-12-08/output.txt}} ``` -This output is better: we now have a reasonable error message. However, we also -have extraneous information we don’t want to give to our users. Perhaps the -technique we used in Listing 9-13 isn’t the best one to use here: a call to -`panic!` is more appropriate for a programming problem than a usage problem, -[as discussed in Chapter 9][ch9-error-guidelines]. Instead, -we’ll use the other technique you learned about in Chapter 9—[returning a -`Result`][ch9-result] that indicates either success or an error. - - +این خروجی بهتر است: اکنون یک پیام خطای منطقی داریم. با این حال، هنوز اطلاعات اضافی داریم که نمی‌خواهیم به کاربران خود ارائه دهیم. شاید تکنیکی که در لیست ۹-۱۳ استفاده کردیم بهترین گزینه برای اینجا نباشد: یک فراخوانی به `panic!` برای مشکل برنامه‌نویسی مناسب‌تر است تا یک مشکل استفاده، [همان‌طور که در فصل ۹ بحث شد][ch9-error-guidelines]. در عوض، از تکنیک دیگری که در فصل ۹ یاد گرفتید استفاده می‌کنیم—[بازگرداندن یک `Result`][ch9-result] که نشان‌دهنده موفقیت یا خطا است. -#### Returning a `Result` Instead of Calling `panic!` +#### بازگرداندن یک `Result` به جای فراخوانی `panic!` -We can instead return a `Result` value that will contain a `Config` instance in -the successful case and will describe the problem in the error case. We’re also -going to change the function name from `new` to `build` because many -programmers expect `new` functions to never fail. When `Config::build` is -communicating to `main`, we can use the `Result` type to signal there was a -problem. Then we can change `main` to convert an `Err` variant into a more -practical error for our users without the surrounding text about `thread -'main'` and `RUST_BACKTRACE` that a call to `panic!` causes. +ما می‌توانیم به جای آن، یک مقدار `Result` بازگردانیم که در صورت موفقیت شامل یک نمونه از `Config` باشد و در صورت خطا مشکل را توصیف کند. همچنین قصد داریم نام تابع را از `new` به `build` تغییر دهیم زیرا بسیاری از برنامه‌نویسان انتظار دارند که توابع `new` هرگز شکست نخورند. وقتی `Config::build` با `main` ارتباط برقرار می‌کند، می‌توانیم از نوع `Result` برای اعلام مشکل استفاده کنیم. سپس می‌توانیم `main` را تغییر دهیم تا یک واریانت `Err` را به یک پیام خطای عملی‌تر برای کاربران خود تبدیل کنیم، بدون متن‌های اضافی مربوط به `thread 'main'` و `RUST_BACKTRACE` که یک فراخوانی به `panic!` ایجاد می‌کند. -Listing 12-9 shows the changes we need to make to the return value of the -function we’re now calling `Config::build` and the body of the function needed -to return a `Result`. Note that this won’t compile until we update `main` as -well, which we’ll do in the next listing. +لیست ۱۲-۹ تغییراتی را که باید در مقدار بازگشتی تابع که اکنون آن را `Config::build` می‌نامیم و بدنه تابع برای بازگرداندن یک `Result` ایجاد کنیم، نشان می‌دهد. توجه داشته باشید که این کد تا زمانی که `main` را نیز به‌روزرسانی نکنیم کامپایل نمی‌شود، که این کار را در لیست بعدی انجام خواهیم داد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-09/src/main.rs:here}} @@ -269,33 +176,22 @@ well, which we’ll do in the next listing. -Our `build` function returns a `Result` with a `Config` instance in the success -case and a string literal in the error case. Our error values will always be -string literals that have the `'static` lifetime. +#### تابع `build` و بازگشت مقدار `Result` -We’ve made two changes in the body of the function: instead of calling `panic!` -when the user doesn’t pass enough arguments, we now return an `Err` value, and -we’ve wrapped the `Config` return value in an `Ok`. These changes make the -function conform to its new type signature. +تابع `build` ما اکنون یک مقدار `Result` را بازمی‌گرداند که در صورت موفقیت شامل یک نمونه از `Config` و در صورت خطا یک مقدار رشته‌ای ثابت (`string literal`) است. مقادیر خطای ما همیشه رشته‌های ثابت با طول عمر `'static` خواهند بود. -Returning an `Err` value from `Config::build` allows the `main` function to -handle the `Result` value returned from the `build` function and exit the -process more cleanly in the error case. +ما دو تغییر در بدنه تابع ایجاد کرده‌ایم: به جای فراخوانی `panic!` زمانی که کاربر آرگومان‌های کافی ارائه نمی‌دهد، اکنون یک مقدار `Err` بازمی‌گردانیم و مقدار بازگشتی `Config` را در یک `Ok` قرار داده‌ایم. این تغییرات باعث می‌شوند تابع با امضای نوع جدید خود سازگار باشد. - +بازگرداندن مقدار `Err` از `Config::build` به تابع `main` اجازه می‌دهد که مقدار `Result` بازگشتی از تابع `build` را مدیریت کرده و در صورت بروز خطا، فرآیند را به شکلی تمیزتر خاتمه دهد. -#### Calling `Config::build` and Handling Errors +#### فراخوانی `Config::build` و مدیریت خطاها -To handle the error case and print a user-friendly message, we need to update -`main` to handle the `Result` being returned by `Config::build`, as shown in -Listing 12-10. We’ll also take the responsibility of exiting the command line -tool with a nonzero error code away from `panic!` and instead implement it by -hand. A nonzero exit status is a convention to signal to the process that -called our program that the program exited with an error state. +برای مدیریت حالت خطا و چاپ یک پیام دوستانه برای کاربر، باید تابع `main` را به‌روزرسانی کنیم تا مقدار `Result` بازگردانده‌شده توسط `Config::build` را مدیریت کند. این کار در لیست ۱۲-۱۰ نشان داده شده است. همچنین مسئولیت خاتمه دادن ابزار خط فرمان با کد خطای غیر صفر را از `panic!` گرفته و به صورت دستی پیاده‌سازی خواهیم کرد. کد خروجی غیر صفر به عنوان یک قرارداد برای اعلام وضعیت خطا به فرآیندی که برنامه ما را فراخوانده است، استفاده می‌شود. + + - ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-10/src/main.rs:here}} @@ -303,52 +199,33 @@ called our program that the program exited with an error state. -In this listing, we’ve used a method we haven’t covered in detail yet: -`unwrap_or_else`, which is defined on `Result` by the standard library. -Using `unwrap_or_else` allows us to define some custom, non-`panic!` error -handling. If the `Result` is an `Ok` value, this method’s behavior is similar -to `unwrap`: it returns the inner value that `Ok` is wrapping. However, if the -value is an `Err` value, this method calls the code in the _closure_, which is -an anonymous function we define and pass as an argument to `unwrap_or_else`. -We’ll cover closures in more detail in [Chapter 13][ch13]. For -now, you just need to know that `unwrap_or_else` will pass the inner value of -the `Err`, which in this case is the static string `"not enough arguments"` -that we added in Listing 12-9, to our closure in the argument `err` that -appears between the vertical pipes. The code in the closure can then use the -`err` value when it runs. - -We’ve added a new `use` line to bring `process` from the standard library into -scope. The code in the closure that will be run in the error case is only two -lines: we print the `err` value and then call `process::exit`. The -`process::exit` function will stop the program immediately and return the -number that was passed as the exit status code. This is similar to the -`panic!`-based handling we used in Listing 12-8, but we no longer get all the -extra output. Let’s try it: +در این لیستینگ، ما از متدی استفاده کرده‌ایم که هنوز جزئیات آن را به‌طور کامل پوشش نداده‌ایم: `unwrap_or_else`. این متد که در استاندارد کتابخانه Rust برای `Result` تعریف شده است، به ما امکان می‌دهد مدیریت خطاهای سفارشی و بدون استفاده از `panic!` را تعریف کنیم. اگر مقدار `Result` از نوع `Ok` باشد، رفتار این متد مشابه `unwrap` است: مقدار داخلی که `Ok` در خود قرار داده را بازمی‌گرداند. با این حال، اگر مقدار از نوع `Err` باشد، این متد کدی را که در _closure_ تعریف کرده‌ایم اجرا می‌کند. _Closure_ یک تابع ناشناس است که آن را تعریف کرده و به‌عنوان آرگومان به `unwrap_or_else` ارسال می‌کنیم. + +ما closures را به تفصیل در [فصل ۱۳][ch13] توضیح خواهیم داد. فعلاً کافی است بدانید که `unwrap_or_else` مقدار داخلی `Err` را به _closure_ می‌دهد. در اینجا، مقدار استاتیک `"not enough arguments"` که در لیستینگ 12-9 اضافه کردیم، به _closure_ ارسال شده و به آرگومان `err` تخصیص داده می‌شود، که بین خط عمودی‌ها قرار دارد. کد درون _closure_ سپس می‌تواند از مقدار `err` استفاده کند. + +ما همچنین یک خط جدید `use` اضافه کرده‌ایم تا `process` را از کتابخانه استاندارد به محدوده بیاوریم. کدی که در حالت خطا اجرا می‌شود تنها شامل دو خط است: ابتدا مقدار `err` را چاپ می‌کنیم و سپس `process::exit` را فراخوانی می‌کنیم. تابع `process::exit` بلافاصله برنامه را متوقف کرده و عددی که به‌عنوان کد وضعیت خروج ارسال شده است را بازمی‌گرداند. این روش شبیه مدیریت مبتنی بر `panic!` است که در لیستینگ 12-8 استفاده کردیم، اما دیگر خروجی اضافی تولید نمی‌شود. حالا آن را آزمایش کنیم: ```console {{#include ../listings/ch12-an-io-project/listing-12-10/output.txt}} ``` -Great! This output is much friendlier for our users. - - - - +عالی! این خروجی برای کاربران ما بسیار دوستانه‌تر است. -### Extracting Logic from the `main` Function +### Extracting Logic from `main` Now that we’ve finished refactoring the configuration parsing, let’s turn to the program’s logic. As we stated in [“Separation of Concerns for Binary Projects”](#separation-of-concerns-for-binary-projects), we’ll extract a function named `run` that will hold all the logic currently in the `main` function that isn’t involved with setting up configuration or handling -errors. When we’re done, the `main` function will be concise and easy to verify -by inspection, and we’ll be able to write tests for all the other logic. +errors. When we’re done, `main` will be concise and easy to verify by +inspection, and we’ll be able to write tests for all the other logic. -Listing 12-11 shows the small, incremental improvement of extracting a `run` -function. +Listing 12-11 shows the extracted `run` function. For now, we’re just making +the small, incremental improvement of extracting the function. We’re still +defining the function in _src/main.rs_. -+ ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-11/src/main.rs:here}} @@ -356,21 +233,13 @@ function. -The `run` function now contains all the remaining logic from `main`, starting -from reading the file. The `run` function takes the `Config` instance as an -argument. +با این تغییرات، `main` اکنون تابع `run` را فراخوانی می‌کند و مسئولیت اجرای منطق اصلی برنامه را به آن واگذار می‌کند. این جداسازی باعث می‌شود تابع `main` ساده‌تر شود و ما بتوانیم تست‌های دقیقی برای بخش‌های مختلف کد بنویسیم. این روش به بهبود قابلیت نگهداری و خوانایی کد کمک شایانی می‌کند. -#### Returning Errors from the `run` Function +### بازگرداندن خطاها از تابع `run` -With the remaining program logic separated into the `run` function, we can -improve the error handling, as we did with `Config::build` in Listing 12-9. -Instead of allowing the program to panic by calling `expect`, the `run` -function will return a `Result` when something goes wrong. This will let -us further consolidate the logic around handling errors into `main` in a -user-friendly way. Listing 12-12 shows the changes we need to make to the -signature and body of `run`. +اکنون که منطق باقی‌مانده برنامه را در تابع `run` جدا کرده‌ایم، می‌توانیم مانند `Config::build` در لیستینگ 12-9، مدیریت خطا را بهبود بخشیم. به جای اجازه دادن به برنامه برای اجرای `panic` با فراخوانی `expect`، تابع `run` در صورت بروز مشکل یک `Result` بازمی‌گرداند. این رویکرد به ما امکان می‌دهد منطق مرتبط با مدیریت خطا را به صورت کاربرپسندانه‌ای در تابع `main` متمرکز کنیم. تغییرات لازم برای امضا و بدنه تابع `run` در لیستینگ 12-12 نشان داده شده است: -+ ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-12/src/main.rs:here}} @@ -378,47 +247,38 @@ signature and body of `run`. -We’ve made three significant changes here. First, we changed the return type of -the `run` function to `Result<(), Box>`. This function previously -returned the unit type, `()`, and we keep that as the value returned in the -`Ok` case. - -For the error type, we used the _trait object_ `Box` (and we’ve -brought `std::error::Error` into scope with a `use` statement at the top). -We’ll cover trait objects in [Chapter 18][ch18]. For now, just -know that `Box` means the function will return a type that -implements the `Error` trait, but we don’t have to specify what particular type -the return value will be. This gives us flexibility to return error values that -may be of different types in different error cases. The `dyn` keyword is short -for _dynamic_. - -Second, we’ve removed the call to `expect` in favor of the `?` operator, as we -talked about in [Chapter 9][ch9-question-mark]. Rather than -`panic!` on an error, `?` will return the error value from the current function -for the caller to handle. - -Third, the `run` function now returns an `Ok` value in the success case. -We’ve declared the `run` function’s success type as `()` in the signature, -which means we need to wrap the unit type value in the `Ok` value. This -`Ok(())` syntax might look a bit strange at first, but using `()` like this is -the idiomatic way to indicate that we’re calling `run` for its side effects -only; it doesn’t return a value we need. - -When you run this code, it will compile but will display a warning: +### تغییرات مهم + +
    +
  • + تغییر نوع بازگشتی: + نوع بازگشتی تابع run به Result<(), Box<dyn Error>> تغییر داده شده است. این تابع قبلاً نوع واحد (()) را بازمی‌گرداند، که همچنان برای حالت موفقیت حفظ شده است. + برای نوع خطا از یک شیء صفات به نام Box<dyn Error> استفاده کرده‌ایم (و با استفاده از use، std::error::Error را به محدوده آورده‌ایم). در فصل 18 بیشتر درباره شیء صفات صحبت خواهیم کرد. فعلاً کافی است بدانید که Box<dyn Error> به این معنا است که تابع می‌تواند نوعی از مقدار را که صفت Error را پیاده‌سازی کرده بازگرداند، بدون اینکه نوع خاصی را مشخص کند. کلمه کلیدی dyn به معنای دینامیک است. +
  • +
  • + حذف expect و استفاده از عملگر ?: + به جای استفاده از panic! در صورت بروز خطا، عملگر ? مقدار خطا را از تابع جاری بازمی‌گرداند تا فراخوانی‌کننده بتواند آن را مدیریت کند. +
  • +
  • + بازگرداندن مقدار Ok در حالت موفقیت: + تابع run اکنون در حالت موفقیت مقدار Ok را بازمی‌گرداند. ما نوع موفقیت تابع را به عنوان () در امضا تعریف کرده‌ایم، که به این معنا است که باید مقدار نوع واحد را در مقدار Ok قرار دهیم. نحو Ok(()) ممکن است در ابتدا کمی عجیب به نظر برسد، اما استفاده از () به این صورت روش استاندارد برای نشان دادن این است که تابع run تنها برای تأثیرات جانبی فراخوانی شده و مقداری بازنمی‌گرداند که به آن نیاز داشته باشیم. +
  • +
+``` + +### بررسی کد + +اجرای این کد باعث می‌شود که کد کامپایل شود اما یک هشدار نمایش دهد: ```console {{#include ../listings/ch12-an-io-project/listing-12-12/output.txt}} ``` -Rust tells us that our code ignored the `Result` value and the `Result` value -might indicate that an error occurred. But we’re not checking to see whether or -not there was an error, and the compiler reminds us that we probably meant to -have some error-handling code here! Let’s rectify that problem now. +Rust به ما یادآوری می‌کند که کد ما مقدار `Result` را نادیده گرفته است و این مقدار ممکن است نشان‌دهنده بروز خطا باشد. اما ما بررسی نمی‌کنیم که آیا خطایی رخ داده است یا خیر، و کامپایلر به ما یادآوری می‌کند که احتمالاً نیاز به مدیریت خطا در این بخش داریم. اکنون این مشکل را اصلاح خواهیم کرد. -#### Handling Errors Returned from `run` in `main` +#### مدیریت خطاهای بازگردانده‌شده از `run` در `main` -We’ll check for errors and handle them using a technique similar to one we used -with `Config::build` in Listing 12-10, but with a slight difference: +ما خطاها را بررسی کرده و با استفاده از تکنیکی مشابه آنچه در `Config::build` در لیست ۱۲-۱۰ استفاده کردیم مدیریت می‌کنیم، اما با یک تفاوت کوچک: Filename: src/main.rs @@ -426,15 +286,9 @@ with `Config::build` in Listing 12-10, but with a slight difference: {{#rustdoc_include ../listings/ch12-an-io-project/no-listing-01-handling-errors-in-main/src/main.rs:here}} ``` -We use `if let` rather than `unwrap_or_else` to check whether `run` returns an -`Err` value and to call `process::exit(1)` if it does. The `run` function -doesn’t return a value that we want to `unwrap` in the same way that -`Config::build` returns the `Config` instance. Because `run` returns `()` in -the success case, we only care about detecting an error, so we don’t need -`unwrap_or_else` to return the unwrapped value, which would only be `()`. +ما به جای `unwrap_or_else` از `if let` استفاده می‌کنیم تا بررسی کنیم آیا `run` یک مقدار `Err` بازمی‌گرداند یا خیر و در صورت وقوع، `process::exit(1)` را فراخوانی کنیم. تابع `run` مقداری بازنمی‌گرداند که بخواهیم به همان شیوه‌ای که `Config::build` نمونه `Config` را بازمی‌گرداند آن را `unwrap` کنیم. از آنجایی که `run` در صورت موفقیت مقدار `()` بازمی‌گرداند، ما فقط به شناسایی یک خطا اهمیت می‌دهیم، بنابراین نیازی به `unwrap_or_else` برای بازگرداندن مقدار آن نداریم، که تنها `()` خواهد بود. -The bodies of the `if let` and the `unwrap_or_else` functions are the same in -both cases: we print the error and exit. +بدنه‌های `if let` و `unwrap_or_else` در هر دو حالت یکسان هستند: ما خطا را چاپ کرده و خارج می‌شویم. ### Splitting Code into a Library Crate @@ -442,16 +296,19 @@ Our `minigrep` project is looking good so far! Now we’ll split the _src/main.rs_ file and put some code into the _src/lib.rs_ file. That way, we can test the code and have a _src/main.rs_ file with fewer responsibilities. -Let’s define the code responsible for searching text in _src/lib.rs_ rather -than in _src/main.rs_, which will let us (or anyone else using our -`minigrep` library) call the searching function from more contexts than our -`minigrep` binary. +Let’s move all the code that isn’t in the `main` function from _src/main.rs_ to +_src/lib.rs_: + +- The `run` function definition +- The relevant `use` statements +- The definition of `Config` +- The `Config::build` function definition -First, let’s define the `search` function signature in _src/lib.rs_ as shown in -Listing 12-13, with a body that calls the `unimplemented!` macro. We’ll explain -the signature in more detail when we fill in the implementation. +The contents of _src/lib.rs_ should have the signatures shown in Listing 12-13 +(we’ve omitted the bodies of the functions for brevity). Note that this won’t +compile until we modify _src/main.rs_ in Listing 12-14. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-13/src/lib.rs}} @@ -459,14 +316,14 @@ the signature in more detail when we fill in the implementation. -We’ve used the `pub` keyword on the function definition to designate `search` -as part of our library crate’s public API. We now have a library crate that we -can use from our binary crate and that we can test! +We’ve made liberal use of the `pub` keyword: on `Config`, on its fields and its +`build` method, and on the `run` function. We now have a library crate that has +a public API we can test! -Now we need to bring the code defined in _src/lib.rs_ into the scope of the -binary crate in _src/main.rs_ and call it, as shown in Listing 12-14. +Now we need to bring the code we moved to _src/lib.rs_ into the scope of the +binary crate in _src/main.rs_, as shown in Listing 12-14. -+ ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-14/src/main.rs:here}} @@ -474,32 +331,18 @@ binary crate in _src/main.rs_ and call it, as shown in Listing 12-14. -We add a `use minigrep::search` line to bring the `search` function from -the library crate into the binary crate’s scope. Then, in the `run` function, -rather than printing out the contents of the file, we call the `search` -function and pass the `config.query` value and `contents` as arguments. Then -`run` will use a `for` loop to print each line returned from `search` that -matched the query. This is also a good time to remove the `println!` calls in -the `main` function that displayed the query and the file path so that our -program only prints the search results (if no errors occur). - -Note that the search function will be collecting all the results into a vector -it returns before any printing happens. This implementation could be slow to -display results when searching large files because results aren’t printed as -they’re found; we’ll discuss a possible way to fix this using iterators in -Chapter 13. - -Whew! That was a lot of work, but we’ve set ourselves up for success in the -future. Now it’s much easier to handle errors, and we’ve made the code more -modular. Almost all of our work will be done in _src/lib.rs_ from here on out. - -Let’s take advantage of this newfound modularity by doing something that would -have been difficult with the old code but is easy with the new code: we’ll -write some tests! - -[ch13]: ch13-00-functional-features.html -[ch9-custom-types]: ch09-03-to-panic-or-not-to-panic.html#creating-custom-types-for-validation -[ch9-error-guidelines]: ch09-03-to-panic-or-not-to-panic.html#guidelines-for-error-handling -[ch9-result]: ch09-02-recoverable-errors-with-result.html -[ch18]: ch18-00-oop.html -[ch9-question-mark]: ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator +We add a `use minigrep::Config` line to bring the `Config` type from the +library crate into the binary crate’s scope, and we prefix the `run` function +with our crate name. Now all the functionality should be connected and should +work. Run the program with `cargo run` and make sure everything works correctly. + +وای! این یک کار سخت بود، اما ما خودمان را برای موفقیت در آینده آماده کردیم. اکنون مدیریت خطاها بسیار آسان‌تر شده است و کد ما ماژولارتر شده است. از اینجا به بعد تقریباً تمام کارهای ما در فایل _src/lib.rs_ انجام خواهد شد. + +بیایید از این ماژولاریت جدید برای انجام کاری استفاده کنیم که با کد قبلی دشوار بود اما با کد جدید آسان است: نوشتن چند تست! + +[ch13]: ch13-00-functional-features.html +[ch9-custom-types]: ch09-03-to-panic-or-not-to-panic.html#creating-custom-types-for-validation +[ch9-error-guidelines]: ch09-03-to-panic-or-not-to-panic.html#guidelines-for-error-handling +[ch9-result]: ch09-02-recoverable-errors-with-result.html +[ch18]: ch18-00-oop.html +[ch9-question-mark]: ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator diff --git a/src/ch12-04-testing-the-librarys-functionality.md b/src/ch12-04-testing-the-librarys-functionality.md index 9a5d31c08d..a59aeb77fc 100644 --- a/src/ch12-04-testing-the-librarys-functionality.md +++ b/src/ch12-04-testing-the-librarys-functionality.md @@ -1,38 +1,28 @@ -## Developing the Library’s Functionality with Test-Driven Development +## توسعه قابلیت‌های کتابخانه با توسعه آزمون‌محور (TDD) یا همان (Test-Driven Development) -Now that we have the search logic in _src/lib.rs_ separate from the `main` -function, it’s much easier to write tests for the core functionality of our -code. We can call functions directly with various arguments and check return -values without having to call our binary from the command line. +اکنون که منطق جست‌وجو را در فایل *src/lib.rs* و جدا از تابع `main` داریم، نوشتن تست برای عملکرد اصلی کد بسیار آسان‌تر شده است. +می‌توانیم توابع را مستقیماً با آرگومان‌های مختلف فراخوانی کنیم و مقادیر بازگشتی را بررسی کنیم، بدون آن‌که نیاز باشد باینری خود را از طریق خط فرمان اجرا کنیم. -In this section, we’ll add the searching logic to the `minigrep` program using -the test-driven development (TDD) process with the following steps: +در این بخش، منطق جستجو را با استفاده از فرآیند توسعه آزمون‌محور (TDD) به برنامه `minigrep` اضافه خواهیم کرد. مراحل این فرآیند به شرح زیر است: -1. Write a test that fails and run it to make sure it fails for the reason you - expect. -2. Write or modify just enough code to make the new test pass. -3. Refactor the code you just added or changed and make sure the tests continue - to pass. -4. Repeat from step 1! +
    +
  1. نوشتن یک تست که شکست می‌خورد و اجرای آن برای اطمینان از اینکه به دلیلی که انتظار داشتید شکست می‌خورد.
  2. +
  3. نوشتن یا تغییر کد به اندازه‌ای که تست جدید پاس شود.
  4. +
  5. بازسازی کدی که به تازگی اضافه یا تغییر داده شده و اطمینان از اینکه تست‌ها همچنان پاس می‌شوند.
  6. +
  7. تکرار از مرحله ۱!
  8. +
-Though it’s just one of many ways to write software, TDD can help drive code -design. Writing the test before you write the code that makes the test pass -helps to maintain high test coverage throughout the process. +TDD تنها یکی از روش‌های نوشتن نرم‌افزار است، اما می‌تواند به طراحی بهتر کد کمک کند. نوشتن تست قبل از نوشتن کدی که تست را پاس می‌کند، کمک می‌کند تا پوشش تست بالا در طول فرآیند حفظ شود. -We’ll test-drive the implementation of the functionality that will actually do -the searching for the query string in the file contents and produce a list of -lines that match the query. We’ll add this functionality in a function called -`search`. +ما با استفاده از TDD پیاده‌سازی قابلیت جستجوی رشته کوئری در محتوای فایل و تولید لیستی از خطوط مطابق با کوئری را توسعه خواهیم داد. این قابلیت را در تابعی به نام `search` اضافه خواهیم کرد. -### Writing a Failing Test +### نوشتن یک تست که شکست می‌خورد -In _src/lib.rs_, we’ll add a `tests` module with a test function, as we did in -[Chapter 11][ch11-anatomy]. The test function specifies the -behavior we want the `search` function to have: it will take a query and the -text to search, and it will return only the lines from the text that contain -the query. Listing 12-15 shows this test. +در فایل *src/lib.rs*، یک ماژول `tests` با یک تابع تست اضافه می‌کنیم، همان‌طور که در \[فصل ۱۱]\[ch11-anatomy] انجام دادیم. +تابع تست، رفتاری را که از تابع `search` انتظار داریم مشخص می‌کند: این تابع یک `query` و متنی برای جست‌وجو دریافت می‌کند، و تنها خطوطی از متن را که شامل `query` هستند بازمی‌گرداند. +لیست ۱۲-۱۵ این تست را نشان می‌دهد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-15/src/lib.rs:here}} @@ -40,84 +30,57 @@ the query. Listing 12-15 shows this test. -This test searches for the string `"duct"`. The text we’re searching is three -lines, only one of which contains `"duct"` (note that the backslash after the -opening double quote tells Rust not to put a newline character at the beginning -of the contents of this string literal). We assert that the value returned from -the `search` function contains only the line we expect. +این تست به دنبال رشته `"duct"` می‌گردد. متنی که در آن جستجو می‌کنیم شامل سه خط است که تنها یکی از آن‌ها شامل `"duct"` است (توجه داشته باشید که بک‌اسلش بعد از علامت نقل قول بازکننده به Rust می‌گوید که کاراکتر newline در ابتدای محتویات این literal رشته قرار ندهد). ما تأیید می‌کنیم که مقدار بازگردانده شده از تابع `search` تنها شامل خطی است که انتظار داریم. -If we run this test, it will currently fail because the `unimplemented!` macro -panics with the message “not implemented”. In accordance with TDD principles, -we’ll take a small step of adding just enough code to get the test to not panic -when calling the function by defining the `search` function to always return an -empty vector, as shown in Listing 12-16. Then the test should compile and fail -because an empty vector doesn’t match a vector containing the line `"safe, -fast, productive."` +هنوز قادر به اجرای این تست و مشاهده شکست آن نیستیم زیرا تست حتی کامپایل نمی‌شود: تابع `search` هنوز وجود ندارد! بر اساس اصول TDD، ما تنها به اندازه‌ای کد اضافه می‌کنیم که تست کامپایل و اجرا شود، با اضافه کردن یک تعریف از تابع `search` که همیشه یک بردار خالی بازمی‌گرداند، همانطور که در لیست ۱۲-۱۶ نشان داده شده است. سپس تست باید کامپایل و شکست بخورد زیرا یک بردار خالی با یک بردار شامل خط `"safe, fast, productive."` مطابقت ندارد. -+ -```rust,noplayground +```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-16/src/lib.rs:here}} ``` -Now let’s discuss why we need to define an explicit lifetime `'a` in the -signature of `search` and use that lifetime with the `contents` argument and -the return value. Recall in [Chapter 10][ch10-lifetimes] that -the lifetime parameters specify which argument lifetime is connected to the -lifetime of the return value. In this case, we indicate that the returned -vector should contain string slices that reference slices of the argument -`contents` (rather than the argument `query`). +حال بیایید بررسی کنیم که چرا نیاز داریم یک lifetime صریح با نام `'a` در امضای تابع `search` تعریف کنیم و این lifetime را با آرگومان `contents` و مقدار بازگشتی استفاده کنیم. +به یاد بیاورید که در [فصل ۱۰][ch10-lifetimes]، پارامترهای lifetime مشخص می‌کردند که lifetime کدام آرگومان با lifetime مقدار بازگشتی مرتبط است. +در این‌جا، مشخص می‌کنیم که بردار بازگشتی باید شامل برش‌هایی از رشته باشد که به بخش‌هایی از آرگومان `contents` رفرنس می‌دهند (نه آرگومان `query`). + +متوجه می‌شوید که ما نیاز داریم یک طول عمر صریح `'a` در امضای تابع `search` تعریف کنیم و از آن طول عمر با آرگومان `contents` و مقدار بازگشتی استفاده کنیم. به یاد داشته باشید که در [فصل ۱۰][ch10-lifetimes] توضیح دادیم که پارامترهای طول عمر مشخص می‌کنند کدام طول عمر آرگومان به طول عمر مقدار بازگشتی متصل است. در این مورد، ما مشخص می‌کنیم که بردار بازگشتی باید شامل برش‌های رشته‌ای باشد که به برش‌های آرگومان `contents` اشاره دارند (نه آرگومان `query`). -In other words, we tell Rust that the data returned by the `search` function -will live as long as the data passed into the `search` function in the -`contents` argument. This is important! The data referenced _by_ a slice needs -to be valid for the reference to be valid; if the compiler assumes we’re making -string slices of `query` rather than `contents`, it will do its safety checking -incorrectly. +به عبارت دیگر، به Rust می‌گوییم داده‌ای که توسط تابع `search` بازگردانده می‌شود به اندازه داده‌ای که به تابع `search` در آرگومان `contents` منتقل می‌شود زنده خواهد بود. این مهم است! داده‌ای که توسط یک برش مرجع داده می‌شود باید معتبر باشد تا مرجع نیز معتبر باشد؛ اگر کامپایلر فرض کند که ما در حال ساختن برش‌های رشته‌ای از `query` هستیم به جای `contents`، بررسی‌های ایمنی را به اشتباه انجام خواهد داد. -If we forget the lifetime annotations and try to compile this function, we’ll -get this error: +اگر طول عمرها را فراموش کنیم و سعی کنیم این تابع را کامپایل کنیم، این خطا را دریافت خواهیم کرد: ```console {{#include ../listings/ch12-an-io-project/output-only-02-missing-lifetimes/output.txt}} ``` -Rust can’t know which of the two parameters we need for the output, so we need -to tell it explicitly. Note that the help text suggests specifying the same -lifetime parameter for all the parameters and the output type, which is -incorrect! Because `contents` is the parameter that contains all of our text -and we want to return the parts of that text that match, we know `contents` is -the only parameter that should be connected to the return value using the -lifetime syntax. +کامپایلر Rust نمی‌تواند به‌صورت خودکار تشخیص دهد که کدام‌یک از دو پارامتر باید به مقدار بازگشتی مرتبط باشد، بنابراین باید این موضوع را به‌صراحت به آن اعلام کنیم. +توجه داشته باشید که متن راهنمای خطا پیشنهاد می‌دهد که برای همه پارامترها و نوع بازگشتی، از یک پارامتر lifetime مشترک استفاده شود، که این پیشنهاد نادرست است! +از آن‌جا که `contents` پارامتری است که تمام متن ما را در بر دارد و ما قصد داریم بخش‌هایی از آن متن را که با جستجو مطابقت دارند بازگردانیم، می‌دانیم که فقط `contents` باید با مقدار بازگشتی از طریق نگارش lifetime مرتبط شود. -Other programming languages don’t require you to connect arguments to return -values in the signature, but this practice will get easier over time. You might -want to compare this example with the examples in the [“Validating References -with Lifetimes”][validating-references-with-lifetimes] section -in Chapter 10. +دیگر زبان‌های برنامه‌نویسی نیازی ندارند آرگومان‌ها را به مقادیر بازگشتی در امضا متصل کنید، اما این تمرین با گذشت زمان آسان‌تر می‌شود. ممکن است بخواهید این مثال را با مثال‌های موجود در بخش [“اعتبارسنجی مراجع با طول عمر”][validating-references-with-lifetimes] از فصل ۱۰ مقایسه کنید. -### Writing Code to Pass the Test +### نوشتن کدی برای پاس کردن تست -Currently, our test is failing because we always return an empty vector. To fix -that and implement `search`, our program needs to follow these steps: +در حال حاضر، تست ما به دلیل اینکه همیشه یک بردار خالی بازمی‌گرداند، شکست می‌خورد. برای رفع این مشکل و پیاده‌سازی `search`، برنامه ما باید این مراحل را دنبال کند: -1. Iterate through each line of the contents. -2. Check whether the line contains our query string. -3. If it does, add it to the list of values we’re returning. -4. If it doesn’t, do nothing. -5. Return the list of results that match. +
    +
  1. تکرار از طریق هر خط از محتوای فایل.
  2. +
  3. بررسی اینکه آیا خط شامل رشته کوئری ما هست یا نه.
  4. +
  5. اگر خط شامل کوئری بود، آن را به لیست مقادیر بازگشتی اضافه کنیم.
  6. +
  7. اگر نبود، کاری انجام ندهیم.
  8. +
  9. لیست نتایجی که مطابقت دارند را بازگردانیم.
  10. +
-Let’s work through each step, starting with iterating through lines. +بیایید هر مرحله را یکی‌یکی اجرا کنیم، با تکرار از طریق خطوط شروع می‌کنیم. -#### Iterating Through Lines with the `lines` Method +#### تکرار از طریق خطوط با متد `lines` -Rust has a helpful method to handle line-by-line iteration of strings, -conveniently named `lines`, that works as shown in Listing 12-17. Note that -this won’t compile yet. +Rust یک متد مفید برای مدیریت تکرار خط به خط در رشته‌ها ارائه می‌دهد که به طور مناسبی `lines` نامیده شده است و همانطور که در لیست ۱۲-۱۷ نشان داده شده کار می‌کند. توجه داشته باشید که این کد هنوز کامپایل نخواهد شد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-17/src/lib.rs:here}} @@ -125,19 +88,13 @@ this won’t compile yet. -The `lines` method returns an iterator. We’ll talk about iterators in depth in -[Chapter 13][ch13-iterators], but recall that you saw this way -of using an iterator in [Listing 3-5][ch3-iter], where we used a -`for` loop with an iterator to run some code on each item in a collection. +متد `lines` یک iterator برمی‌گرداند. ما در [فصل ۱۳][ch13-iterators] عمیقاً در مورد iteratorها صحبت خواهیم کرد، اما به یاد داشته باشید که قبلاً این روش استفاده از یک iterator را در [لیست ۳-۵][ch3-iter] دیدید، جایی که از یک حلقه `for` با یک iterator برای اجرای کدی روی هر آیتم در یک مجموعه استفاده کردیم. -#### Searching Each Line for the Query +#### جستجو در هر خط برای کوئری -Next, we’ll check whether the current line contains our query string. -Fortunately, strings have a helpful method named `contains` that does this for -us! Add a call to the `contains` method in the `search` function, as shown in -Listing 12-18. Note that this still won’t compile yet. +اکنون، بررسی خواهیم کرد که آیا خط فعلی شامل رشته کوئری ما هست یا نه. خوشبختانه، رشته‌ها یک متد مفید به نام `contains` دارند که این کار را برای ما انجام می‌دهد! یک فراخوانی به متد `contains` را در تابع `search` اضافه کنید، همانطور که در لیست ۱۲-۱۸ نشان داده شده است. توجه داشته باشید که این کد همچنان کامپایل نخواهد شد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-18/src/lib.rs:here}} @@ -145,18 +102,13 @@ Listing 12-18. Note that this still won’t compile yet. -At the moment, we’re building up functionality. To get the code to compile, we -need to return a value from the body as we indicated we would in the function -signature. +در حال حاضر، ما در حال ایجاد قابلیت‌های بیشتر هستیم. برای اینکه کد کامپایل شود، نیاز داریم مقداری را از بدنه تابع بازگردانیم همانطور که در امضای تابع اشاره کردیم. -#### Storing Matching Lines +#### ذخیره خطوط مطابق -To finish this function, we need a way to store the matching lines that we want -to return. For that, we can make a mutable vector before the `for` loop and -call the `push` method to store a `line` in the vector. After the `for` loop, -we return the vector, as shown in Listing 12-19. +برای تکمیل این تابع، نیاز داریم روشی برای ذخیره خطوط مطابق که می‌خواهیم بازگردانیم داشته باشیم. برای این کار، می‌توانیم یک بردار mutable قبل از حلقه `for` ایجاد کنیم و با استفاده از متد `push` یک خط را در بردار ذخیره کنیم. بعد از حلقه `for`، بردار را بازمی‌گردانیم، همانطور که در لیست ۱۲-۱۹ نشان داده شده است. -+ ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-19/src/lib.rs:here}} @@ -164,49 +116,49 @@ we return the vector, as shown in Listing 12-19. -Now the `search` function should return only the lines that contain `query`, -and our test should pass. Let’s run the test: +اکنون تابع `search` باید فقط خطوطی را که شامل `query` هستند بازگرداند، و تست ما باید پاس شود. بیایید تست را اجرا کنیم: ```console {{#include ../listings/ch12-an-io-project/listing-12-19/output.txt}} ``` -Our test passed, so we know it works! +تست ما پاس شد، بنابراین می‌دانیم که کار می‌کند! + +در این مرحله، می‌توانیم فرصت‌هایی برای بازسازی پیاده‌سازی تابع جستجو در نظر بگیریم و در عین حال تست‌ها را پاس نگه داریم تا همان قابلیت را حفظ کنیم. کد در تابع جستجو چندان بد نیست، اما از برخی ویژگی‌های مفید iteratorها استفاده نمی‌کند. ما در [فصل ۱۳][ch13-iterators] به این مثال بازخواهیم گشت، جایی که iteratorها را با جزئیات بررسی می‌کنیم و به نحوه بهبود آن می‌پردازیم. + +#### استفاده از تابع `search` در تابع `run` + +اکنون که تابع `search` کار می‌کند و تست شده است، باید تابع `search` را از تابع `run` فراخوانی کنیم. ما باید مقدار `config.query` و `contents` که `run` از فایل می‌خواند را به تابع `search` بدهیم. سپس `run` هر خطی که از `search` برگردانده شده را چاپ خواهد کرد: + +Filename: src/lib.rs + +```rust,ignore +{{#rustdoc_include ../listings/ch12-an-io-project/no-listing-02-using-search-in-run/src/lib.rs:here}} +``` -At this point, we could consider opportunities for refactoring the -implementation of the search function while keeping the tests passing to -maintain the same functionality. The code in the search function isn’t too bad, -but it doesn’t take advantage of some useful features of iterators. We’ll -return to this example in [Chapter 13][ch13-iterators], where -we’ll explore iterators in detail, and look at how to improve it. +ما هنوز از یک حلقه `for` برای بازگرداندن هر خط از `search` و چاپ آن استفاده می‌کنیم. -Now the entire program should work! Let’s try it out, first with a word that -should return exactly one line from the Emily Dickinson poem: _frog_. +اکنون باید کل برنامه به‌درستی کار کند! بیایید آن را امتحان کنیم، ابتدا با واژه‌ای مانند *frog* که باید دقیقاً یک خط از شعر امیلی دیکینسون را بازگرداند. ```console {{#include ../listings/ch12-an-io-project/no-listing-02-using-search-in-run/output.txt}} ``` -Cool! Now let’s try a word that will match multiple lines, like _body_: +عالی! حالا بیایید کلمه‌ای را امتحان کنیم که چندین خط را مطابقت دهد، مثل _body_: ```console {{#include ../listings/ch12-an-io-project/output-only-03-multiple-matches/output.txt}} ``` -And finally, let’s make sure that we don’t get any lines when we search for a -word that isn’t anywhere in the poem, such as _monomorphization_: +و در نهایت، مطمئن شویم که وقتی کلمه‌ای را جستجو می‌کنیم که در هیچ جای شعر وجود ندارد، مثل _monomorphization_، هیچ خطی دریافت نخواهیم کرد: ```console {{#include ../listings/ch12-an-io-project/output-only-04-no-matches/output.txt}} ``` -Excellent! We’ve built our own mini version of a classic tool and learned a lot -about how to structure applications. We’ve also learned a bit about file input -and output, lifetimes, testing, and command line parsing. +عالی! ما نسخه کوچکی از یک ابزار کلاسیک ساختیم و چیزهای زیادی درباره نحوه ساختاردهی برنامه‌ها آموختیم. همچنین کمی درباره ورودی و خروجی فایل، طول عمر‌ها، تست کردن و تجزیه دستورات خط فرمان یاد گرفتیم. -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. +برای تکمیل این پروژه، به طور مختصر نشان خواهیم داد که چگونه با متغیرهای محیطی کار کنیم و چگونه به خطای استاندارد (standard error) چاپ کنیم، که هر دو در هنگام نوشتن برنامه‌های خط فرمان مفید هستند. [validating-references-with-lifetimes]: ch10-03-lifetime-syntax.html#validating-references-with-lifetimes [ch11-anatomy]: ch11-01-writing-tests.html#the-anatomy-of-a-test-function diff --git a/src/ch12-05-working-with-environment-variables.md b/src/ch12-05-working-with-environment-variables.md index cd90c789b8..df706e5628 100644 --- a/src/ch12-05-working-with-environment-variables.md +++ b/src/ch12-05-working-with-environment-variables.md @@ -1,22 +1,12 @@ -## Working with Environment Variables +## کار با متغیرهای محیطی -We’ll improve the `minigrep` binary by adding an extra feature: an option for -case-insensitive searching that the user can turn on via an environment -variable. We could make this feature a command line option and require that -users enter it each time they want it to apply, but by instead making it an -environment variable, we allow our users to set the environment variable once -and have all their searches be case insensitive in that terminal session. +ما باینری `minigrep` را با افزودن یک ویژگی اضافی بهبود می‌دهیم: گزینه‌ای برای جستجوی حساس‌نبودن به حروف کوچک و بزرگ که کاربر می‌تواند از طریق یک متغیر محیطی آن را فعال کند. می‌توانستیم این ویژگی را به‌صورت گزینه‌ای در خط فرمان پیاده‌سازی کنیم و از کاربران بخواهیم که هر بار آن را وارد کنند، اما با استفاده از یک متغیر محیطی، به کاربران اجازه می‌دهیم که فقط یک‌بار این متغیر را تنظیم کنند و در کل آن نشست ترمینال، تمام جستجوهای آن‌ها به‌صورت حساس‌نبودن به حروف انجام شود. -### Writing a Failing Test for the Case-Insensitive `search` Function +### نوشتن یک تست شکست‌خورده برای تابع `search_case_insensitive` -We first add a new `search_case_insensitive` function to the `minigrep` library -that will be called when the environment variable has a value. We’ll continue -to follow the TDD process, so the first step is again to write a failing test. -We’ll add a new test for the new `search_case_insensitive` function and rename -our old test from `one_result` to `case_sensitive` to clarify the differences -between the two tests, as shown in Listing 12-20. +ابتدا تابع جدیدی به نام `search_case_insensitive` به کتابخانه `minigrep` اضافه می‌کنیم که زمانی فراخوانی می‌شود که متغیر محیطی مقدار داشته باشد. ما همچنان از فرایند توسعه آزمون‌محور (TDD) پیروی می‌کنیم، بنابراین گام اول، نوشتن یک تست شکست‌خورده است. یک تست جدید برای تابع `search_case_insensitive` اضافه می‌کنیم و نام تست قبلی‌مان را از `one_result` به `case_sensitive` تغییر می‌دهیم تا تفاوت بین این دو تست واضح‌تر شود، همان‌طور که در لیست 12-20 نشان داده شده است. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-20/src/lib.rs:here}} @@ -24,30 +14,29 @@ between the two tests, as shown in Listing 12-20. -Note that we’ve edited the old test’s `contents` too. We’ve added a new line -with the text `"Duct tape."` using a capital _D_ that shouldn’t match the query -`"duct"` when we’re searching in a case-sensitive manner. Changing the old test -in this way helps ensure that we don’t accidentally break the case-sensitive -search functionality that we’ve already implemented. This test should pass now -and should continue to pass as we work on the case-insensitive search. +توجه کنید که ما متن تست قدیمی را نیز ویرایش کرده‌ایم. ما یک خط جدید با متن `"Duct tape."` با +حرف بزرگ _D_ اضافه کرده‌ایم که نباید با عبارت جستجو `"duct"` در حالت حساس به حروف کوچک و +بزرگ مطابقت داشته باشد. تغییر دادن تست قدیمی به این صورت کمک می‌کند که مطمئن شویم عملکرد +جستجوی حساس به حروف کوچک و بزرگ که قبلاً پیاده‌سازی کرده‌ایم به طور تصادفی شکسته نمی‌شود. +این تست باید اکنون عبور کند و همچنان باید عبور کند در حالی که ما روی جستجوی غیرحساس به حروف +کار می‌کنیم. -The new test for the case-_insensitive_ search uses `"rUsT"` as its query. In -the `search_case_insensitive` function we’re about to add, the query `"rUsT"` -should match the line containing `"Rust:"` with a capital _R_ and match the -line `"Trust me."` even though both have different casing from the query. This -is our failing test, and it will fail to compile because we haven’t yet defined -the `search_case_insensitive` function. Feel free to add a skeleton -implementation that always returns an empty vector, similar to the way we did -for the `search` function in Listing 12-16 to see the test compile and fail. +تست جدید برای جستجوی غیرحساس به حروف کوچک و بزرگ از `"rUsT"` به عنوان عبارت جستجو استفاده +می‌کند. در تابع `search_case_insensitive` که قصد داریم اضافه کنیم، عبارت جستجوی `"rUsT"` +باید با خط حاوی `"Rust:"` با حرف بزرگ _R_ و خط `"Trust me."` مطابقت داشته باشد، حتی اگر هر +دو حالت متفاوتی نسبت به عبارت جستجو داشته باشند. این تست شکست‌خورده ما است و به دلیل اینکه +هنوز تابع `search_case_insensitive` تعریف نشده است، کامپایل نخواهد شد. می‌توانید یک +پیاده‌سازی موقتی که همیشه یک وکتور خالی برمی‌گرداند اضافه کنید، مشابه کاری که برای تابع +`search` در لیستینگ 12-16 انجام دادیم تا تست کامپایل شده و شکست بخورد. -### Implementing the `search_case_insensitive` Function +### پیاده‌سازی تابع `search_case_insensitive` -The `search_case_insensitive` function, shown in Listing 12-21, will be almost -the same as the `search` function. The only difference is that we’ll lowercase -the `query` and each `line` so that whatever the case of the input arguments, -they’ll be the same case when we check whether the line contains the query. +تابع `search_case_insensitive` که در لیستینگ 12-21 نشان داده شده است، تقریباً مشابه تابع +`search` خواهد بود. تنها تفاوت این است که ما عبارت جستجو و هر خط را کوچک‌حرف می‌کنیم تا +صرف‌نظر از مورد ورودی‌ها، هنگام بررسی اینکه آیا خط شامل عبارت جستجو است، هر دو به یک مورد +تبدیل شوند. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-21/src/lib.rs:here}} @@ -55,38 +44,29 @@ they’ll be the same case when we check whether the line contains the query. -First we lowercase the `query` string and store it in a new variable with the -same name, shadowing the original `query`. Calling `to_lowercase` on the query -is necessary so that no matter whether the user’s query is `"rust"`, `"RUST"`, -`"Rust"`, or `"``rUsT``"`, we’ll treat the query as if it were `"rust"` and be -insensitive to the case. While `to_lowercase` will handle basic Unicode, it -won’t be 100 percent accurate. If we were writing a real application, we’d want -to do a bit more work here, but this section is about environment variables, -not Unicode, so we’ll leave it at that here. - -Note that `query` is now a `String` rather than a string slice because calling -`to_lowercase` creates new data rather than referencing existing data. Say the -query is `"rUsT"`, as an example: that string slice doesn’t contain a lowercase -`u` or `t` for us to use, so we have to allocate a new `String` containing -`"rust"`. When we pass `query` as an argument to the `contains` method now, we -need to add an ampersand because the signature of `contains` is defined to take -a string slice. - -Next, we add a call to `to_lowercase` on each `line` to lowercase all -characters. Now that we’ve converted `line` and `query` to lowercase, we’ll -find matches no matter what the case of the query is. - -Let’s see if this implementation passes the tests: +ابتدا رشته `query` را به حروف کوچک تبدیل می‌کنیم و آن را در متغیر جدیدی با همان نام ذخیره می‌کنیم و مقدار اصلی `query` را شَدو (shadow) می‌کنیم. فراخوانی `to_lowercase` بر روی `query` ضروری است تا صرف‌نظر از این‌که کاربر عبارت مورد جستجوی خود را به صورت `"rust"`، `"RUST"`، `"Rust"` یا `"rUsT"` وارد کند، ما با آن گویی که `"rust"` وارد شده است برخورد کنیم و نسبت به حروف کوچک و بزرگ حساس نباشیم. هرچند `to_lowercase` نگاشت پایه‌ی Unicode را انجام می‌دهد، اما صد درصد دقیق نخواهد بود. اگر قصد نوشتن یک برنامه واقعی را داشتیم، نیاز به کار بیشتری در این بخش بود، اما از آن‌جا که این بخش در مورد متغیرهای محیطی است، نه Unicode، در همین حد باقی می‌مانیم. + +توجه کنید که اکنون `query` یک رشته (`String`) به جای برش رشته (`string slice`) است، زیرا +فراخوانی `to_lowercase` داده‌های جدید ایجاد می‌کند به جای اینکه به داده‌های موجود اشاره کند. +به عنوان مثال، بگویید عبارت جستجو `"rUsT"` است: آن رشته شامل یک `u` یا `t` کوچک نیست که بتوانیم +استفاده کنیم، بنابراین باید یک `String` جدید شامل `"rust"` تخصیص دهیم. وقتی اکنون `query` را +به عنوان یک آرگومان به متد `contains` منتقل می‌کنیم، نیاز داریم که یک علامت `&` اضافه کنیم +چون امضای `contains` به گونه‌ای تعریف شده است که یک برش رشته دریافت می‌کند. + +بعداً یک فراخوانی به `to_lowercase` بر روی هر `line` اضافه می‌کنیم تا همه کاراکترها کوچک‌حرف +شوند. اکنون که `line` و `query` را به کوچک‌حرف تبدیل کرده‌ایم، مطمئن می‌شویم که مطابقت‌ها +صرف‌نظر از مورد عبارت جستجو پیدا شوند. + +بیایید ببینیم آیا این پیاده‌سازی تست‌ها را پاس می‌کند یا خیر: ```console {{#include ../listings/ch12-an-io-project/listing-12-21/output.txt}} ``` -Great! They passed. Now, let’s call the new `search_case_insensitive` function -from the `run` function. First we’ll add a configuration option to the `Config` -struct to switch between case-sensitive and case-insensitive search. Adding -this field will cause compiler errors because we aren’t initializing this field -anywhere yet: +عالی! تست‌ها پاس شدند. حالا بیایید تابع جدید `search_case_insensitive` را از تابع `run` +فراخوانی کنیم. ابتدا یک گزینه پیکربندی به ساختار `Config` اضافه می‌کنیم تا بین جستجوی حساس +به حروف کوچک و بزرگ و غیرحساس به حروف کوچک و بزرگ سوئیچ کنیم. افزودن این فیلد باعث ایجاد +خطاهای کامپایل می‌شود زیرا هنوز این فیلد را در هیچ جا مقداردهی نکرده‌ایم: Filename: src/main.rs @@ -107,13 +87,9 @@ function, as shown in Listing 12-22. This still won’t compile yet. -Finally, we need to check for the environment variable. The functions for -working with environment variables are in the `env` module in the standard -library, which is already in scope at the top of _src/main.rs_. We’ll use the -`var` function from the `env` module to check to see if any value has been set -for an environment variable named `IGNORE_CASE`, as shown in Listing 12-23. +در نهایت، باید بررسی کنیم که آیا متغیر محیطی تنظیم شده است یا خیر. توابع مربوط به کار با متغیرهای محیطی در ماژول `env` از کتابخانه استاندارد قرار دارند که در بالای فایل *src/main.rs* از قبل در scope قرار دارد. برای بررسی اینکه آیا متغیر محیطی‌ای به نام `IGNORE_CASE` مقداری دارد یا نه، از تابع `var` در ماژول `env` استفاده می‌کنیم، همان‌طور که در لیست 12-23 نشان داده شده است. -+ ```rust,ignore,noplayground {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-23/src/main.rs:here}} @@ -121,61 +97,54 @@ for an environment variable named `IGNORE_CASE`, as shown in Listing 12-23. -Here, we create a new variable, `ignore_case`. To set its value, we call the -`env::var` function and pass it the name of the `IGNORE_CASE` environment -variable. The `env::var` function returns a `Result` that will be the -successful `Ok` variant that contains the value of the environment variable if -the environment variable is set to any value. It will return the `Err` variant -if the environment variable is not set. - -We’re using the `is_ok` method on the `Result` to check whether the environment -variable is set, which means the program should do a case-insensitive search. -If the `IGNORE_CASE` environment variable isn’t set to anything, `is_ok` will -return `false` and the program will perform a case-sensitive search. We don’t -care about the _value_ of the environment variable, just whether it’s set or -unset, so we’re checking `is_ok` rather than using `unwrap`, `expect`, or any -of the other methods we’ve seen on `Result`. - -We pass the value in the `ignore_case` variable to the `Config` instance so the -`run` function can read that value and decide whether to call -`search_case_insensitive` or `search`, as we implemented in Listing 12-22. - -Let’s give it a try! First we’ll run our program without the environment -variable set and with the query `to`, which should match any line that contains -the word _to_ in all lowercase: +اینجا یک متغیر جدید به نام `ignore_case` ایجاد می‌کنیم. برای مقداردهی آن، تابع `env::var` را +فراخوانی کرده و نام متغیر محیطی `IGNORE_CASE` را به آن می‌دهیم. تابع `env::var` یک `Result` +برمی‌گرداند که در صورت تنظیم بودن متغیر محیطی به هر مقداری، مقدار `Ok` با مقدار متغیر محیطی را +دارد. اگر متغیر محیطی تنظیم نشده باشد، مقدار `Err` برگردانده می‌شود. + +ما از متد `is_ok` روی `Result` استفاده می‌کنیم تا بررسی کنیم که آیا متغیر محیطی تنظیم شده است، +که نشان می‌دهد برنامه باید جستجو را به صورت غیرحساس به حروف کوچک و بزرگ انجام دهد. اگر متغیر +محیطی `IGNORE_CASE` به هیچ مقداری تنظیم نشده باشد، `is_ok` مقدار `false` برمی‌گرداند و برنامه +جستجو را به صورت حساس به حروف کوچک و بزرگ انجام می‌دهد. ما به مقدار متغیر محیطی نیازی نداریم، فقط +می‌خواهیم بررسی کنیم که آیا تنظیم شده است یا نه. بنابراین از `is_ok` به جای متدهایی مانند +`unwrap`، `expect` یا دیگر متدهای مرتبط با `Result` استفاده می‌کنیم. + +ما مقدار متغیر `ignore_case` را به نمونه `Config` منتقل می‌کنیم تا تابع `run` بتواند این مقدار +را بخواند و تصمیم بگیرد که آیا باید تابع `search_case_insensitive` یا `search` را فراخوانی کند. + +### امتحان کردن برنامه + +حالا بیایید برنامه را امتحان کنیم! ابتدا برنامه را بدون تنظیم متغیر محیطی و با عبارت جستجوی +`to` اجرا می‌کنیم. این عبارت باید با هر خطی که شامل کلمه _to_ به صورت تمام حروف کوچک باشد، +مطابقت داشته باشد: ```console -{{#include ../listings/ch12-an-io-project/listing-12-23/output.txt}} +$ cargo run -- to poem.txt ``` -Looks like that still works! Now let’s run the program with `IGNORE_CASE` set -to `1` but with the same query _to_: +برنامه همچنان باید به درستی کار کند و تنها خطوطی که کاملاً با عبارت مطابقت دارند را برگرداند. +حالا برنامه را با متغیر محیطی `IGNORE_CASE` که به مقدار `1` تنظیم شده است اجرا می‌کنیم و +همان عبارت جستجو _to_ را امتحان می‌کنیم: ```console $ IGNORE_CASE=1 cargo run -- to poem.txt ``` -If you’re using PowerShell, you will need to set the environment variable and -run the program as separate commands: +در صورت استفاده از PowerShell، نیاز است که متغیر محیطی را تنظیم کنید و سپس برنامه را به صورت +دستورات جداگانه اجرا کنید: ```console PS> $Env:IGNORE_CASE=1; cargo run -- to poem.txt ``` -This will make `IGNORE_CASE` persist for the remainder of your shell session. -It can be unset with the `Remove-Item` cmdlet: +این دستور باعث می‌شود که `IGNORE_CASE` برای مدت زمان نشست ترمینال شما تنظیم باقی بماند. می‌توانید +آن را با دستور `Remove-Item` حذف کنید: ```console PS> Remove-Item Env:IGNORE_CASE ``` -We should get lines that contain _to_ that might have uppercase letters: - - +برنامه باید خطوطی که شامل _to_ هستند و ممکن است حروف بزرگ داشته باشند را برگرداند: ```console Are you nobody, too? @@ -184,18 +153,15 @@ To tell your name the livelong day To an admiring bog! ``` -Excellent, we also got lines containing _To_! Our `minigrep` program can now do -case-insensitive searching controlled by an environment variable. Now you know -how to manage options set using either command line arguments or environment -variables. - -Some programs allow arguments _and_ environment variables for the same -configuration. In those cases, the programs decide that one or the other takes -precedence. For another exercise on your own, try controlling case sensitivity -through either a command line argument or an environment variable. Decide -whether the command line argument or the environment variable should take -precedence if the program is run with one set to case sensitive and one set to -ignore case. - -The `std::env` module contains many more useful features for dealing with -environment variables: check out its documentation to see what is available. +عالی! حالا برنامه `minigrep` ما می‌تواند جستجوهای غیرحساس به حروف کوچک و بزرگ را انجام دهد که +با یک متغیر محیطی کنترل می‌شود. حالا شما می‌دانید چگونه گزینه‌هایی را که از طریق آرگومان‌های +خط فرمان یا متغیرهای محیطی تنظیم می‌شوند مدیریت کنید. + +برخی برنامه‌ها اجازه می‌دهند که آرگومان‌ها و متغیرهای محیطی برای یک پیکربندی واحد استفاده شوند. +در این موارد، برنامه‌ها تصمیم می‌گیرند که یکی از آن‌ها اولویت داشته باشد. برای تمرین بیشتر، +سعی کنید حساسیت به حروف کوچک و بزرگ را از طریق یک آرگومان خط فرمان یا یک متغیر محیطی کنترل کنید. +تصمیم بگیرید که در صورت تنظیم یکی به حساس و دیگری به غیرحساس بودن، آرگومان خط فرمان یا متغیر +محیطی باید اولویت داشته باشد. + +ماژول `std::env` ویژگی‌های مفید بسیاری برای کار با متغیرهای محیطی دارد: مستندات آن را بررسی کنید +تا ببینید چه امکاناتی در دسترس است. diff --git a/src/ch12-06-writing-to-stderr-instead-of-stdout.md b/src/ch12-06-writing-to-stderr-instead-of-stdout.md index 8113abf1f6..288524a6db 100644 --- a/src/ch12-06-writing-to-stderr-instead-of-stdout.md +++ b/src/ch12-06-writing-to-stderr-instead-of-stdout.md @@ -1,60 +1,34 @@ -## Writing Error Messages to Standard Error Instead of Standard Output +## نوشتن پیام‌های خطا به خروجی خطای استاندارد به جای خروجی استاندارد -At the moment, we’re writing all of our output to the terminal using the -`println!` macro. In most terminals, there are two kinds of output: _standard -output_ (`stdout`) for general information and _standard error_ (`stderr`) for -error messages. This distinction enables users to choose to direct the -successful output of a program to a file but still print error messages to the -screen. +در حال حاضر، ما تمام خروجی‌های خود را با استفاده از ماکروی `println!` به ترمینال می‌نویسیم. در بیشتر ترمینال‌ها، دو نوع خروجی وجود دارد: _خروجی استاندارد_ (`stdout`) برای اطلاعات عمومی و _خروجی خطای استاندارد_ (`stderr`) برای پیام‌های خطا. این تمایز به کاربران امکان می‌دهد که خروجی موفقیت‌آمیز یک برنامه را به یک فایل هدایت کنند اما همچنان پیام‌های خطا را روی صفحه ببینند. -The `println!` macro is only capable of printing to standard output, so we have -to use something else to print to standard error. +ماکروی `println!` فقط قادر به نوشتن در خروجی استاندارد است، بنابراین برای نوشتن به خروجی خطای استاندارد باید از چیزی دیگر استفاده کنیم. -### Checking Where Errors Are Written +### بررسی محل نوشتن خطاها -First let’s observe how the content printed by `minigrep` is currently being -written to standard output, including any error messages we want to write to -standard error instead. We’ll do that by redirecting the standard output stream -to a file while intentionally causing an error. We won’t redirect the standard -error stream, so any content sent to standard error will continue to display on -the screen. +ابتدا بررسی می‌کنیم که محتوای چاپ‌شده توسط `minigrep` در حال حاضر به خروجی استاندارد نوشته می‌شود، از جمله پیام‌های خطایی که می‌خواهیم به جای آن‌ها در خروجی خطای استاندارد نوشته شوند. این کار را با هدایت جریان خروجی استاندارد به یک فایل و عمداً ایجاد یک خطا انجام خواهیم داد. ما جریان خروجی خطای استاندارد را هدایت نمی‌کنیم، بنابراین هر محتوایی که به خروجی خطای استاندارد ارسال شود همچنان روی صفحه نمایش داده خواهد شد. -Command line programs are expected to send error messages to the standard error -stream so we can still see error messages on the screen even if we redirect the -standard output stream to a file. Our program is not currently well behaved: -we’re about to see that it saves the error message output to a file instead! +برنامه‌های خط فرمان انتظار می‌رود که پیام‌های خطای خود را به جریان خروجی خطای استاندارد ارسال کنند تا در صورت هدایت جریان خروجی استاندارد به یک فایل، پیام‌های خطا همچنان روی صفحه نمایش داده شوند. برنامه ما در حال حاضر به درستی عمل نمی‌کند: ما به زودی خواهیم دید که پیام خطا به جای صفحه نمایش به فایل ذخیره می‌شود! -To demonstrate this behavior, we’ll run the program with `>` and the file path, -_output.txt_, that we want to redirect the standard output stream to. We won’t -pass any arguments, which should cause an error: +برای نشان دادن این رفتار، برنامه را با استفاده از دستور `>` و مسیر فایل _output.txt_ که می‌خواهیم جریان خروجی استاندارد را به آن هدایت کنیم، اجرا می‌کنیم. هیچ آرگومانی ارائه نخواهیم کرد، که باید منجر به یک خطا شود: ```console $ cargo run > output.txt ``` -The `>` syntax tells the shell to write the contents of standard output to -_output.txt_ instead of the screen. We didn’t see the error message we were -expecting printed to the screen, so that means it must have ended up in the -file. This is what _output.txt_ contains: +دستور `>` به شل می‌گوید که محتوای جریان خروجی استاندارد را به _output.txt_ بنویسد به جای اینکه آن را روی صفحه نمایش دهد. ما پیام خطایی که انتظار داشتیم روی صفحه ببینیم را ندیدیم، بنابراین به این معنی است که باید در فایل ذخیره شده باشد. این همان چیزی است که _output.txt_ شامل می‌شود: ```text Problem parsing arguments: not enough arguments ``` -Yup, our error message is being printed to standard output. It’s much more -useful for error messages like this to be printed to standard error so only -data from a successful run ends up in the file. We’ll change that. +بله، پیام خطای ما به خروجی استاندارد چاپ می‌شود. برای پیام‌های خطایی مانند این بهتر است که به خروجی خطای استاندارد چاپ شوند تا فقط داده‌های حاصل از اجرای موفقیت‌آمیز در فایل قرار گیرند. ما این موضوع را تغییر خواهیم داد. -### Printing Errors to Standard Error +### نوشتن خطاها به خروجی خطای استاندارد -We’ll use the code in Listing 12-24 to change how error messages are printed. -Because of the refactoring we did earlier in this chapter, all the code that -prints error messages is in one function, `main`. The standard library provides -the `eprintln!` macro that prints to the standard error stream, so let’s change -the two places we were calling `println!` to print errors to use `eprintln!` -instead. +ما از کدی که در لیستینگ 12-24 نشان داده شده است برای تغییر نحوه چاپ پیام‌های خطا استفاده می‌کنیم. به دلیل بازسازی‌ای که قبلاً در این فصل انجام دادیم، تمام کدی که پیام‌های خطا را چاپ می‌کند در یک تابع به نام `main` قرار دارد. کتابخانه استاندارد ماکروی `eprintln!` را ارائه می‌دهد که به جریان خروجی خطای استاندارد چاپ می‌کند، بنابراین دو جایی که ما `println!` را برای چاپ خطاها فراخوانی کرده‌ایم را به `eprintln!` تغییر می‌دهیم. -+ ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-24/src/main.rs:here}} @@ -62,26 +36,22 @@ instead. -Let’s now run the program again in the same way, without any arguments and -redirecting standard output with `>`: +حالا برنامه را دوباره اجرا می‌کنیم به همان روش، بدون هیچ آرگومانی و با هدایت خروجی استاندارد با استفاده از `>`: ```console $ cargo run > output.txt Problem parsing arguments: not enough arguments ``` -Now we see the error onscreen and _output.txt_ contains nothing, which is the -behavior we expect of command line programs. +حالا خطا را روی صفحه می‌بینیم و _output.txt_ خالی است، که همان رفتاری است که از برنامه‌های خط فرمان انتظار داریم. -Let’s run the program again with arguments that don’t cause an error but still -redirect standard output to a file, like so: +برنامه را دوباره اجرا می‌کنیم با آرگومان‌هایی که خطایی ایجاد نمی‌کنند اما همچنان خروجی استاندارد را به یک فایل هدایت می‌کنند، مانند این: ```console $ cargo run -- to poem.txt > output.txt ``` -We won’t see any output to the terminal, and _output.txt_ will contain our -results: +هیچ خروجی روی ترمینال نخواهیم دید و _output.txt_ شامل نتایج ما خواهد بود: Filename: output.txt @@ -90,18 +60,8 @@ Are you nobody, too? How dreary to be somebody! ``` -This demonstrates that we’re now using standard output for successful output -and standard error for error output as appropriate. +این نشان می‌دهد که اکنون از خروجی استاندارد برای خروجی‌های موفقیت‌آمیز و از خروجی خطای استاندارد برای خروجی‌های خطا استفاده می‌کنیم، همان‌طور که مناسب است. -## Summary +## خلاصه -This chapter recapped some of the major concepts you’ve learned so far and -covered how to perform common I/O operations in Rust. By using command line -arguments, files, environment variables, and the `eprintln!` macro for printing -errors, you’re now prepared to write command line applications. Combined with -the concepts in previous chapters, your code will be well organized, store data -effectively in the appropriate data structures, handle errors nicely, and be -well tested. - -Next, we’ll explore some Rust features that were influenced by functional -languages: closures and iterators. +این فصل به طور خلاصه برخی از مفاهیم اصلی که تاکنون آموخته‌اید را مرور کرد و توضیح داد که چگونه عملیات ورودی/خروجی معمول را در Rust انجام دهید. با استفاده از آرگومان‌های خط فرمان، فایل‌ها، متغیرهای محیطی و ماکروی `eprintln!` برای چاپ خطاها، شما اکنون آماده‌اید تا برنامه‌های خط فرمان بنویسید. همراه با مفاهیم فصل‌های قبلی، کد شما سازماندهی خوبی خواهد داشت، داده‌ها را به طور مؤثر در ساختارهای داده مناسب ذخیره می‌کند، خطاها را به خوبی مدیریت می‌کند و به خوبی تست شده است. diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 3332c4d986..81fa32cf73 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -1,24 +1,21 @@ -# Functional Language Features: Iterators and Closures +# ویژگی‌های زبان‌های تابعی: تکرارگرها و closureها -Rust’s design has taken inspiration from many existing languages and -techniques, and one significant influence is _functional programming_. -Programming in a functional style often includes using functions as values by -passing them in arguments, returning them from other functions, assigning them -to variables for later execution, and so forth. +طراحی زبان Rust از بسیاری از زبان‌ها و تکنیک‌های موجود الهام گرفته است و یکی از تأثیرات مهم آن +_برنامه‌نویسی تابعی_ است. برنامه‌نویسی به سبک تابعی اغلب شامل استفاده از توابع به عنوان مقادیر +است، از طریق ارسال آن‌ها به عنوان آرگومان، بازگرداندن آن‌ها از دیگر توابع، اختصاص آن‌ها به +متغیرها برای اجرای بعدی و موارد دیگر. -In this chapter, we won’t debate the issue of what functional programming is or -isn’t but will instead discuss some features of Rust that are similar to -features in many languages often referred to as functional. +در این فصل، ما بحث نخواهیم کرد که برنامه‌نویسی تابعی چیست یا چه نیست، بلکه به جای آن درباره +برخی از ویژگی‌های Rust که مشابه ویژگی‌های بسیاری از زبان‌هایی است که اغلب به آن‌ها تابعی گفته +می‌شود، صحبت خواهیم کرد. -More specifically, we’ll cover: +به طور خاص، ما پوشش خواهیم داد: -- _Closures_, a function-like construct you can store in a variable -- _Iterators_, a way of processing a series of elements -- How to use closures and iterators to improve the I/O project in Chapter 12 -- The performance of closures and iterators (spoiler alert: they’re faster than - you might think!) +* _Closure_‌ها، ساختاری شبیه به تابع که می‌توان آن را در یک متغیر ذخیره کرد +* _پیمایشگر_‌ها (Iterator)، روشی برای پردازش مجموعه‌ای از عناصر +* نحوه استفاده از closureها و iteratorها برای بهبود پروژهٔ ورودی/خروجی در فصل ۱۲ +* عملکرد closureها و iteratorها (هشدار: آن‌ها سریع‌تر از چیزی هستند که ممکن است فکر کنید!) -We’ve already covered some other Rust features, such as pattern matching and -enums, that are also influenced by the functional style. Because mastering -closures and iterators is an important part of writing idiomatic, fast Rust -code, we’ll devote this entire chapter to them. +ما قبلاً برخی از ویژگی‌های دیگر Rust، مانند الگوها و enums را پوشش داده‌ایم که همچنین از +سبک تابعی تأثیر گرفته‌اند. از آنجایی که تسلط بر closureها و تکرارگرها بخش مهمی از نوشتن کد +ایدوماکتیک و سریع در Rust است، ما کل این فصل را به آن‌ها اختصاص خواهیم داد. \ No newline at end of file diff --git a/src/ch13-01-closures.md b/src/ch13-01-closures.md index c4859d2f42..f7d5e2325f 100644 --- a/src/ch13-01-closures.md +++ b/src/ch13-01-closures.md @@ -2,42 +2,25 @@ -## Closures: Anonymous Functions That Capture Their Environment +

closureها: توابع ناشناسی که محیط خود را می‌گیرند

-Rust’s closures are anonymous functions you can save in a variable or pass as -arguments to other functions. You can create the closure in one place and then -call the closure elsewhere to evaluate it in a different context. Unlike -functions, closures can capture values from the scope in which they’re defined. -We’ll demonstrate how these closure features allow for code reuse and behavior -customization. + - +closureهای Rust توابع ناشناسی هستند که می‌توانید آن‌ها را در یک متغیر ذخیره کنید یا به عنوان آرگومان به توابع دیگر ارسال کنید. شما می‌توانید closure را در یک مکان ایجاد کنید و سپس آن را در جای دیگری فراخوانی کنید تا در یک زمینه متفاوت ارزیابی شود. برخلاف توابع، closureها می‌توانند مقادیر را از محیطی که در آن تعریف شده‌اند، بگیرند. ما نشان خواهیم داد که چگونه این ویژگی‌های closure امکان استفاده مجدد از کد و سفارشی‌سازی رفتار را فراهم می‌کند. + + -### Capturing the Environment with Closures +### گرفتن محیط با closureها -We’ll first examine how we can use closures to capture values from the -environment they’re defined in for later use. Here’s the scenario: every so -often, our T-shirt company gives away an exclusive, limited-edition shirt to -someone on our mailing list as a promotion. People on the mailing list can -optionally add their favorite color to their profile. If the person chosen for -a free shirt has their favorite color set, they get that color shirt. If the -person hasn’t specified a favorite color, they get whatever color the company -currently has the most of. +در ابتدا بررسی می‌کنیم که چگونه می‌توانیم از closureها برای گرفتن مقادیر از محیطی که در آن تعریف شده‌اند جهت استفاده در آینده استفاده کنیم. سناریو به این صورت است: هر از چندگاهی، شرکت تی‌شرت ما یک تی‌شرت خاص و نسخه محدود را به عنوان تبلیغ به یکی از افراد موجود در لیست ایمیل هدیه می‌دهد. افراد موجود در لیست ایمیل می‌توانند به‌صورت اختیاری رنگ مورد علاقه خود را به پروفایلشان اضافه کنند. اگر فردی که برای دریافت تی‌شرت رایگان انتخاب شده، رنگ مورد علاقه‌ای مشخص کرده باشد، همان رنگ را دریافت خواهد کرد. اگر رنگی انتخاب نکرده باشد، رنگی را دریافت خواهد کرد که شرکت در حال حاضر بیشترین موجودی از آن را دارد. -There are many ways to implement this. For this example, we’re going to use an -enum called `ShirtColor` that has the variants `Red` and `Blue` (limiting the -number of colors available for simplicity). We represent the company’s -inventory with an `Inventory` struct that has a field named `shirts` that -contains a `Vec` representing the shirt colors currently in stock. -The method `giveaway` defined on `Inventory` gets the optional shirt -color preference of the free-shirt winner, and returns the shirt color the -person will get. This setup is shown in Listing 13-1. +راه‌های زیادی برای پیاده‌سازی این سناریو وجود دارد. در این مثال، از یک `enum` به نام `ShirtColor` استفاده می‌کنیم که شامل دو حالت `Red` و `Blue` است (برای سادگی، تعداد رنگ‌ها را محدود کرده‌ایم). موجودی شرکت با یک `struct` به نام `Inventory` نمایش داده می‌شود که دارای فیلدی به نام `shirts` است و یک `Vec` شامل رنگ‌های تی‌شرت موجود در انبار را نگهداری می‌کند. متد `giveaway` که روی `Inventory` تعریف شده، رنگ دلخواه (اختیاری) فرد برنده تی‌شرت رایگان را دریافت می‌کند و رنگ تی‌شرتی که قرار است به او داده شود را بازمی‌گرداند. این ساختار در لیست 13-1 نشان داده شده است. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch13-functional-features/listing-13-01/src/main.rs}} @@ -45,69 +28,34 @@ person will get. This setup is shown in Listing 13-1. -The `store` defined in `main` has two blue shirts and one red shirt remaining -to distribute for this limited-edition promotion. We call the `giveaway` method -for a user with a preference for a red shirt and a user without any preference. - -Again, this code could be implemented in many ways, and here, to focus on -closures, we’ve stuck to concepts you’ve already learned, except for the body of -the `giveaway` method that uses a closure. In the `giveaway` method, we get the -user preference as a parameter of type `Option` and call the -`unwrap_or_else` method on `user_preference`. The [`unwrap_or_else` method on -`Option`][unwrap-or-else] is defined by the standard library. -It takes one argument: a closure without any arguments that returns a value `T` -(the same type stored in the `Some` variant of the `Option`, in this case -`ShirtColor`). If the `Option` is the `Some` variant, `unwrap_or_else` -returns the value from within the `Some`. If the `Option` is the `None` -variant, `unwrap_or_else` calls the closure and returns the value returned by -the closure. - -We specify the closure expression `|| self.most_stocked()` as the argument to -`unwrap_or_else`. This is a closure that takes no parameters itself (if the -closure had parameters, they would appear between the two vertical pipes). The -body of the closure calls `self.most_stocked()`. We’re defining the closure -here, and the implementation of `unwrap_or_else` will evaluate the closure -later if the result is needed. - -Running this code prints the following: +در این کد، `store` تعریف‌شده در `main` دو تی‌شرت آبی و یک تی‌شرت قرمز باقی‌مانده برای توزیع در این تبلیغ نسخه محدود دارد. ما متد `giveaway` را برای یک کاربر با ترجیح یک تی‌شرت قرمز و یک کاربر بدون هیچ ترجیحی فراخوانی می‌کنیم. + +دوباره تأکید می‌کنیم که این کد را می‌توان به روش‌های مختلفی پیاده‌سازی کرد. در اینجا، برای تمرکز بر closureها، به مفاهیمی که قبلاً آموخته‌اید پایبند مانده‌ایم، به جز بخش بدنه متد `giveaway` که از یک closure استفاده می‌کند. در متد `giveaway`، ما اولویت کاربر را به عنوان یک آرگومان از نوع `Option` دریافت می‌کنیم و متد `unwrap_or_else` را روی `user_preference` فراخوانی می‌کنیم. + +متد [`unwrap_or_else` روی `Option`][unwrap-or-else] توسط کتابخانه استاندارد تعریف شده است. این متد یک آرگومان می‌گیرد: یک closure بدون هیچ آرگومانی که یک مقدار `T` را بازمی‌گرداند (همان نوعی که در متغیر `Some` از `Option` ذخیره شده است، در این مورد `ShirtColor`). اگر `Option` مقدار `Some` داشته باشد، `unwrap_or_else` مقدار داخل `Some` را بازمی‌گرداند. اگر `Option` مقدار `None` باشد، `unwrap_or_else` closure را فراخوانی کرده و مقداری که closure بازمی‌گرداند را بازمی‌گرداند. + +ما عبارت closure `|| self.most_stocked()` را به عنوان آرگومان به `unwrap_or_else` ارسال می‌کنیم. این یک closure است که خود هیچ آرگومانی نمی‌گیرد (اگر closure آرگومان‌هایی داشت، آن‌ها بین دو خط عمودی قرار می‌گرفتند). بدنه closure متد `self.most_stocked()` را فراخوانی می‌کند. ما closure را اینجا تعریف می‌کنیم و پیاده‌سازی `unwrap_or_else` در صورت نیاز، closure را ارزیابی می‌کند. + + +اجرای این کد موارد زیر را چاپ می‌کند: ```console {{#include ../listings/ch13-functional-features/listing-13-01/output.txt}} ``` -One interesting aspect here is that we’ve passed a closure that calls -`self.most_stocked()` on the current `Inventory` instance. The standard library -didn’t need to know anything about the `Inventory` or `ShirtColor` types we -defined, or the logic we want to use in this scenario. The closure captures an -immutable reference to the `self` `Inventory` instance and passes it with the -code we specify to the `unwrap_or_else` method. Functions, on the other hand, -are not able to capture their environment in this way. - -### Closure Type Inference and Annotation - -There are more differences between functions and closures. Closures don’t -usually require you to annotate the types of the parameters or the return value -like `fn` functions do. Type annotations are required on functions because the -types are part of an explicit interface exposed to your users. Defining this -interface rigidly is important for ensuring that everyone agrees on what types -of values a function uses and returns. Closures, on the other hand, aren’t used -in an exposed interface like this: they’re stored in variables and used without -naming them and exposing them to users of our library. - -Closures are typically short and relevant only within a narrow context rather -than in any arbitrary scenario. Within these limited contexts, the compiler can -infer the types of the parameters and the return type, similar to how it’s able -to infer the types of most variables (there are rare cases where the compiler -needs closure type annotations too). - -As with variables, we can add type annotations if we want to increase -explicitness and clarity at the cost of being more verbose than is strictly -necessary. Annotating the types for a closure would look like the definition -shown in Listing 13-2. In this example, we’re defining a closure and storing it -in a variable rather than defining the closure in the spot we pass it as an -argument, as we did in Listing 13-1. - -+یکی از جنبه‌های جالب در اینجا این است که ما یک closure ارسال کرده‌ایم که متد `self.most_stocked()` را روی نمونه فعلی `Inventory` فراخوانی می‌کند. کتابخانه استاندارد نیازی به دانستن چیزی درباره انواع `Inventory` یا `ShirtColor` که تعریف کرده‌ایم یا منطقی که می‌خواهیم در این سناریو استفاده کنیم، ندارد. closure یک ارجاع غیرقابل تغییر به نمونه `self` از `Inventory` را می‌گیرد و آن را همراه با کدی که مشخص کرده‌ایم به متد `unwrap_or_else` ارسال می‌کند. از طرف دیگر، توابع قادر به گرفتن محیط خود به این صورت نیستند. + +### استنباط نوع closure و حاشیه‌نویسی + +تفاوت‌های بیشتری بین توابع و closureها وجود دارد. closureها معمولاً نیازی به حاشیه‌نویسی انواع آرگومان‌ها یا مقدار بازگشتی ندارند، برخلاف توابع `fn` که به این حاشیه‌نویسی نیاز دارند. حاشیه‌نویسی انواع در توابع ضروری است زیرا این انواع بخشی از رابط کاربری صریحی هستند که برای کاربران شما ارائه می‌شود. تعریف این رابط به صورت سختگیرانه برای اطمینان از توافق همه در مورد انواع مقادیر استفاده شده و بازگشتی یک تابع مهم است. از طرف دیگر، closureها به این صورت در یک رابط کاربری صریح استفاده نمی‌شوند: آن‌ها در متغیرها ذخیره می‌شوند و بدون نام‌گذاری و افشای آن‌ها به کاربران کتابخانه ما استفاده می‌شوند. + +
+closureها معمولاً کوتاه هستند و فقط در یک زمینه محدود مرتبط هستند، نه در هر سناریوی دلخواه. در این زمینه‌های محدود، کامپایلر می‌تواند انواع پارامترها و مقدار بازگشتی را استنباط کند، مشابه آنچه که می‌تواند انواع اکثر متغیرها را استنباط کند (موارد نادری وجود دارند که کامپایلر به حاشیه‌نویسی نوع closure نیز نیاز دارد). +
+
+همانند متغیرها، ما می‌توانیم حاشیه‌نویسی نوع اضافه کنیم اگر بخواهیم وضوح و شفافیت را افزایش دهیم، به قیمت پرحرف‌تر شدن از آنچه که به طور دقیق ضروری است. افزودن حاشیه‌نویسی نوع برای یک closure به این صورت است که در لیستینگ 13-2 نشان داده شده است. در این مثال، ما یک closure تعریف کرده و آن را در یک متغیر ذخیره می‌کنیم، به جای اینکه closure را در مکانی که به عنوان آرگومان ارسال می‌کنیم تعریف کنیم، همانطور که در لیستینگ 13-1 انجام دادیم. + + ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-02/src/main.rs:here}} @@ -115,12 +63,7 @@ argument, as we did in Listing 13-1. -With type annotations added, the syntax of closures looks more similar to the -syntax of functions. Here, we define a function that adds 1 to its parameter and -a closure that has the same behavior, for comparison. We’ve added some spaces -to line up the relevant parts. This illustrates how closure syntax is similar -to function syntax except for the use of pipes and the amount of syntax that is -optional: +با اضافه کردن حاشیه‌نویسی نوع، نحوه نوشتن closureها بیشتر شبیه به نوشتن توابع می‌شود. در اینجا، ما یک تابع تعریف کرده‌ایم که 1 به آرگومان خود اضافه می‌کند و یک closure که همان رفتار را دارد، برای مقایسه. ما فضاهایی اضافه کرده‌ایم تا بخش‌های مرتبط را هم‌ردیف کنیم. این نشان می‌دهد که نحو closure چقدر شبیه به نحو توابع است، به جز استفاده از خطوط عمودی و میزان نحوی که اختیاری است. ```rust,ignore fn add_one_v1 (x: u32) -> u32 { x + 1 } @@ -129,26 +72,11 @@ let add_one_v3 = |x| { x + 1 }; let add_one_v4 = |x| x + 1 ; ``` -The first line shows a function definition and the second line shows a fully -annotated closure definition. In the third line, we remove the type annotations -from the closure definition. In the fourth line, we remove the brackets, which -are optional because the closure body has only one expression. These are all -valid definitions that will produce the same behavior when they’re called. The -`add_one_v3` and `add_one_v4` lines require the closures to be evaluated to be -able to compile because the types will be inferred from their usage. This is -similar to `let v = Vec::new();` needing either type annotations or values of -some type to be inserted into the `Vec` for Rust to be able to infer the type. - -For closure definitions, the compiler will infer one concrete type for each of -their parameters and for their return value. For instance, Listing 13-3 shows -the definition of a short closure that just returns the value it receives as a -parameter. This closure isn’t very useful except for the purposes of this -example. Note that we haven’t added any type annotations to the definition. -Because there are no type annotations, we can call the closure with any type, -which we’ve done here with `String` the first time. If we then try to call -`example_closure` with an integer, we’ll get an error. - -+خط اول تعریف یک تابع را نشان می‌دهد، و خط دوم تعریف یک closure با حاشیه‌نویسی کامل را نمایش می‌دهد. در خط سوم، حاشیه‌نویسی انواع از تعریف closure حذف شده است. در خط چهارم، براکت‌ها را حذف می‌کنیم، که اختیاری هستند زیرا بدنه closure فقط یک عبارت دارد. همه این‌ها تعاریف معتبری هستند که هنگام فراخوانی رفتار یکسانی تولید می‌کنند. خطوط `add_one_v3` و `add_one_v4` نیاز دارند که closureها ارزیابی شوند تا کامپایل شوند زیرا انواع از نحوه استفاده آن‌ها استنباط خواهند شد. این مشابه با `let v = Vec::new();` است که نیاز دارد یا حاشیه‌نویسی نوع داشته باشد یا مقادیر از نوعی در `Vec` وارد شوند تا Rust بتواند نوع را استنباط کند. + +برای تعریف closureها، کامپایلر یک نوع مشخص برای هر یک از پارامترها و مقدار بازگشتی آن‌ها استنباط می‌کند. برای مثال، لیستینگ 13-3 تعریف یک closure کوتاه را نشان می‌دهد که فقط مقداری که به عنوان پارامتر دریافت می‌کند را بازمی‌گرداند. این closure برای اهداف این مثال استفاده چندانی ندارد. توجه کنید که هیچ حاشیه‌نویسی نوعی به تعریف اضافه نکرده‌ایم. چون هیچ حاشیه‌نویسی وجود ندارد، می‌توانیم closure را با هر نوعی فراخوانی کنیم، همان‌طور که اولین بار این کار را با `String` انجام دادیم. اگر سپس سعی کنیم `example_closure` را با یک عدد صحیح فراخوانی کنیم، خطایی دریافت خواهیم کرد. + + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch13-functional-features/listing-13-03/src/main.rs:here}} @@ -156,30 +84,21 @@ which we’ve done here with `String` the first time. If we then try to call -The compiler gives us this error: +کامپایلر این خطا را می‌دهد: ```console {{#include ../listings/ch13-functional-features/listing-13-03/output.txt}} ``` -The first time we call `example_closure` with the `String` value, the compiler -infers the type of `x` and the return type of the closure to be `String`. Those -types are then locked into the closure in `example_closure`, and we get a type -error when we next try to use a different type with the same closure. +اولین باری که `example_closure` را با مقدار `String` فراخوانی می‌کنیم، کامپایلر نوع `x` و مقدار بازگشتی closure را به عنوان `String` استنباط می‌کند. سپس این انواع در closure `example_closure` قفل می‌شوند و هنگام تلاش برای استفاده از یک نوع دیگر با همان closure، یک خطای نوع دریافت می‌کنیم. -### Capturing References or Moving Ownership +### گرفتن ارجاعات یا انتقال مالکیت -Closures can capture values from their environment in three ways, which -directly map to the three ways a function can take a parameter: borrowing -immutably, borrowing mutably, and taking ownership. The closure will decide -which of these to use based on what the body of the function does with the -captured values. +closureها می‌توانند مقادیر را از محیط خود به سه روش بگیرند که مستقیماً به سه روشی که یک تابع می‌تواند یک پارامتر بگیرد، نگاشت می‌شوند: قرض‌گیری غیرقابل تغییر، قرض‌گیری قابل تغییر، و گرفتن مالکیت. closure تصمیم می‌گیرد که کدام یک از این‌ها را بر اساس کاری که بدنه تابع با مقادیر گرفته شده انجام می‌دهد، استفاده کند. -In Listing 13-4, we define a closure that captures an immutable reference to -the vector named `list` because it only needs an immutable reference to print -the value. +در لیستینگ 13-4، یک closure تعریف می‌کنیم که یک ارجاع غیرقابل تغییر به بردار با نام `list` را می‌گیرد زیرا فقط به یک ارجاع غیرقابل تغییر نیاز دارد تا مقدار را چاپ کند: -+ ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-04/src/main.rs}} @@ -187,23 +106,17 @@ the value. -This example also illustrates that a variable can bind to a closure definition, -and we can later call the closure by using the variable name and parentheses as -if the variable name were a function name. +این مثال همچنین نشان می‌دهد که یک متغیر می‌تواند به تعریف یک closure متصل شود و بعداً می‌توان closure را با استفاده از نام متغیر و پرانتزها فراخوانی کرد، گویی که نام متغیر یک نام تابع است. -Because we can have multiple immutable references to `list` at the same time, -`list` is still accessible from the code before the closure definition, after -the closure definition but before the closure is called, and after the closure -is called. This code compiles, runs, and prints: +از آنجا که می‌توانیم چندین ارجاع غیرقابل تغییر به `list` به طور همزمان داشته باشیم، `list` همچنان از کدی که قبل از تعریف closure، بعد از تعریف closure اما قبل از فراخوانی closure و بعد از فراخوانی closure وجود دارد، قابل دسترسی است. این کد کامپایل شده، اجرا می‌شود و نتیجه زیر را چاپ می‌کند: ```console {{#include ../listings/ch13-functional-features/listing-13-04/output.txt}} ``` -Next, in Listing 13-5, we change the closure body so that it adds an element to -the `list` vector. The closure now captures a mutable reference. +در ادامه، در لیستینگ 13-5، بدنه closure را تغییر می‌دهیم تا یک عنصر به بردار `list` اضافه کند. closure اکنون یک ارجاع قابل تغییر می‌گیرد: -+ ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-05/src/main.rs}} @@ -211,32 +124,19 @@ the `list` vector. The closure now captures a mutable reference. -This code compiles, runs, and prints: +این کد کامپایل شده، اجرا می‌شود و نتیجه زیر را چاپ می‌کند: ```console {{#include ../listings/ch13-functional-features/listing-13-05/output.txt}} ``` -Note that there’s no longer a `println!` between the definition and the call of -the `borrows_mutably` closure: when `borrows_mutably` is defined, it captures a -mutable reference to `list`. We don’t use the closure again after the closure -is called, so the mutable borrow ends. Between the closure definition and the -closure call, an immutable borrow to print isn’t allowed because no other -borrows are allowed when there’s a mutable borrow. Try adding a `println!` -there to see what error message you get! +توجه داشته باشید که دیگر `println!` بین تعریف و فراخوانی closure `borrows_mutably` وجود ندارد: زمانی که `borrows_mutably` تعریف می‌شود، یک ارجاع قابل تغییر به `list` می‌گیرد. ما بعد از فراخوانی closure دوباره از آن استفاده نمی‌کنیم، بنابراین قرض‌گیری قابل تغییر پایان می‌یابد. بین تعریف closure و فراخوانی آن، قرض‌گیری غیرقابل تغییر برای چاپ مجاز نیست، زیرا هیچ قرض دیگری هنگام وجود یک قرض قابل تغییر مجاز نیست. سعی کنید یک `println!` در آنجا اضافه کنید تا ببینید چه پیام خطایی دریافت می‌کنید! -If you want to force the closure to take ownership of the values it uses in the -environment even though the body of the closure doesn’t strictly need -ownership, you can use the `move` keyword before the parameter list. +اگر بخواهید closure را مجبور کنید که مالکیت مقادیر استفاده‌شده در محیط را بگیرد، حتی اگر بدنه closure به طور دقیق به مالکیت نیاز نداشته باشد، می‌توانید از کلیدواژه `move` قبل از لیست پارامترها استفاده کنید. -This technique is mostly useful when passing a closure to a new thread to move -the data so that it’s owned by the new thread. We’ll discuss threads and why -you would want to use them in detail in Chapter 16 when we talk about -concurrency, but for now, let’s briefly explore spawning a new thread using a -closure that needs the `move` keyword. Listing 13-6 shows Listing 13-4 modified -to print the vector in a new thread rather than in the main thread. +این تکنیک بیشتر زمانی مفید است که یک closure را به یک نخ جدید ارسال می‌کنید تا داده‌ها به گونه‌ای انتقال داده شوند که توسط نخ جدید مالکیت پیدا کنند. ما موضوع نخ‌ها و دلایلی که ممکن است بخواهید از آن‌ها استفاده کنید را به تفصیل در فصل 16 زمانی که در مورد هم‌زمانی صحبت می‌کنیم، بررسی خواهیم کرد. اما برای حالا، بیایید به صورت مختصر ایجاد یک نخ جدید با استفاده از یک closure که به کلیدواژه `move` نیاز دارد را بررسی کنیم. لیستینگ 13-6 لیستینگ 13-4 را اصلاح می‌کند تا بردار را در یک نخ جدید چاپ کند به جای اینکه در نخ اصلی این کار را انجام دهد: -+ ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-06/src/main.rs}} @@ -244,45 +144,35 @@ to print the vector in a new thread rather than in the main thread. -We spawn a new thread, giving the thread a closure to run as an argument. The -closure body prints out the list. In Listing 13-4, the closure only captured -`list` using an immutable reference because that's the least amount of access -to `list` needed to print it. In this example, even though the closure body -still only needs an immutable reference, we need to specify that `list` should -be moved into the closure by putting the `move` keyword at the beginning of the -closure definition. If the main thread performed more operations before calling -`join` on the new thread, the new thread might finish before the rest of the -main thread finishes, or the main thread might finish first. If the main thread -maintained ownership of `list` but ended before the new thread and drops -`list`, the immutable reference in the thread would be invalid. Therefore, the -compiler requires that `list` be moved into the closure given to the new thread -so the reference will be valid. Try removing the `move` keyword or using `list` -in the main thread after the closure is defined to see what compiler errors you -get! - - +ما یک نخ (thread) جدید ایجاد می‌کنیم و به آن یک closure برای اجرا به‌عنوان آرگومان می‌دهیم. بدنه‌ی closure لیست را چاپ می‌کند. در لیستینگ 13-4، closure فقط با استفاده از یک رفرنس تغییرناپذیر (`immutable reference`) به `list` دسترسی دارد، زیرا این کمترین میزان دسترسی موردنیاز برای چاپ لیست است. +در این مثال، با اینکه بدنه‌ی closure هنوز فقط به یک رفرنس تغییرناپذیر نیاز دارد، ما باید مشخص کنیم که `list` باید به درون closure منتقل شود. برای این کار، از کلمه‌ی کلیدی `move` در ابتدای تعریف closure استفاده می‌کنیم. + +اگر نخ اصلی (main thread) قبل از فراخوانی `join` عملیات بیشتری انجام دهد، ممکن است نخ جدید زودتر از نخ اصلی تمام شود، یا بالعکس، نخ اصلی زودتر خاتمه یابد. اگر نخ اصلی مالکیت `list` را حفظ کرده باشد ولی قبل از پایان نخ جدید خاتمه یابد و `list` را آزاد کند، رفرنسی که نخ جدید استفاده می‌کند نامعتبر خواهد شد. + +بنابراین، کامپایلر الزام می‌کند که `list` به درون closure داده‌شده به نخ جدید منتقل شود تا رفرنس معتبر باقی بماند. + +سعی کنید کلمه‌ی کلیدی `move` را حذف کنید یا از `list` در نخ اصلی پس از تعریف closure استفاده کنید تا ببینید چه خطاهایی از سوی کامپایلر دریافت می‌کنید! + +نخ جدید ممکن است قبل از تکمیل نخ اصلی تمام شود، یا نخ اصلی ممکن است زودتر تمام شود. اگر نخ اصلی مالکیت `list` را حفظ می‌کرد اما قبل از نخ جدید به پایان می‌رسید و `list` را حذف می‌کرد، ارجاع غیرقابل تغییر در نخ دیگر معتبر نبود. بنابراین، کامپایلر نیاز دارد که `list` به داخل closure داده‌شده به نخ جدید منتقل شود تا ارجاع معتبر باقی بماند. سعی کنید کلمه کلیدی `move` را حذف کنید یا از `list` در نخ اصلی پس از تعریف closure استفاده کنید تا ببینید چه خطاهای کامپایلری دریافت می‌کنید! + + -### Moving Captured Values Out of Closures and the `Fn` Traits +### انتقال مقادیر گرفته‌شده به خارج از closureها و صفات `Fn` + +زمانی که یک closure رفرنس یا مالکیت یک مقدار را از محیطی که در آن تعریف شده، **گرفته باشد** (که مشخص می‌کند چه چیزی — در صورت وجود — به **درون** closure منتقل می‌شود)، کدی که در بدنه‌ی closure قرار دارد تعیین می‌کند که چه اتفاقی برای آن رفرنس‌ها یا مقادیر در زمان اجرای closure می‌افتد (که مشخص می‌کند چه چیزی — در صورت وجود — از closure به **بیرون** منتقل می‌شود). -Once a closure has captured a reference or captured ownership of a value from -the environment where the closure is defined (thus affecting what, if anything, -is moved _into_ the closure), the code in the body of the closure defines what -happens to the references or values when the closure is evaluated later (thus -affecting what, if anything, is moved _out of_ the closure). +بدنه‌ی یک closure می‌تواند هر یک از موارد زیر را انجام دهد: -A closure body can do any of the following: move a captured value out of the -closure, mutate the captured value, neither move nor mutate the value, or -capture nothing from the environment to begin with. +* یک مقدار گرفته‌شده را به بیرون از closure منتقل کند (move) +* مقدار گرفته‌شده را تغییر دهد (mutate) +* نه مقداری را منتقل کند و نه تغییری ایجاد کند +* هیچ چیزی از محیط را در ابتدا نگرفته باشد -The way a closure captures and handles values from the environment affects -which traits the closure implements, and traits are how functions and structs -can specify what kinds of closures they can use. Closures will automatically -implement one, two, or all three of these `Fn` traits, in an additive fashion, -depending on how the closure’s body handles the values: +نحوه گرفتن و مدیریت مقادیر توسط closure از محیط مشخص می‌کند که closure کدام صفات را پیاده‌سازی می‌کند. صفات روشی هستند که توابع و ساختارها می‌توانند مشخص کنند از چه نوع closureهایی می‌توانند استفاده کنند. closureها به صورت خودکار یکی، دو یا هر سه این صفات `Fn` را پیاده‌سازی می‌کنند، به صورت افزایشی، بسته به نحوه مدیریت مقادیر توسط بدنه closure: * `FnOnce` applies to closures that can be called once. All closures implement at least this trait because all closures can be called. A closure that moves @@ -297,8 +187,7 @@ depending on how the closure’s body handles the values: without mutating their environment, which is important in cases such as calling a closure multiple times concurrently. -Let’s look at the definition of the `unwrap_or_else` method on `Option` that -we used in Listing 13-1: +بیایید تعریف متد `unwrap_or_else` در `Option` را که در لیستینگ 13-1 استفاده کردیم بررسی کنیم: ```rust,ignore impl Option { @@ -314,42 +203,22 @@ impl Option { } ``` -Recall that `T` is the generic type representing the type of the value in the -`Some` variant of an `Option`. That type `T` is also the return type of the -`unwrap_or_else` function: code that calls `unwrap_or_else` on an -`Option`, for example, will get a `String`. - -Next, notice that the `unwrap_or_else` function has the additional generic type -parameter `F`. The `F` type is the type of the parameter named `f`, which is -the closure we provide when calling `unwrap_or_else`. - -The trait bound specified on the generic type `F` is `FnOnce() -> T`, which -means `F` must be able to be called once, take no arguments, and return a `T`. -Using `FnOnce` in the trait bound expresses the constraint that -`unwrap_or_else` is only going to call `f` at most one time. In the body of -`unwrap_or_else`, we can see that if the `Option` is `Some`, `f` won’t be -called. If the `Option` is `None`, `f` will be called once. Because all -closures implement `FnOnce`, `unwrap_or_else` accepts all three kinds of -closures and is as flexible as it can be. - -> Note: If what we want to do doesn’t require capturing a value from the -> environment, we can use the name of a function rather than a closure where we -> need something that implements one of the `Fn` traits. For example, on an -> `Option>` value, we could call `unwrap_or_else(Vec::new)` to get a -> new, empty vector if the value is `None`. The compiler automatically -> implements whichever of the `Fn` traits is applicable for a function -> definition. - -Now let’s look at the standard library method `sort_by_key`, defined on slices, -to see how that differs from `unwrap_or_else` and why `sort_by_key` uses -`FnMut` instead of `FnOnce` for the trait bound. The closure gets one argument -in the form of a reference to the current item in the slice being considered, -and returns a value of type `K` that can be ordered. This function is useful -when you want to sort a slice by a particular attribute of each item. In -Listing 13-7, we have a list of `Rectangle` instances and we use `sort_by_key` -to order them by their `width` attribute from low to high. - -+ +به یاد داشته باشید که `T` نوع جنریک است که نوع مقدار موجود در واریانت `Some` از `Option` را نشان می‌دهد. این نوع `T` همچنین نوع بازگشتی تابع `unwrap_or_else` است: به عنوان مثال، کدی که `unwrap_or_else` را روی یک `Option` فراخوانی می‌کند، یک `String` دریافت خواهد کرد. + +بعدی، توجه داشته باشید که تابع `unwrap_or_else` پارامتر نوع جنریک اضافی `F` را دارد. نوع `F` نوع پارامتر نام‌گذاری‌شده `f` است، که closureی است که هنگام فراخوانی `unwrap_or_else` ارائه می‌دهیم. + +محدودیت صفت مشخص‌شده روی نوع جنریک `F`، `FnOnce() -> T` است، که به این معناست که `F` باید بتواند یک بار فراخوانی شود، هیچ آرگومانی نگیرد و یک `T` بازگرداند. استفاده از `FnOnce` در محدودیت صفت، محدودیت این موضوع را بیان می‌کند که `unwrap_or_else` حداکثر یک بار `f` را فراخوانی خواهد کرد. در بدنه `unwrap_or_else`، می‌بینیم که اگر `Option` برابر با `Some` باشد، `f` فراخوانی نمی‌شود. اگر `Option` برابر با `None` باشد، `f` یک بار فراخوانی خواهد شد. از آنجایی که تمام closureها `FnOnce` را پیاده‌سازی می‌کنند، `unwrap_or_else` همه انواع سه‌گانه closureها را می‌پذیرد و به اندازه کافی انعطاف‌پذیر است. + +> نکته: اگر کاری که می‌خواهیم انجام دهیم نیازی به گرفتن مقدار از محیط نداشته باشد، می‌توانیم به‌جای یک closure، از نام یک تابع استفاده کنیم؛ در جایی که نیاز به چیزی داریم که یکی از traitهای `Fn` را پیاده‌سازی کند. +> برای مثال، روی یک مقدار از نوع `Option>` می‌توانیم `unwrap_or_else(Vec::new)` را فراخوانی کنیم تا در صورتی که مقدار `None` بود، یک `vector` جدید و خالی دریافت کنیم. +> کامپایلر به‌طور خودکار هرکدام از traitهای `Fn` که برای تعریف یک تابع مناسب باشند را پیاده‌سازی می‌کند. + +حال بیایید به متد `sort_by_key` از کتابخانه استاندارد که روی sliceها تعریف شده است نگاهی بیندازیم تا ببینیم چه تفاوتی با `unwrap_or_else` دارد و چرا `sort_by_key` به جای `FnOnce` از `FnMut` به‌عنوان محدودیت trait استفاده می‌کند. این closure یک آرگومان دریافت می‌کند که به‌صورت رفرنسی به آیتم جاری در slice است، و مقداری از نوع `K` برمی‌گرداند که قابل مرتب‌سازی باشد. این تابع زمانی مفید است که بخواهید یک slice را بر اساس ویژگی خاصی از هر آیتم مرتب کنید. در لیستینگ 13-7، ما یک لیست از نمونه‌های `Rectangle` داریم و از `sort_by_key` برای مرتب‌سازی آن‌ها بر اساس ویژگی `width` از کم به زیاد استفاده می‌کنیم. + +اکنون بیایید به متد استاندارد کتابخانه `sort_by_key` که روی برش‌ها (slices) تعریف شده است نگاهی بیندازیم تا ببینیم چگونه با `unwrap_or_else` متفاوت است و چرا `sort_by_key` به جای `FnOnce` از `FnMut` برای محدودیت صفت استفاده می‌کند. closure یک آرگومان به شکل یک ارجاع به آیتم جاری در برشی که در نظر گرفته می‌شود می‌گیرد و یک مقدار از نوع `K` را که قابل مرتب‌سازی است بازمی‌گرداند. این تابع زمانی مفید است که بخواهید یک برش را بر اساس ویژگی خاصی از هر آیتم مرتب کنید. در لیست 13-7، ما لیستی از نمونه‌های `Rectangle` داریم و از `sort_by_key` برای مرتب کردن آن‌ها بر اساس ویژگی `width` از کم به زیاد استفاده می‌کنیم: + + ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-07/src/main.rs}} @@ -357,22 +226,17 @@ to order them by their `width` attribute from low to high. -This code prints: +این کد خروجی زیر را چاپ می‌کند: ```console {{#include ../listings/ch13-functional-features/listing-13-07/output.txt}} ``` -The reason `sort_by_key` is defined to take an `FnMut` closure is that it calls -the closure multiple times: once for each item in the slice. The closure `|r| -r.width` doesn’t capture, mutate, or move anything out from its environment, so -it meets the trait bound requirements. +دلیل اینکه `sort_by_key` به گونه‌ای تعریف شده که یک closure `FnMut` بگیرد این است که closure را چندین بار فراخوانی می‌کند: یک بار برای هر آیتم در برش. closure `|r| r.width` چیزی را از محیط خود نمی‌گیرد، تغییر نمی‌دهد یا منتقل نمی‌کند، بنابراین با الزامات محدودیت صفت مطابقت دارد. -In contrast, Listing 13-8 shows an example of a closure that implements just -the `FnOnce` trait, because it moves a value out of the environment. The -compiler won’t let us use this closure with `sort_by_key`. +در مقابل، لیست 13-8 مثالی از closureی را نشان می‌دهد که فقط صفت `FnOnce` را پیاده‌سازی می‌کند، زیرا مقداری را از محیط منتقل می‌کند. کامپایلر اجازه نمی‌دهد از این closure با `sort_by_key` استفاده کنیم: -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch13-functional-features/listing-13-08/src/main.rs}} @@ -380,31 +244,15 @@ compiler won’t let us use this closure with `sort_by_key`. -This is a contrived, convoluted way (that doesn’t work) to try and count the -number of times `sort_by_key` calls the closure when sorting `list`. This code -attempts to do this counting by pushing `value`—a `String` from the closure’s -environment—into the `sort_operations` vector. The closure captures `value` and -then moves `value` out of the closure by transferring ownership of `value` to -the `sort_operations` vector. This closure can be called once; trying to call -it a second time wouldn’t work because `value` would no longer be in the -environment to be pushed into `sort_operations` again! Therefore, this closure -only implements `FnOnce`. When we try to compile this code, we get this error -that `value` can’t be moved out of the closure because the closure must -implement `FnMut`: +این یک روش مصنوعی و پیچیده (که کار نمی‌کند) برای تلاش در شمارش تعداد دفعاتی است که `sort_by_key` closure را هنگام مرتب کردن `list` فراخوانی می‌کند. این کد سعی می‌کند این شمارش را با افزودن `value`—یک `String` از محیط closure—به وکتور `sort_operations` انجام دهد. closure، `value` را می‌گیرد و سپس با انتقال مالکیت `value` به وکتور `sort_operations`، `value` را از closure منتقل می‌کند. این closure فقط یک بار می‌تواند فراخوانی شود؛ تلاش برای فراخوانی آن برای بار دوم کار نمی‌کند زیرا `value` دیگر در محیط وجود ندارد که دوباره به `sort_operations` اضافه شود! بنابراین، این closure فقط صفت `FnOnce` را پیاده‌سازی می‌کند. وقتی سعی می‌کنیم این کد را کامپایل کنیم، این خطا دریافت می‌شود که `value` نمی‌تواند از closure منتقل شود، زیرا closure باید `FnMut` را پیاده‌سازی کند: ```console {{#include ../listings/ch13-functional-features/listing-13-08/output.txt}} ``` -The error points to the line in the closure body that moves `value` out of the -environment. To fix this, we need to change the closure body so that it doesn’t -move values out of the environment. Keeping a counter in the environment and -incrementing its value in the closure body is a more straightforward way to -count the number of times the closure is called. The closure in Listing 13-9 -works with `sort_by_key` because it is only capturing a mutable reference to the -`num_sort_operations` counter and can therefore be called more than once: +این خطا به خطی در بدنه closure اشاره می‌کند که `value` را از محیط منتقل می‌کند. برای رفع این مشکل، باید بدنه closure را تغییر دهیم تا مقادیر را از محیط منتقل نکند. برای شمارش تعداد دفعاتی که closure فراخوانی می‌شود، نگه داشتن یک شمارنده در محیط و افزایش مقدار آن در بدنه closure روشی ساده‌تر برای محاسبه آن است. closure در لیست 13-9 با `sort_by_key` کار می‌کند زیرا فقط یک ارجاع قابل تغییر به شمارنده `num_sort_operations` را می‌گیرد و بنابراین می‌تواند بیش از یک بار فراخوانی شود: -+ ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-09/src/main.rs}} @@ -412,9 +260,6 @@ works with `sort_by_key` because it is only capturing a mutable reference to the -The `Fn` traits are important when defining or using functions or types that -make use of closures. In the next section, we’ll discuss iterators. Many -iterator methods take closure arguments, so keep these closure details in mind -as we continue! +صفات `Fn` هنگام تعریف یا استفاده از توابع یا انواعی که از closureها استفاده می‌کنند، مهم هستند. در بخش بعدی، ما درباره iteratorها بحث خواهیم کرد. بسیاری از متدهای iterator آرگومان‌های closure می‌گیرند، بنابراین این جزئیات closure را هنگام ادامه مطالعه در نظر داشته باشید! -[unwrap-or-else]: ../std/option/enum.Option.html#method.unwrap_or_else +[unwrap-or-else]: https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap_or_else diff --git a/src/ch13-02-iterators.md b/src/ch13-02-iterators.md index 7113cedb35..afe288c800 100644 --- a/src/ch13-02-iterators.md +++ b/src/ch13-02-iterators.md @@ -1,17 +1,10 @@ -## Processing a Series of Items with Iterators +## پردازش یک سری از آیتم‌ها با استفاده از Iteratorها -The iterator pattern allows you to perform some task on a sequence of items in -turn. An iterator is responsible for the logic of iterating over each item and -determining when the sequence has finished. When you use iterators, you don’t -have to reimplement that logic yourself. +الگوی iterator به شما اجازه می‌دهد تا به ترتیب روی یک دنباله از آیتم‌ها کاری انجام دهید. یک iterator مسئول منطق پیمایش هر آیتم و تعیین زمان پایان دنباله است. وقتی از iteratorها استفاده می‌کنید، نیازی به پیاده‌سازی مجدد آن منطق ندارید. -In Rust, iterators are _lazy_, meaning they have no effect until you call -methods that consume the iterator to use it up. For example, the code in -Listing 13-10 creates an iterator over the items in the vector `v1` by calling -the `iter` method defined on `Vec`. This code by itself doesn’t do anything -useful. +در Rust، iteratorها _تنبل_ هستند، به این معنی که تا زمانی که متدهایی که iterator را مصرف می‌کنند فراخوانی نشوند، هیچ اثری ندارند. به عنوان مثال، کد در لیست 13-10 یک iterator را بر روی آیتم‌های وکتور `v1` با فراخوانی متد `iter` که روی `Vec` تعریف شده است، ایجاد می‌کند. این کد به تنهایی هیچ کار مفیدی انجام نمی‌دهد. -+ ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-10/src/main.rs:here}} @@ -19,18 +12,11 @@ useful. -The iterator is stored in the `v1_iter` variable. Once we’ve created an -iterator, we can use it in a variety of ways. In Listing 3-5, we iterated over -an array using a `for` loop to execute some code on each of its items. Under -the hood, this implicitly created and then consumed an iterator, but we glossed -over how exactly that works until now. +پیمایشگر در متغیر `v1_iter` ذخیره شده است. پس از ایجاد یک پیمایشگر، می‌توانیم آن را به روش‌های مختلفی استفاده کنیم. در لیست 3-5، ما با استفاده از یک حلقه `for` بر روی یک آرایه پیمایش کردیم تا کدی را روی هر یک از آیتم‌های آن اجرا کنیم. در پشت صحنه، این عملیات به‌طور ضمنی یک پیمایشگر ایجاد کرده و سپس آن را مصرف می‌کند، اما تا این لحظه به‌طور دقیق توضیح ندادیم که این فرآیند چگونه کار می‌کند. -In the example in Listing 13-11, we separate the creation of the iterator from -the use of the iterator in the `for` loop. When the `for` loop is called using -the iterator in `v1_iter`, each element in the iterator is used in one -iteration of the loop, which prints out each value. +در مثال لیست 13-11، ما ایجاد iterator را از استفاده از آن در حلقه `for` جدا می‌کنیم. وقتی حلقه `for` با استفاده از iterator در `v1_iter` فراخوانی می‌شود، هر عنصر در iterator در یک تکرار از حلقه استفاده می‌شود، که هر مقدار را چاپ می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-11/src/main.rs:here}} @@ -38,21 +24,13 @@ iteration of the loop, which prints out each value. -In languages that don’t have iterators provided by their standard libraries, -you would likely write this same functionality by starting a variable at index -0, using that variable to index into the vector to get a value, and -incrementing the variable value in a loop until it reached the total number of -items in the vector. +در زبان‌هایی که iteratorها توسط کتابخانه استاندارد آن‌ها ارائه نمی‌شوند، احتمالاً همین عملکرد را با شروع یک متغیر در شاخص 0، استفاده از آن متغیر برای شاخص‌گذاری در وکتور برای دریافت یک مقدار و افزایش مقدار متغیر در یک حلقه تا زمانی که به تعداد کل آیتم‌ها در وکتور برسد، می‌نوشتید. -Iterators handle all of that logic for you, cutting down on repetitive code you -could potentially mess up. Iterators give you more flexibility to use the same -logic with many different kinds of sequences, not just data structures you can -index into, like vectors. Let’s examine how iterators do that. +پیمایشگرها تمام این منطق را برای شما مدیریت می‌کنند و باعث کاهش کدی تکراری می‌شوند که ممکن است به‌اشتباه نوشته شود. پیمایشگرها انعطاف‌پذیری بیشتری در اختیار شما قرار می‌دهند تا بتوانید از همان منطق با انواع مختلفی از توالی‌ها استفاده کنید، نه فقط ساختارهای داده‌ای که می‌توان در آن‌ها با اندیس به عناصر دسترسی داشت، مانند `vector`. بیایید بررسی کنیم که پیمایشگرها چگونه این کار را انجام می‌دهند. -### The `Iterator` Trait and the `next` Method +### صفت `Iterator` و متد `next` -All iterators implement a trait named `Iterator` that is defined in the -standard library. The definition of the trait looks like this: +همه iteratorها یک صفت به نام `Iterator` را پیاده‌سازی می‌کنند که در کتابخانه استاندارد تعریف شده است. تعریف این صفت به صورت زیر است: ```rust pub trait Iterator { @@ -60,27 +38,17 @@ pub trait Iterator { fn next(&mut self) -> Option; - // methods with default implementations elided + // متدهایی با پیاده‌سازی پیش‌فرض حذف شده‌اند } ``` -Notice that this definition uses some new syntax: `type Item` and `Self::Item`, -which are defining an _associated type_ with this trait. We’ll talk about -associated types in depth in Chapter 20. For now, all you need to know is that -this code says implementing the `Iterator` trait requires that you also define -an `Item` type, and this `Item` type is used in the return type of the `next` -method. In other words, the `Item` type will be the type returned from the -iterator. +توجه کنید که این تعریف از یک نحو جدید استفاده می‌کند: `type Item` و `Self::Item`، که یک _نوع مرتبط_ را با این صفت تعریف می‌کنند. ما در فصل 20 به طور مفصل درباره انواع مرتبط صحبت خواهیم کرد. فعلاً فقط باید بدانید که این کد می‌گوید پیاده‌سازی صفت `Iterator` نیاز دارد که شما یک نوع `Item` نیز تعریف کنید، و این نوع `Item` در نوع بازگشتی متد `next` استفاده می‌شود. به عبارت دیگر، نوع `Item` همان نوعی خواهد بود که از iterator بازگردانده می‌شود. -The `Iterator` trait only requires implementors to define one method: the -`next` method, which returns one item of the iterator at a time, wrapped in -`Some`, and, when iteration is over, returns `None`. +صفت `Iterator` فقط از پیاده‌کنندگان می‌خواهد یک متد را تعریف کنند: متد `next`، که یک آیتم از iterator را در هر زمان بازمی‌گرداند، که در `Some` بسته‌بندی شده است، و وقتی پیمایش تمام شد، `None` بازمی‌گرداند. -We can call the `next` method on iterators directly; Listing 13-12 demonstrates -what values are returned from repeated calls to `next` on the iterator created -from the vector. +ما می‌توانیم مستقیماً متد `next` را روی iteratorها فراخوانی کنیم؛ لیست 13-12 نشان می‌دهد چه مقادیری از فراخوانی‌های مکرر `next` روی iterator ایجادشده از وکتور بازگردانده می‌شود. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch13-functional-features/listing-13-12/src/lib.rs:here}} @@ -88,37 +56,17 @@ from the vector. -Note that we needed to make `v1_iter` mutable: calling the `next` method on an -iterator changes internal state that the iterator uses to keep track of where -it is in the sequence. In other words, this code _consumes_, or uses up, the -iterator. Each call to `next` eats up an item from the iterator. We didn’t need -to make `v1_iter` mutable when we used a `for` loop because the loop took -ownership of `v1_iter` and made it mutable behind the scenes. - -Also note that the values we get from the calls to `next` are immutable -references to the values in the vector. The `iter` method produces an iterator -over immutable references. If we want to create an iterator that takes -ownership of `v1` and returns owned values, we can call `into_iter` instead of -`iter`. Similarly, if we want to iterate over mutable references, we can call -`iter_mut` instead of `iter`. - -### Methods That Consume the Iterator - -The `Iterator` trait has a number of different methods with default -implementations provided by the standard library; you can find out about these -methods by looking in the standard library API documentation for the `Iterator` -trait. Some of these methods call the `next` method in their definition, which -is why you’re required to implement the `next` method when implementing the -`Iterator` trait. - -Methods that call `next` are called _consuming adapters_, because calling them -uses up the iterator. One example is the `sum` method, which takes ownership of -the iterator and iterates through the items by repeatedly calling `next`, thus -consuming the iterator. As it iterates through, it adds each item to a running -total and returns the total when iteration is complete. Listing 13-13 has a -test illustrating a use of the `sum` method. - -+توجه کنید که ما نیاز داشتیم `v1_iter` را قابل تغییر (mutable) کنیم: فراخوانی متد `next` روی یک iterator، وضعیت داخلی را تغییر می‌دهد که iterator از آن برای ردیابی موقعیت خود در دنباله استفاده می‌کند. به عبارت دیگر، این کد iterator را _مصرف_ می‌کند یا از بین می‌برد. هر فراخوانی به `next` یک آیتم از iterator را مصرف می‌کند. نیازی نبود `v1_iter` را هنگام استفاده از یک حلقه `for` قابل تغییر کنیم، زیرا حلقه مالکیت `v1_iter` را به عهده گرفت و به طور پنهانی آن را قابل تغییر کرد. + +همچنین توجه کنید که مقادیری که از فراخوانی‌های `next` دریافت می‌کنیم، ارجاعات غیرقابل تغییر به مقادیر موجود در وکتور هستند. متد `iter` یک iterator روی ارجاعات غیرقابل تغییر تولید می‌کند. اگر بخواهیم یک iterator ایجاد کنیم که مالکیت `v1` را بگیرد و مقادیر مالک‌شده را بازگرداند، می‌توانیم به جای `iter`، `into_iter` را فراخوانی کنیم. به همین ترتیب، اگر بخواهیم روی ارجاعات قابل تغییر پیمایش کنیم، می‌توانیم به جای `iter`، `iter_mut` را فراخوانی کنیم. + +### متدهایی که Iterator را مصرف می‌کنند + +صفت `Iterator` تعداد زیادی متد مختلف با پیاده‌سازی‌های پیش‌فرض ارائه‌شده توسط کتابخانه استاندارد دارد؛ می‌توانید درباره این متدها با نگاه کردن به مستندات API کتابخانه استاندارد برای صفت `Iterator` اطلاعات بیشتری کسب کنید. برخی از این متدها در تعریف خود متد `next` را فراخوانی می‌کنند، به همین دلیل است که شما باید متد `next` را هنگام پیاده‌سازی صفت `Iterator` تعریف کنید. + +متدهایی که `next` را فراخوانی می‌کنند، _تطبیق‌دهنده‌های مصرفی_ نامیده می‌شوند، زیرا فراخوانی آن‌ها iterator را مصرف می‌کند. یک مثال، متد `sum` است که مالکیت iterator را به عهده می‌گیرد و با فراخوانی مکرر `next`، از میان آیتم‌ها عبور می‌کند، بنابراین iterator را مصرف می‌کند. هنگام عبور، هر آیتم را به یک مجموع در حال اجرا اضافه می‌کند و وقتی پیمایش کامل شد، مجموع را بازمی‌گرداند. لیست 13-13 یک تست را نشان می‌دهد که استفاده از متد `sum` را نشان می‌دهد: + + ```rust,noplayground {{#rustdoc_include ../listings/ch13-functional-features/listing-13-13/src/lib.rs:here}} @@ -126,22 +74,15 @@ test illustrating a use of the `sum` method. -We aren’t allowed to use `v1_iter` after the call to `sum` because `sum` takes -ownership of the iterator we call it on. +ما اجازه نداریم پس از فراخوانی متد `sum` از `v1_iter` استفاده کنیم، زیرا `sum` مالکیت iteratorی که روی آن فراخوانی می‌شود را به عهده می‌گیرد. -### Methods That Produce Other Iterators +### متدهایی که Iteratorهای دیگری تولید می‌کنند -_Iterator adapters_ are methods defined on the `Iterator` trait that don’t -consume the iterator. Instead, they produce different iterators by changing -some aspect of the original iterator. +_تطبیق‌دهنده‌های Iterator_ متدهایی هستند که روی صفت `Iterator` تعریف شده‌اند و iterator را مصرف نمی‌کنند. در عوض، آن‌ها با تغییر برخی جنبه‌های iterator اصلی، iteratorهای متفاوتی تولید می‌کنند. -Listing 13-14 shows an example of calling the iterator adapter method `map`, -which takes a closure to call on each item as the items are iterated through. -The `map` method returns a new iterator that produces the modified items. The -closure here creates a new iterator in which each item from the vector will be -incremented by 1. +لیست 13-14 مثالی از فراخوانی متد تطبیق‌دهنده iterator به نام `map` را نشان می‌دهد که یک closure را برای فراخوانی روی هر آیتم هنگام پیمایش از میان آیتم‌ها می‌گیرد. متد `map` یک iterator جدید بازمی‌گرداند که آیتم‌های تغییر یافته را تولید می‌کند. closure در اینجا یک iterator جدید ایجاد می‌کند که در آن هر آیتم از وکتور ۱ واحد افزایش می‌یابد: -+ ```rust,not_desired_behavior {{#rustdoc_include ../listings/ch13-functional-features/listing-13-14/src/main.rs:here}} @@ -149,25 +90,19 @@ incremented by 1. -However, this code produces a warning: +با این حال، این کد یک هشدار تولید می‌کند: ```console {{#include ../listings/ch13-functional-features/listing-13-14/output.txt}} ``` -The code in Listing 13-14 doesn’t do anything; the closure we’ve specified -never gets called. The warning reminds us why: iterator adapters are lazy, and -we need to consume the iterator here. +کد در لیست 13-14 هیچ کاری انجام نمی‌دهد؛ closureی که مشخص کرده‌ایم هرگز فراخوانی نمی‌شود. این هشدار به ما یادآوری می‌کند چرا: تطبیق‌دهنده‌های iterator تنبل هستند و ما باید iterator را در اینجا مصرف کنیم. -To fix this warning and consume the iterator, we’ll use the `collect` method, -which we used with `env::args` in Listing 12-1. This method consumes the -iterator and collects the resultant values into a collection data type. +برای رفع این هشدار و مصرف iterator، از متد `collect` استفاده می‌کنیم، که در فصل 12 با `env::args` در لیست 12-1 استفاده کردیم. این متد iterator را مصرف کرده و مقادیر حاصل را در یک نوع داده مجموعه جمع‌آوری می‌کند. -In Listing 13-15, we collect the results of iterating over the iterator that’s -returned from the call to `map` into a vector. This vector will end up -containing each item from the original vector, incremented by 1. +در لیست 13-15، نتایج پیمایش بر روی iterator بازگردانده‌شده از فراخوانی `map` را در یک وکتور جمع‌آوری می‌کنیم. این وکتور در نهایت شامل هر آیتم از وکتور اصلی با افزایش ۱ واحد خواهد بود. -+ ```rust {{#rustdoc_include ../listings/ch13-functional-features/listing-13-15/src/main.rs:here}} @@ -175,31 +110,19 @@ containing each item from the original vector, incremented by 1. -Because `map` takes a closure, we can specify any operation we want to perform -on each item. This is a great example of how closures let you customize some -behavior while reusing the iteration behavior that the `Iterator` trait -provides. +از آنجا که `map` یک closure می‌گیرد، می‌توانیم هر عملیاتی را که می‌خواهیم روی هر آیتم انجام دهیم مشخص کنیم. این مثال بسیار خوبی است از اینکه چگونه closureها به شما اجازه می‌دهند تا برخی رفتارها را سفارشی کنید در حالی که از رفتار پیمایشی که صفت `Iterator` فراهم می‌کند استفاده مجدد می‌کنید. -You can chain multiple calls to iterator adapters to perform complex actions in -a readable way. But because all iterators are lazy, you have to call one of the -consuming adapter methods to get results from calls to iterator adapters. +می‌توانید چندین فراخوانی به تطبیق‌دهنده‌های iterator را زنجیره کنید تا اقدامات پیچیده‌ای را به شکلی خوانا انجام دهید. اما از آنجا که همه iteratorها تنبل هستند، باید یکی از متدهای تطبیق‌دهنده مصرفی را برای گرفتن نتایج از فراخوانی تطبیق‌دهنده‌های iterator فراخوانی کنید. -### Using Closures That Capture Their Environment +### استفاده از closureهایی که محیط خود را می‌گیرند -Many iterator adapters take closures as arguments, and commonly the closures -we’ll specify as arguments to iterator adapters will be closures that capture -their environment. +بسیاری از تطبیق‌دهنده‌های iterator closureها را به عنوان آرگومان می‌پذیرند، و معمولاً closureهایی که به عنوان آرگومان به تطبیق‌دهنده‌های iterator مشخص می‌کنیم closureهایی هستند که محیط خود را می‌گیرند. -For this example, we’ll use the `filter` method that takes a closure. The -closure gets an item from the iterator and returns a `bool`. If the closure -returns `true`, the value will be included in the iteration produced by -`filter`. If the closure returns `false`, the value won’t be included. +برای این مثال، از متد `filter` استفاده خواهیم کرد که یک closure می‌گیرد. closure یک آیتم از iterator دریافت کرده و یک مقدار `bool` بازمی‌گرداند. اگر closure مقدار `true` بازگرداند، مقدار در پیمایش تولید شده توسط `filter` گنجانده می‌شود. اگر closure مقدار `false` بازگرداند، مقدار گنجانده نخواهد شد. -In Listing 13-16, we use `filter` with a closure that captures the `shoe_size` -variable from its environment to iterate over a collection of `Shoe` struct -instances. It will return only shoes that are the specified size. +در لیست 13-16، از `filter` با یک closure که متغیر `shoe_size` را از محیط خود می‌گیرد استفاده می‌کنیم تا روی مجموعه‌ای از نمونه‌های ساختار `Shoe` پیمایش کنیم. این متد فقط کفش‌هایی را که اندازه مشخص شده دارند بازمی‌گرداند. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch13-functional-features/listing-13-16/src/lib.rs}} @@ -207,19 +130,10 @@ instances. It will return only shoes that are the specified size. -The `shoes_in_size` function takes ownership of a vector of shoes and a shoe -size as parameters. It returns a vector containing only shoes of the specified -size. +تابع `shoes_in_size` مالکیت یک وکتور از کفش‌ها و یک اندازه کفش را به عنوان پارامتر می‌گیرد. این تابع یک وکتور بازمی‌گرداند که فقط شامل کفش‌هایی با اندازه مشخص شده است. -In the body of `shoes_in_size`, we call `into_iter` to create an iterator -that takes ownership of the vector. Then we call `filter` to adapt that -iterator into a new iterator that only contains elements for which the closure -returns `true`. +در بدنه `shoes_in_size`، ما `into_iter` را فراخوانی می‌کنیم تا یک iterator ایجاد کنیم که مالکیت وکتور را می‌گیرد. سپس `filter` را فراخوانی می‌کنیم تا آن iterator را به یک iterator جدید تبدیل کنیم که فقط شامل عناصری است که closure برای آن‌ها مقدار `true` بازمی‌گرداند. -The closure captures the `shoe_size` parameter from the environment and -compares the value with each shoe’s size, keeping only shoes of the size -specified. Finally, calling `collect` gathers the values returned by the -adapted iterator into a vector that’s returned by the function. +closure پارامتر `shoe_size` را از محیط می‌گیرد و مقدار آن را با اندازه هر کفش مقایسه می‌کند و فقط کفش‌هایی با اندازه مشخص شده را نگه می‌دارد. در نهایت، با فراخوانی `collect` مقادیر بازگردانده‌شده توسط iterator تطبیق‌یافته در یک وکتور جمع‌آوری می‌شوند که توسط تابع بازگردانده می‌شود. -The test shows that when we call `shoes_in_size`, we get back only shoes -that have the same size as the value we specified. +تست نشان می‌دهد که وقتی `shoes_in_size` را فراخوانی می‌کنیم، فقط کفش‌هایی را دریافت می‌کنیم که اندازه آن‌ها با مقداری که مشخص کرده‌ایم یکسان است. diff --git a/src/ch13-03-improving-our-io-project.md b/src/ch13-03-improving-our-io-project.md index 69811a6cd4..bad7e8c2d8 100644 --- a/src/ch13-03-improving-our-io-project.md +++ b/src/ch13-03-improving-our-io-project.md @@ -1,19 +1,12 @@ -## Improving Our I/O Project +## بهبود پروژه I/O -With this new knowledge about iterators, we can improve the I/O project in -Chapter 12 by using iterators to make places in the code clearer and more -concise. Let’s look at how iterators can improve our implementation of the -`Config::build` function and the `search` function. +با این دانش جدید درباره iteratorها، می‌توانیم پروژه I/O در فصل ۱۲ را با استفاده از iteratorها بهبود بخشیم تا بخش‌هایی از کد واضح‌تر و مختصرتر شوند. بیایید ببینیم چگونه iteratorها می‌توانند پیاده‌سازی تابع `Config::build` و تابع `search` را بهبود دهند. -### Removing a `clone` Using an Iterator +### حذف یک `clone` با استفاده از یک Iterator -In Listing 12-6, we added code that took a slice of `String` values and created -an instance of the `Config` struct by indexing into the slice and cloning the -values, allowing the `Config` struct to own those values. In Listing 13-17, -we’ve reproduced the implementation of the `Config::build` function as it was -in Listing 12-23. +در لیست 12-6، کدی اضافه کردیم که یک برش از مقادیر `String` را گرفته و یک نمونه از ساختار `Config` ایجاد می‌کرد. این کار با شاخص‌گذاری در برش و کلون کردن مقادیر انجام شد تا ساختار `Config` مالک آن مقادیر شود. در لیست 13-17، پیاده‌سازی تابع `Config::build` را همانطور که در لیست 12-23 بود بازتولید کرده‌ایم: -+ ```rust,ignore {{#rustdoc_include ../listings/ch13-functional-features/listing-12-23-reproduced/src/main.rs:ch13}} @@ -21,39 +14,27 @@ in Listing 12-23. -At the time, we said not to worry about the inefficient `clone` calls because -we would remove them in the future. Well, that time is now! +در آن زمان گفتیم که نگران تماس‌های ناکارآمد `clone` نباشید زیرا در آینده آن‌ها را حذف خواهیم کرد. خب، اکنون زمان آن فرا رسیده است! -We needed `clone` here because we have a slice with `String` elements in the -parameter `args`, but the `build` function doesn’t own `args`. To return -ownership of a `Config` instance, we had to clone the values from the `query` -and `file_path` fields of `Config` so the `Config` instance can own its values. +ما در اینجا به `clone` نیاز داشتیم زیرا در پارامتر `args` یک برش با عناصر `String` داریم، اما تابع `build` مالک `args` نیست. برای بازگرداندن مالکیت یک نمونه `Config`، مجبور بودیم مقادیر فیلدهای `query` و `file_path` را از `Config` کلون کنیم تا نمونه `Config` بتواند مالک مقادیرش باشد. -With our new knowledge about iterators, we can change the `build` function to -take ownership of an iterator as its argument instead of borrowing a slice. -We’ll use the iterator functionality instead of the code that checks the length -of the slice and indexes into specific locations. This will clarify what the -`Config::build` function is doing because the iterator will access the values. +با دانش جدیدمان درباره iteratorها، می‌توانیم تابع `build` را تغییر دهیم تا مالکیت یک iterator را به عنوان آرگومان خود بگیرد، به جای اینکه یک برش را قرض بگیرد. ما از قابلیت‌های iterator به جای کدی که طول برش را بررسی می‌کند و به مکان‌های خاص شاخص می‌زند، استفاده خواهیم کرد. این کار مشخص می‌کند که تابع `Config::build` چه کاری انجام می‌دهد زیرا iterator به مقادیر دسترسی پیدا خواهد کرد. -Once `Config::build` takes ownership of the iterator and stops using indexing -operations that borrow, we can move the `String` values from the iterator into -`Config` rather than calling `clone` and making a new allocation. +زمانی که `Config::build` مالکیت iterator را به دست آورد و استفاده از عملیات شاخص‌گذاری که قرض می‌گیرند را متوقف کرد، می‌توانیم مقادیر `String` را از iterator به `Config` منتقل کنیم به جای اینکه `clone` را فراخوانی کنیم و تخصیص جدیدی ایجاد کنیم. -#### Using the Returned Iterator Directly +#### استفاده مستقیم از Iterator بازگردانده‌شده -Open your I/O project’s _src/main.rs_ file, which should look like this: +فایل _src/main.rs_ پروژه I/O خود را باز کنید، که باید به این شکل باشد: -Filename: src/main.rs +نام فایل: src/main.rs ```rust,ignore {{#rustdoc_include ../listings/ch13-functional-features/listing-12-24-reproduced/src/main.rs:ch13}} ``` -We’ll first change the start of the `main` function that we had in Listing -12-24 to the code in Listing 13-18, which this time uses an iterator. This -won’t compile until we update `Config::build` as well. +ابتدا شروع تابع `main` که در لیست 12-24 داشتیم را به کدی که در لیست 13-18 است تغییر می‌دهیم، که این بار از یک iterator استفاده می‌کند. این کد تا زمانی که `Config::build` را نیز به‌روزرسانی کنیم، کامپایل نخواهد شد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch13-functional-features/listing-13-18/src/main.rs:here}} @@ -61,16 +42,11 @@ won’t compile until we update `Config::build` as well. -The `env::args` function returns an iterator! Rather than collecting the -iterator values into a vector and then passing a slice to `Config::build`, now -we’re passing ownership of the iterator returned from `env::args` to -`Config::build` directly. +تابع `env::args` یک iterator بازمی‌گرداند! به جای جمع‌آوری مقادیر iterator در یک وکتور و سپس ارسال یک برش به `Config::build`، اکنون ما مالکیت iterator بازگردانده‌شده از `env::args` را مستقیماً به `Config::build` ارسال می‌کنیم. -Next, we need to update the definition of `Config::build`. Let’s change the -signature of `Config::build` to look like Listing 13-19. This still won’t -compile, because we need to update the function body. +سپس باید تعریف تابع `Config::build` را به‌روزرسانی کنیم. در فایل _src/lib.rs_ پروژه I/O خود، امضای تابع `Config::build` را به شکلی که در لیست 13-19 نشان داده شده تغییر دهید. این کد هنوز کامپایل نخواهد شد زیرا باید بدنه تابع را نیز به‌روزرسانی کنیم. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch13-functional-features/listing-13-19/src/main.rs:here}} @@ -78,28 +54,17 @@ compile, because we need to update the function body. -The standard library documentation for the `env::args` function shows that the -type of the iterator it returns is `std::env::Args`, and that type implements -the `Iterator` trait and returns `String` values. +مستندات کتابخانه استاندارد برای تابع `env::args` نشان می‌دهد که نوع iterator بازگردانده‌شده `std::env::Args` است، و این نوع صفت `Iterator` را پیاده‌سازی کرده و مقادیر `String` بازمی‌گرداند. -We’ve updated the signature of the `Config::build` function so the parameter -`args` has a generic type with the trait bounds `impl Iterator` -instead of `&[String]`. This usage of the `impl Trait` syntax we discussed in -the [“Traits as Parameters”][impl-trait] section of Chapter 10 -means that `args` can be any type that implements the `Iterator` trait and -returns `String` items. +ما امضای تابع `Config::build` را به‌روزرسانی کرده‌ایم تا پارامتر `args` یک نوع جنریک با محدودیت‌های صفت `impl Iterator` باشد به جای `&[String]`. این استفاده از نحو `impl Trait` که در بخش [“Traits به عنوان پارامترها”][impl-trait] فصل 10 بحث شد، به این معناست که `args` می‌تواند هر نوعی باشد که صفت `Iterator` را پیاده‌سازی کرده و آیتم‌های `String` بازمی‌گرداند. -Because we’re taking ownership of `args` and we’ll be mutating `args` by -iterating over it, we can add the `mut` keyword into the specification of the -`args` parameter to make it mutable. +از آنجا که مالکیت `args` را به دست می‌آوریم و با پیمایش در آن، `args` را تغییر خواهیم داد، می‌توانیم کلمه کلیدی `mut` را به مشخصات پارامتر `args` اضافه کنیم تا آن را قابل تغییر کنیم. -#### Using `Iterator` Trait Methods Instead of Indexing +#### استفاده از متدهای صفت `Iterator` به جای شاخص‌گذاری -Next, we’ll fix the body of `Config::build`. Because `args` implements the -`Iterator` trait, we know we can call the `next` method on it! Listing 13-20 -updates the code from Listing 12-23 to use the `next` method. +سپس بدنه تابع `Config::build` را اصلاح می‌کنیم. از آنجا که `args` صفت `Iterator` را پیاده‌سازی کرده است، می‌دانیم که می‌توانیم متد `next` را روی آن فراخوانی کنیم! لیست 13-20 کد لیست 12-23 را برای استفاده از متد `next` به‌روزرسانی می‌کند: -+ ```rust,ignore,noplayground {{#rustdoc_include ../listings/ch13-functional-features/listing-13-20/src/main.rs:here}} @@ -107,20 +72,13 @@ updates the code from Listing 12-23 to use the `next` method. -Remember that the first value in the return value of `env::args` is the name of -the program. We want to ignore that and get to the next value, so first we call -`next` and do nothing with the return value. Then we call `next` to get the -value we want to put in the `query` field of `Config`. If `next` returns `Some`, -we use a `match` to extract the value. If it returns `None`, it means not enough -arguments were given and we return early with an `Err` value. We do the same -thing for the `file_path` value. +به یاد داشته باشید که اولین مقدار در مقدار بازگردانده‌شده از `env::args` نام برنامه است. ما می‌خواهیم آن را نادیده بگیریم و به مقدار بعدی برسیم، بنابراین ابتدا `next` را فراخوانی می‌کنیم و هیچ کاری با مقدار بازگشتی انجام نمی‌دهیم. سپس، `next` را فراخوانی می‌کنیم تا مقداری که می‌خواهیم در فیلد `query` از `Config` قرار دهیم را دریافت کنیم. اگر `next` یک `Some` بازگرداند، از یک `match` برای استخراج مقدار استفاده می‌کنیم. اگر `None` بازگرداند، به این معنی است که آرگومان‌های کافی ارائه نشده‌اند و با مقدار `Err` زودتر بازمی‌گردیم. همین کار را برای مقدار `file_path` انجام می‌دهیم. -### Making Code Clearer with Iterator Adapters +### واضح‌تر کردن کد با تطبیق‌دهنده‌های Iterator -We can also take advantage of iterators in the `search` function in our I/O -project, which is reproduced here in Listing 13-21 as it was in Listing 12-19. +ما همچنین می‌توانیم از iteratorها در تابع `search` پروژه I/O خود بهره ببریم. این تابع در لیست 13-21 به همان شکلی که در لیست 12-19 بود بازتولید شده است: -+ ```rust,ignore {{#rustdoc_include ../listings/ch12-an-io-project/listing-12-19/src/lib.rs:ch13}} @@ -128,14 +86,9 @@ project, which is reproduced here in Listing 13-21 as it was in Listing 12-19. -We can write this code in a more concise way using iterator adapter methods. -Doing so also lets us avoid having a mutable intermediate `results` vector. The -functional programming style prefers to minimize the amount of mutable state to -make code clearer. Removing the mutable state might enable a future enhancement -to make searching happen in parallel because we wouldn’t have to manage -concurrent access to the `results` vector. Listing 13-22 shows this change. +ما می‌توانیم این کد را با استفاده از متدهای تطبیق‌دهنده iterator به شکلی مختصرتر بنویسیم. این کار همچنین به ما اجازه می‌دهد که از داشتن یک وکتور میانی قابل تغییر به نام `results` اجتناب کنیم. سبک برنامه‌نویسی تابعی ترجیح می‌دهد مقدار حالت‌های قابل تغییر را به حداقل برساند تا کد واضح‌تر شود. حذف حالت قابل تغییر ممکن است امکان ارتقاء آینده را فراهم کند تا جستجو به صورت موازی انجام شود، زیرا نیازی به مدیریت دسترسی همزمان به وکتور `results` نخواهیم داشت. لیست 13-22 این تغییر را نشان می‌دهد: -+ ```rust,ignore {{#rustdoc_include ../listings/ch13-functional-features/listing-13-22/src/lib.rs:here}} @@ -143,42 +96,16 @@ concurrent access to the `results` vector. Listing 13-22 shows this change. -Recall that the purpose of the `search` function is to return all lines in -`contents` that contain the `query`. Similar to the `filter` example in Listing -13-16, this code uses the `filter` adapter to keep only the lines for which -`line.contains(query)` returns `true`. We then collect the matching lines into -another vector with `collect`. Much simpler! Feel free to make the same change -to use iterator methods in the `search_case_insensitive` function as well. - -For a further improvement, return an iterator from the `search` function by -removing the call to `collect` and changing the return type to `impl -Iterator` so that the function becomes an iterator adapter. -Note that you’ll also need to update the tests! Search through a large file -using your `minigrep` tool before and after making this change to observe the -difference in behavior. Before this change, the program won’t print any results -until it has collected all of the results, but after the change, the results -will be printed as each matching line is found because the `for` loop in the -`run` function is able to take advantage of the laziness of the iterator. - - +به یاد دارید که هدف تابع `search` این است که تمام خطوط موجود در `contents` را که شامل `query` هستند برگرداند. مشابه با مثال `filter` در لیستینگ 13-16، این کد از آداپتور `filter` استفاده می‌کند تا فقط خطوطی را نگه دارد که در آن‌ها `line.contains(query)` مقدار `true` را بازمی‌گرداند. سپس خطوط مطابق را با استفاده از `collect` در یک وکتور جدید جمع‌آوری می‌کنیم. خیلی ساده‌تر! شما می‌توانید همین تغییر را در تابع `search_case_insensitive` نیز اعمال کرده و از متدهای پیمایشگر استفاده کنید. + +برای بهبود بیشتر، مقدار بازگشتی تابع `search` را به‌جای وکتور، یک پیمایشگر قرار دهید؛ با حذف فراخوانی `collect` و تغییر نوع بازگشتی به `impl Iterator`، این تابع به یک آداپتور پیمایشگر تبدیل می‌شود. توجه داشته باشید که باید تست‌ها را نیز مطابق این تغییر به‌روزرسانی کنید! یک فایل بزرگ را با ابزار `minigrep` خود، قبل و بعد از این تغییر جست‌وجو کنید تا تفاوت رفتار را مشاهده نمایید. قبل از این تغییر، برنامه تا زمانی که تمام نتایج جمع‌آوری نشده‌اند چیزی چاپ نمی‌کند، اما پس از این تغییر، نتایج به‌محض یافتن هر خط مطابق چاپ می‌شوند، زیرا حلقه `for` در تابع `run` می‌تواند از ویژگی تنبلی پیمایشگر استفاده کند. -### Choosing Between Loops and Iterators - -The next logical question is which style you should choose in your own code and -why: the original implementation in Listing 13-21 or the version using -iterators in Listing 13-22 (assuming we’re collecting all the results before -returning them rather than returning the iterator). Most Rust programmers -prefer to use the iterator style. It’s a bit tougher to get the hang of at -first, but once you get a feel for the various iterator adapters and what they -do, iterators can be easier to understand. Instead of fiddling with the various -bits of looping and building new vectors, the code focuses on the high-level -objective of the loop. This abstracts away some of the commonplace code so it’s -easier to see the concepts that are unique to this code, such as the filtering -condition each element in the iterator must pass. - -But are the two implementations truly equivalent? The intuitive assumption -might be that the lower-level loop will be faster. Let’s talk about performance. +### انتخاب بین حلقه‌ها و پیمایشگرها + +سؤال منطقی بعدی این است که کدام سبک را در کد خود انتخاب کنیم و چرا: پیاده‌سازی اولیه در لیستینگ 13-21 یا نسخه‌ای که از پیمایشگرها استفاده می‌کند در لیستینگ 13-22 (با فرض اینکه تمام نتایج را پیش از بازگرداندن جمع‌آوری می‌کنیم و نه اینکه خود پیمایشگر را بازگردانیم). بیشتر برنامه‌نویسان Rust ترجیح می‌دهند از سبک پیمایشگر استفاده کنند. در ابتدا ممکن است درک آن کمی دشوارتر باشد، اما زمانی که با آداپتورهای مختلف پیمایشگر و عملکرد آن‌ها آشنا شدید، کار با آن‌ها آسان‌تر خواهد بود. به جای کلنجار رفتن با بخش‌های مختلف حلقه و ساخت وکتورهای جدید، کد روی هدف سطح بالای حلقه تمرکز می‌کند. این امر باعث پنهان شدن بخشی از کدهای تکراری شده و فهم مفاهیم خاص این کد (مانند شرط فیلتر شدن هر عنصر پیمایشگر) را آسان‌تر می‌کند. + +اما آیا این دو پیاده‌سازی واقعاً معادل هم هستند؟ فرض شهودی ممکن است این باشد که حلقه سطح پایین‌تر سریع‌تر است. بیایید درباره عملکرد صحبت کنیم. [impl-trait]: ch10-02-traits.html#traits-as-parameters diff --git a/src/ch13-04-performance.md b/src/ch13-04-performance.md index 2cb9710db4..7ce05d934b 100644 --- a/src/ch13-04-performance.md +++ b/src/ch13-04-performance.md @@ -1,53 +1,26 @@ -## Comparing Performance: Loops vs. Iterators +## مقایسه عملکرد: حلقه‌ها در برابر Iteratorها -To determine whether to use loops or iterators, you need to know which -implementation is faster: the version of the `search` function with an explicit -`for` loop or the version with iterators. +برای تعیین اینکه از حلقه‌ها یا iteratorها استفاده کنید، باید بدانید کدام پیاده‌سازی سریع‌تر است: نسخه تابع `search` با حلقه صریح `for` یا نسخه با iteratorها. -We ran a benchmark by loading the entire contents of _The Adventures of -Sherlock Holmes_ by Sir Arthur Conan Doyle into a `String` and looking for the -word _the_ in the contents. Here are the results of the benchmark on the -version of `search` using the `for` loop and the version using iterators: +ما یک بنچمارک اجرا کردیم که در آن تمام محتوای کتاب _The Adventures of Sherlock Holmes_ اثر سر آرتور کانن دویل را در یک `String` بارگذاری کردیم و به دنبال کلمه _the_ در محتوا گشتیم. نتایج بنچمارک برای نسخه `search` با استفاده از حلقه `for` و نسخه با iteratorها به شرح زیر است: ```text test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700) test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200) ``` -The two implementations have similar performance! We won’t explain the -benchmark code here because the point is not to prove that the two versions -are equivalent but to get a general sense of how these two implementations -compare performance-wise. - -For a more comprehensive benchmark, you should check using various texts of -various sizes as the `contents`, different words and words of different lengths -as the `query`, and all kinds of other variations. The point is this: -iterators, although a high-level abstraction, get compiled down to roughly the -same code as if you’d written the lower-level code yourself. Iterators are one -of Rust’s _zero-cost abstractions_, by which we mean that using the abstraction -imposes no additional runtime overhead. This is analogous to how Bjarne -Stroustrup, the original designer and implementor of C++, defines -_zero-overhead_ in “Foundations of C++” (2012): - -> In general, C++ implementations obey the zero-overhead principle: What you -> don’t use, you don’t pay for. And further: What you do use, you couldn’t hand -> code any better. - -In many cases, Rust code using iterators compiles to the same assembly you’d -write by hand. Optimizations such as loop unrolling and eliminating bounds -checking on array access apply and make the resultant code extremely efficient. -Now that you know this, you can use iterators and closures without fear! They -make code seem like it’s higher level but don’t impose a runtime performance -penalty for doing so. - -## Summary - -Closures and iterators are Rust features inspired by functional programming -language ideas. They contribute to Rust’s capability to clearly express -high-level ideas at low-level performance. The implementations of closures and -iterators are such that runtime performance is not affected. This is part of -Rust’s goal to strive to provide zero-cost abstractions. - -Now that we’ve improved the expressiveness of our I/O project, let’s look at -some more features of `cargo` that will help us share the project with the -world. +دو پیاده‌سازی عملکرد مشابهی دارند! ما کد بنچمارک (benchmark) را اینجا توضیح نمی‌دهیم، زیرا هدف این نیست که ثابت کنیم این دو نسخه معادل هستند، بلکه هدف این است که به یک درک کلی از نحوه مقایسه عملکردی این دو پیاده‌سازی برسیم. + +برای یک بنچمارک جامع‌تر، باید از متن‌های مختلف با اندازه‌های گوناگون به‌عنوان `contents`، کلمات مختلف و کلماتی با طول‌های متفاوت به‌عنوان `query`، و انواع دیگری از تغییرات استفاده کنید. نکته این است: iteratorها، اگرچه یک انتزاع سطح بالا هستند، به کدی که تقریباً همان سطح پایینی دارد کامپایل می‌شوند، انگار خودتان کد سطح پایین را نوشته باشید. iteratorها یکی از _انتزاع‌های بدون هزینه_ Rust هستند، به این معنی که استفاده از انتزاع هیچ هزینه اضافی زمان اجرای برنامه را تحمیل نمی‌کند. این موضوع مشابه تعریفی است که بیارنه استراس‌تروپ، طراح و پیاده‌ساز اصلی ++C، در مقاله "Foundations of C++" (2012) برای _بدون هزینه اضافی_ ارائه می‌دهد: + +> به طور کلی، پیاده‌سازی‌های ++C از اصل بدون هزینه اضافی پیروی می‌کنند: چیزی که استفاده نمی‌کنید، هزینه‌ای برای شما ندارد. و علاوه بر این: چیزی که استفاده می‌کنید، نمی‌توانید بهتر از این دستی کدنویسی کنید. + +در بسیاری از موارد، کدی که در Rust با استفاده از پیمایشگرها نوشته می‌شود، به همان کدی در اسمبلی کامپایل می‌شود که اگر دستی می‌نوشتید تولید می‌شد. بهینه‌سازی‌هایی مانند بازگشایی حلقه‌ها (loop unrolling) و حذف بررسی محدوده (bounds checking) در دسترسی به آرایه‌ها اعمال می‌شوند و کد نهایی را بسیار بهینه می‌سازند. اکنون که این را می‌دانید، می‌توانید با خیال راحت از پیمایشگرها و closures استفاده کنید! آن‌ها باعث می‌شوند کد سطح بالاتری به نظر برسد، اما هیچ جریمه‌ای از نظر عملکرد در زمان اجرا به همراه ندارند. + +## خلاصه + +
+closureها و iteratorها ویژگی‌های Rust هستند که از ایده‌های زبان‌های برنامه‌نویسی تابعی الهام گرفته‌اند. آن‌ها به توانایی Rust در بیان واضح ایده‌های سطح بالا با عملکرد سطح پایین کمک می‌کنند. پیاده‌سازی closureها و iteratorها به گونه‌ای است که عملکرد زمان اجرا تحت تأثیر قرار نمی‌گیرد. این بخشی از هدف Rust برای ارائه انتزاع‌های بدون هزینه است. +
+ +اکنون که قابلیت بیان پروژه I/O خود را بهبود داده‌ایم، بیایید نگاهی به برخی ویژگی‌های بیشتر `cargo` بیندازیم که به ما کمک می‌کنند پروژه را با دنیا به اشتراک بگذاریم. diff --git a/src/ch14-00-more-about-cargo.md b/src/ch14-00-more-about-cargo.md index 4d99fedf8c..bc52181aad 100644 --- a/src/ch14-00-more-about-cargo.md +++ b/src/ch14-00-more-about-cargo.md @@ -1,14 +1,16 @@ -# More About Cargo and Crates.io +# اطلاعات بیشتر درباره Cargo و Crates.io -So far, we’ve used only the most basic features of Cargo to build, run, and -test our code, but it can do a lot more. In this chapter, we’ll discuss some of -its other, more advanced features to show you how to do the following: +تا کنون ما فقط از ویژگی‌های ابتدایی Cargo برای ساخت، اجرا و تست کد خود استفاده کرده‌ایم، اما Cargo قابلیت‌های بیشتری دارد. در این فصل، برخی از ویژگی‌های پیشرفته‌تر آن را بررسی می‌کنیم تا نشان دهیم چگونه می‌توانید کارهای زیر را انجام دهید: -- Customize your build through release profiles -- Publish libraries on [crates.io](https://crates.io/) -- Organize large projects with workspaces -- Install binaries from [crates.io](https://crates.io/) -- Extend Cargo using custom commands +
+
    +
  • شخصی‌سازی فرآیند ساخت از طریق پروفایل‌های نسخه انتشار
  • +
  • انتشار کتابخانه‌ها در crates.io
  • +
  • سازماندهی پروژه‌های بزرگ با استفاده از Workspaces
  • +
  • نصب باینری‌ها از crates.io
  • +
  • گسترش قابلیت‌های Cargo با استفاده از دستورات سفارشی
  • +
+
-Cargo can do even more than the functionality we cover in this chapter, so for -a full explanation of all its features, see [its documentation](https://doc.rust-lang.org/cargo/). + +Cargo می‌تواند بسیار بیشتر از آنچه در این فصل پوشش می‌دهیم انجام دهد، بنابراین برای توضیحات کامل درباره تمام قابلیت‌های آن، به [مستندات آن](https://doc.rust-lang.org/cargo/) مراجعه کنید. diff --git a/src/ch14-01-release-profiles.md b/src/ch14-01-release-profiles.md index 20c20fce6e..22ffda9d56 100644 --- a/src/ch14-01-release-profiles.md +++ b/src/ch14-01-release-profiles.md @@ -1,23 +1,10 @@ -## Customizing Builds with Release Profiles +## شخصی‌سازی فرآیند ساخت با استفاده از پروفایل‌های نسخه انتشار -In Rust, _release profiles_ are predefined and customizable profiles with -different configurations that allow a programmer to have more control over -various options for compiling code. Each profile is configured independently of -the others. +در Rust، _پروفایل‌های نسخه انتشار_ پروفایل‌هایی از پیش تعریف‌شده و قابل شخصی‌سازی هستند که با پیکربندی‌های مختلف به برنامه‌نویس اجازه می‌دهند کنترل بیشتری روی گزینه‌های مختلف کامپایل کد داشته باشد. هر پروفایل به صورت مستقل از دیگران پیکربندی می‌شود. -Cargo has two main profiles: the `dev` profile Cargo uses when you run `cargo -build`, and the `release` profile Cargo uses when you run `cargo build ---release`. The `dev` profile is defined with good defaults for development, -and the `release` profile has good defaults for release builds. +‌Cargo دو پروفایل اصلی دارد: پروفایل `dev` که Cargo هنگام اجرای دستور `cargo build` از آن استفاده می‌کند، و پروفایل `release` که هنگام اجرای `cargo build --release` استفاده می‌شود. پروفایل `dev` با مقادیر پیش‌فرض مناسبی برای توسعه تعریف شده است، و پروفایل `release` دارای مقادیر پیش‌فرض مناسبی برای ساخت نسخه نهایی (انتشار) است. -These profile names might be familiar from the output of your builds: - - +این نام‌های پروفایل ممکن است از خروجی‌های ساخت شما آشنا به نظر برسند: ```console $ cargo build @@ -26,13 +13,9 @@ $ cargo build --release Finished `release` profile [optimized] target(s) in 0.32s ``` -The `dev` and `release` are these different profiles used by the compiler. +`dev` و `release` پروفایل‌های متفاوتی هستند که توسط کامپایلر استفاده می‌شوند. -Cargo has default settings for each of the profiles that apply when you haven't -explicitly added any `[profile.*]` sections in the project’s _Cargo.toml_ file. -By adding `[profile.*]` sections for any profile you want to customize, you -override any subset of the default settings. For example, here are the default -values for the `opt-level` setting for the `dev` and `release` profiles: +Cargo تنظیمات پیش‌فرضی برای هر یک از پروفایل‌ها دارد که زمانی اعمال می‌شوند که هیچ بخش `[profile.*]` در فایل _Cargo.toml_ پروژه شما به طور صریح اضافه نشده باشد. با افزودن بخش‌های `[profile.*]` برای هر پروفایلی که می‌خواهید سفارشی کنید، می‌توانید هر بخشی از تنظیمات پیش‌فرض را بازنویسی کنید. به عنوان مثال، در اینجا مقادیر پیش‌فرض تنظیم `opt-level` برای پروفایل‌های `dev` و `release` آورده شده است: Filename: Cargo.toml @@ -44,20 +27,7 @@ opt-level = 0 opt-level = 3 ``` -The `opt-level` setting controls the number of optimizations Rust will apply to -your code, with a range of 0 to 3. Applying more optimizations extends -compiling time, so if you’re in development and compiling your code often, -you’ll want fewer optimizations to compile faster even if the resultant code -runs slower. The default `opt-level` for `dev` is therefore `0`. When you’re -ready to release your code, it’s best to spend more time compiling. You’ll only -compile in release mode once, but you’ll run the compiled program many times, -so release mode trades longer compile time for code that runs faster. That is -why the default `opt-level` for the `release` profile is `3`. - -You can override a default setting by adding a different value for it in -_Cargo.toml_. For example, if we want to use optimization level 1 in the -development profile, we can add these two lines to our project’s _Cargo.toml_ -file: +تنظیم `opt-level` تعیین می‌کند که چه مقدار بهینه‌سازی توسط Rust روی کد شما اعمال شود، و این مقدار در بازه‌ای از ۰ تا ۳ قرار دارد. اعمال بهینه‌سازی‌های بیشتر زمان کامپایل را افزایش می‌دهد، بنابراین اگر در حال توسعه هستید و کدتان را مرتباً کامپایل می‌کنید، ترجیح می‌دهید بهینه‌سازی کمتری انجام شود تا سرعت کامپایل بالاتر باشد، حتی اگر اجرای نهایی برنامه کندتر باشد. به همین دلیل، مقدار پیش‌فرض `opt-level` برای پروفایل `dev` برابر با `0` است. زمانی که آماده‌ی انتشار کد خود هستید، بهتر است زمان بیشتری را صرف کامپایل کنید. شما فقط یک‌بار در حالت انتشار کامپایل انجام می‌دهید، اما برنامه‌ی کامپایل‌شده را بارها اجرا خواهید کرد، بنابراین حالت انتشار زمان کامپایل بیشتر را با اجرای سریع‌تر برنامه مبادله می‌کند. به همین دلیل، مقدار پیش‌فرض `opt-level` برای پروفایل `release` برابر با `3` است. Filename: Cargo.toml @@ -66,10 +36,6 @@ file: opt-level = 1 ``` -This code overrides the default setting of `0`. Now when we run `cargo build`, -Cargo will use the defaults for the `dev` profile plus our customization to -`opt-level`. Because we set `opt-level` to `1`, Cargo will apply more -optimizations than the default, but not as many as in a release build. +این کد تنظیم پیش‌فرض `0` را بازنویسی می‌کند. اکنون، زمانی که `cargo build` را اجرا می‌کنیم، Cargo از تنظیمات پیش‌فرض برای پروفایل `dev` به همراه سفارشی‌سازی ما برای `opt-level` استفاده خواهد کرد. از آنجایی که ما مقدار `opt-level` را به `1` تنظیم کرده‌ایم، Cargo بهینه‌سازی‌های بیشتری نسبت به پیش‌فرض اعمال خواهد کرد، اما نه به اندازه یک ساخت در حالت `release`. -For the full list of configuration options and defaults for each profile, see -[Cargo’s documentation](https://doc.rust-lang.org/cargo/reference/profiles.html). +برای مشاهده لیست کامل گزینه‌های پیکربندی و تنظیمات پیش‌فرض برای هر پروفایل، به [مستندات Cargo](https://doc.rust-lang.org/cargo/reference/profiles.html) مراجعه کنید. diff --git a/src/ch14-02-publishing-to-crates-io.md b/src/ch14-02-publishing-to-crates-io.md index 03c4f23199..9e3ee13c0f 100644 --- a/src/ch14-02-publishing-to-crates-io.md +++ b/src/ch14-02-publishing-to-crates-io.md @@ -1,32 +1,16 @@ -## Publishing a Crate to Crates.io +## انتشار یک Crate در Crates.io -We’ve used packages from [crates.io](https://crates.io/) as -dependencies of our project, but you can also share your code with other people -by publishing your own packages. The crate registry at -[crates.io](https://crates.io/) distributes the source code of -your packages, so it primarily hosts code that is open source. +ما از پکیج‌های موجود در [crates.io](https://crates.io/) به عنوان وابستگی‌های پروژه خود استفاده کرده‌ایم، اما شما همچنین می‌توانید کد خود را با دیگران به اشتراک بگذارید با انتشار پکیج‌های خودتان. رجیستری Crates.io کد منبع پکیج‌های شما را توزیع می‌کند، بنابراین به طور عمده میزبان کدهای منبع باز است. -Rust and Cargo have features that make your published package easier for people -to find and use. We’ll talk about some of these features next and then explain -how to publish a package. +Rust و Cargo ویژگی‌هایی دارند که باعث می‌شود پکیج منتشرشده شما برای دیگران راحت‌تر پیدا شده و استفاده شود. ما ابتدا درباره برخی از این ویژگی‌ها صحبت می‌کنیم و سپس توضیح می‌دهیم چگونه یک پکیج منتشر کنیم. -### Making Useful Documentation Comments +### ایجاد نظرات مستندات مفید -Accurately documenting your packages will help other users know how and when to -use them, so it’s worth investing the time to write documentation. In Chapter -3, we discussed how to comment Rust code using two slashes, `//`. Rust also has -a particular kind of comment for documentation, known conveniently as a -_documentation comment_, that will generate HTML documentation. The HTML -displays the contents of documentation comments for public API items intended -for programmers interested in knowing how to _use_ your crate as opposed to how -your crate is _implemented_. +مستندسازی دقیق پکیج‌های شما به دیگر کاربران کمک می‌کند بدانند چگونه و چه زمانی از آن‌ها استفاده کنند، بنابراین ارزش دارد که وقت خود را برای نوشتن مستندات صرف کنید. در فصل 3، نحوه اضافه کردن نظرات به کد Rust با استفاده از دو اسلش `//` را بررسی کردیم. Rust همچنین نوع خاصی از نظرات برای مستندات دارد که به نام _نظرات مستندات_ شناخته می‌شود و مستندات HTML تولید می‌کند. این مستندات HTML محتوای نظرات مستندات را برای آیتم‌های عمومی API نمایش می‌دهد که برای برنامه‌نویسانی که به دنبال دانستن چگونگی _استفاده از_ crate شما هستند، طراحی شده است و نه چگونگی _پیاده‌سازی_ crate شما. -Documentation comments use three slashes, `///`, instead of two and support -Markdown notation for formatting the text. Place documentation comments just -before the item they’re documenting. Listing 14-1 shows documentation comments -for an `add_one` function in a crate named `my_crate`. +نظرات مستندات به جای دو اسلش از سه اسلش `///` استفاده می‌کنند و از نشانه‌گذاری Markdown برای قالب‌بندی متن پشتیبانی می‌کنند. نظرات مستندات را درست قبل از آیتمی که قرار است مستندسازی شود قرار دهید. لیستینگ 14-1 نظرات مستندات برای یک تابع `add_one` در یک crate به نام `my_crate` را نشان می‌دهد. -+ ```rust,ignore {{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-01/src/lib.rs}} @@ -34,47 +18,37 @@ for an `add_one` function in a crate named `my_crate`. -Here, we give a description of what the `add_one` function does, start a -section with the heading `Examples`, and then provide code that demonstrates -how to use the `add_one` function. We can generate the HTML documentation from -this documentation comment by running `cargo doc`. This command runs the -`rustdoc` tool distributed with Rust and puts the generated HTML documentation -in the _target/doc_ directory. +اینجا، ما توضیحی درباره عملکرد تابع `add_one` می‌دهیم، بخشی با عنوان `Examples` شروع می‌کنیم، و سپس کدی که نشان می‌دهد چگونه از تابع `add_one` استفاده کنیم ارائه می‌دهیم. می‌توانیم مستندات HTML را از این نظر مستند با اجرای دستور `cargo doc` تولید کنیم. این دستور ابزار `rustdoc` که با Rust توزیع شده را اجرا می‌کند و مستندات HTML تولیدشده را در دایرکتوری _target/doc_ قرار می‌دهد. +<<<<<<< HEAD +برای راحتی، اجرای دستور `cargo doc --open` مستندات HTML را برای crate فعلی شما (و همچنین مستندات همه وابستگی‌های crate شما) می‌سازد و نتیجه را در مرورگر وب باز می‌کند. به تابع `add_one` بروید و خواهید دید که چگونه متن موجود در نظرات مستندات نمایش داده می‌شود، همانطور که در شکل 14-1 نشان داده شده است: +======= For convenience, running `cargo doc --open` will build the HTML for your current crate’s documentation (as well as the documentation for all of your crate’s dependencies) and open the result in a web browser. Navigate to the `add_one` function and you’ll see how the text in the documentation comments is rendered, as shown in Figure 14-1. +>>>>>>> upstream/main -Rendered HTML documentation for the `add_one` function of `my_crate` +مستندات HTML تولیدشده برای تابع `add_one` از `my_crate` -Figure 14-1: HTML documentation for the `add_one` -function +شکل 14-1: مستندات HTML برای تابع `add_one` -#### Commonly Used Sections +#### بخش‌های متداول مورد استفاده -We used the `# Examples` Markdown heading in Listing 14-1 to create a section -in the HTML with the title “Examples.” Here are some other sections that crate -authors commonly use in their documentation: +ما در لیستینگ 14-1 از عنوان Markdown `# Examples` برای ایجاد یک بخش در HTML با عنوان "Examples" استفاده کردیم. در اینجا برخی دیگر از بخش‌هایی که نویسندگان crate معمولاً در مستندات خود استفاده می‌کنند آورده شده است: -- **Panics**: The scenarios in which the function being documented could - panic. Callers of the function who don’t want their programs to panic should - make sure they don’t call the function in these situations. -- **Errors**: If the function returns a `Result`, describing the kinds of - errors that might occur and what conditions might cause those errors to be - returned can be helpful to callers so they can write code to handle the - different kinds of errors in different ways. -- **Safety**: If the function is `unsafe` to call (we discuss unsafety in - Chapter 20), there should be a section explaining why the function is unsafe - and covering the invariants that the function expects callers to uphold. +- **Panics**: سناریوهایی که در آن ممکن است تابع مستند شده باعث ایجاد panic شود. فراخوانان تابع که نمی‌خواهند برنامه‌هایشان panic کنند باید مطمئن شوند که تابع را در این شرایط فراخوانی نمی‌کنند. +- **Errors**: اگر تابع یک مقدار `Result` بازگرداند، توضیح انواع خطاهایی که ممکن است رخ دهد و شرایطی که ممکن است این خطاها را ایجاد کند، برای فراخوانان مفید است تا بتوانند کدهایی برای مدیریت انواع مختلف خطاها بنویسند. +- **Safety**: اگر تابع `unsafe` برای فراخوانی باشد (ما عدم ایمنی را در فصل 20 بررسی خواهیم کرد)، باید بخشی توضیح دهد که چرا تابع ناامن است و اصولی را که تابع از فراخوانان انتظار دارد رعایت کنند پوشش دهد. -Most documentation comments don’t need all of these sections, but this is a -good checklist to remind you of the aspects of your code users will be -interested in knowing about. +بیشتر نظرات مستندات به همه این بخش‌ها نیاز ندارند، اما این یک چک‌لیست خوب برای یادآوری جنبه‌هایی از کد شما است که کاربران علاقه‌مند به دانستن آن هستند. -#### Documentation Comments as Tests +#### نظرات مستندات به عنوان تست +<<<<<<< HEAD +اضافه کردن بلوک‌های کد مثال به نظرات مستندات شما می‌تواند به نمایش نحوه استفاده از کتابخانه شما کمک کند، و انجام این کار یک مزیت اضافی دارد: اجرای دستور `cargo test`، مثال‌های کد در مستندات شما را به عنوان تست اجرا خواهد کرد! هیچ چیزی بهتر از مستندات با مثال نیست. اما هیچ چیزی بدتر از مثال‌هایی نیست که کار نمی‌کنند زیرا کد از زمان نوشته شدن مستندات تغییر کرده است. اگر `cargo test` را با مستندات تابع `add_one` از لیستینگ 14-1 اجرا کنیم، بخشی در نتایج تست مانند زیر خواهیم دید: +======= Adding example code blocks in your documentation comments can help demonstrate how to use your library, and doing so has an additional bonus: running `cargo test` will run the code examples in your documentation as tests! Nothing is @@ -89,6 +63,7 @@ cd listings/ch14-more-about-cargo/listing-14-01/ cargo test copy just the doc-tests section below --> +>>>>>>> upstream/main ```text Doc-tests my_crate @@ -99,12 +74,21 @@ test src/lib.rs - add_one (line 5) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s ``` +<<<<<<< HEAD +اکنون، اگر تابع یا مثال را تغییر دهیم به طوری که `assert_eq!` در مثال باعث panic شود و دوباره `cargo test` را اجرا کنیم، خواهیم دید که تست‌های مستندات تشخیص می‌دهند که مثال و کد با یکدیگر همگام نیستند! +======= Now, if we change either the function or the example so the `assert_eq!` in the example panics, and run `cargo test` again, we’ll see that the doc tests catch that the example and the code are out of sync with each other! +>>>>>>> upstream/main -#### Commenting Contained Items +#### مستندسازی آیتم‌های شامل شده +<<<<<<< HEAD +سبک نظر مستند `//!` مستندات را به آیتمی که نظرات را شامل می‌شود اضافه می‌کند، به جای آیتم‌هایی که بعد از نظرات قرار دارند. ما معمولاً از این نظرات مستند در فایل اصلی crate (_src/lib.rs_ بر اساس قرارداد) یا در داخل یک ماژول برای مستندسازی کل crate یا ماژول استفاده می‌کنیم. + +برای مثال، برای اضافه کردن مستنداتی که هدف crate `my_crate` را که شامل تابع `add_one` است توضیح می‌دهد، نظرات مستندی که با `//!` شروع می‌شوند را به ابتدای فایل _src/lib.rs_ اضافه می‌کنیم، همان‌طور که در لیستینگ 14-2 نشان داده شده است: +======= The style of doc comment `//!` adds documentation to the item that *contains* the comments rather than to the items *following* the comments. We typically use these doc comments inside the crate root file (_src/lib.rs_ by convention) or @@ -114,8 +98,9 @@ For example, to add documentation that describes the purpose of the `my_crate` crate that contains the `add_one` function, we add documentation comments that start with `//!` to the beginning of the _src/lib.rs_ file, as shown in Listing 14-2. +>>>>>>> upstream/main -+ ```rust,ignore {{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-02/src/lib.rs:here}} @@ -123,32 +108,33 @@ start with `//!` to the beginning of the _src/lib.rs_ file, as shown in Listing -Notice there isn’t any code after the last line that begins with `//!`. Because -we started the comments with `//!` instead of `///`, we’re documenting the item -that contains this comment rather than an item that follows this comment. In -this case, that item is the _src/lib.rs_ file, which is the crate root. These -comments describe the entire crate. +توجه داشته باشید که هیچ کدی بعد از آخرین خطی که با `//!` شروع می‌شود وجود ندارد. چون ما نظرات را با `//!` شروع کرده‌ایم به جای `///`، ما در حال مستندسازی آیتمی هستیم که این نظر را شامل می‌شود به جای آیتمی که بعد از این نظر قرار دارد. در این مورد، آن آیتم فایل _src/lib.rs_ است که ریشه crate است. این نظرات کل crate را توضیح می‌دهند. +<<<<<<< HEAD +وقتی `cargo doc --open` را اجرا می‌کنیم، این نظرات در صفحه اول مستندات crate `my_crate` بالای لیست آیتم‌های عمومی در crate نمایش داده می‌شوند، همان‌طور که در شکل 14-2 نشان داده شده است: +======= When we run `cargo doc --open`, these comments will display on the front page of the documentation for `my_crate` above the list of public items in the crate, as shown in Figure 14-2. +>>>>>>> upstream/main Rendered HTML documentation with a comment for the crate as a whole -Figure 14-2: Rendered documentation for `my_crate`, -including the comment describing the crate as a whole +شکل 14-2: مستندات تولید شده برای `my_crate`، شامل توضیحات در مورد کل crate + +نظرات مستندات داخل آیتم‌ها به ویژه برای توصیف crates و ماژول‌ها مفید هستند. از آن‌ها برای توضیح هدف کلی container استفاده کنید تا به کاربران خود در درک سازمان‌دهی crate کمک کنید. + +### صادرات یک API عمومی کارآمد با استفاده از `pub use` -Documentation comments within items are useful for describing crates and -modules especially. Use them to explain the overall purpose of the container to -help your users understand the crate’s organization. +ساختار API عمومی شما یک موضوع مهم هنگام انتشار یک crate است. افرادی که از crate شما استفاده می‌کنند، کمتر از شما با ساختار آن آشنا هستند و ممکن است در یافتن قسمت‌هایی که می‌خواهند استفاده کنند، اگر crate شما دارای یک سلسله‌مراتب ماژول بزرگ باشد، دچار مشکل شوند. -### Exporting a Convenient Public API with `pub use` +<<<<<<< HEAD +در فصل 7، نحوه عمومی کردن آیتم‌ها با استفاده از کلمه کلیدی `pub` و آوردن آیتم‌ها به یک scope با استفاده از کلمه کلیدی `use` را پوشش دادیم. با این حال، ساختاری که هنگام توسعه یک crate برای شما منطقی به نظر می‌رسد ممکن است برای کاربران شما چندان مناسب نباشد. ممکن است بخواهید ساختارهای خود را در یک سلسله‌مراتب با چندین سطح سازماندهی کنید، اما سپس افرادی که می‌خواهند از یک نوع تعریف‌شده عمیق در سلسله‌مراتب استفاده کنند ممکن است در پیدا کردن آن نوع دچار مشکل شوند. همچنین ممکن است مجبور شوند به جای `use my_crate::UsefulType;`، چیزی مانند `use my_crate::some_module::another_module::UsefulType;` بنویسند که ناخوشایند است. -The structure of your public API is a major consideration when publishing a -crate. People who use your crate are less familiar with the structure than you -are and might have difficulty finding the pieces they want to use if your crate -has a large module hierarchy. +خبر خوب این است که اگر ساختار _برای دیگران راحت نیست_، نیازی نیست سازمان‌دهی داخلی خود را دوباره بچینید: به جای آن می‌توانید آیتم‌ها را با استفاده از `pub use` مجدداً صادر کنید تا یک ساختار عمومی متفاوت از ساختار خصوصی خود ایجاد کنید. صادرات مجدد یک آیتم عمومی در یک مکان را می‌گیرد و آن را در یک مکان دیگر عمومی می‌کند، گویی که در مکان دیگر تعریف شده است. +برای مثال، فرض کنید ما یک کتابخانه به نام `art` برای مدل‌سازی مفاهیم هنری ایجاد کرده‌ایم. در این کتابخانه دو ماژول وجود دارند: یک ماژول `kinds` که شامل دو enum به نام‌های `PrimaryColor` و `SecondaryColor` است و یک ماژول `utils` که شامل یک تابع به نام `mix` است، همان‌طور که در لیستینگ 14-3 نشان داده شده است: +======= In Chapter 7, we covered how to make items public using the `pub` keyword, and how to bring items into a scope with the `use` keyword. However, the structure that makes sense to you while you’re developing a crate might not be very @@ -170,8 +156,9 @@ For example, say we made a library named `art` for modeling artistic concepts. Within this library are two modules: a `kinds` module containing two enums named `PrimaryColor` and `SecondaryColor` and a `utils` module containing a function named `mix`, as shown in Listing 14-3. +>>>>>>> upstream/main -+ ```rust,noplayground,test_harness {{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-03/src/lib.rs:here}} @@ -179,22 +166,27 @@ function named `mix`, as shown in Listing 14-3. +<<<<<<< HEAD +شکل 14-3 نشان می‌دهد که صفحه اول مستندات این crate که توسط `cargo doc` تولید شده است چگونه به نظر می‌رسد: +======= Figure 14-3 shows what the front page of the documentation for this crate generated by `cargo doc` would look like. +>>>>>>> upstream/main -Rendered documentation for the `art` crate that lists the `kinds` and `utils` modules +مستندات تولید شده برای crate `art` که ماژول‌های `kinds` و `utils` را لیست می‌کند -Figure 14-3: Front page of the documentation for `art` -that lists the `kinds` and `utils` modules +شکل 14-3: صفحه اول مستندات crate `art` که ماژول‌های `kinds` و `utils` را لیست می‌کند -Note that the `PrimaryColor` and `SecondaryColor` types aren’t listed on the -front page, nor is the `mix` function. We have to click `kinds` and `utils` to -see them. +توجه کنید که انواع `PrimaryColor` و `SecondaryColor` در صفحه اول لیست نشده‌اند، و تابع `mix` نیز لیست نشده است. برای دیدن آن‌ها باید روی `kinds` و `utils` کلیک کنیم. +<<<<<<< HEAD +یک crate دیگر که به این کتابخانه وابسته است نیاز دارد که بیانیه‌های `use` مشخص کنند که آیتم‌ها را از `art` به scope می‌آورند، و ساختار ماژول تعریف‌شده کنونی را بیان کنند. لیستینگ 14-4 یک مثال از crate‌ای که آیتم‌های `PrimaryColor` و `mix` را از crate `art` استفاده می‌کند نشان می‌دهد: +======= Another crate that depends on this library would need `use` statements that bring the items from `art` into scope, specifying the module structure that’s currently defined. Listing 14-4 shows an example of a crate that uses the `PrimaryColor` and `mix` items from the `art` crate. +>>>>>>> upstream/main @@ -204,20 +196,17 @@ currently defined. Listing 14-4 shows an example of a crate that uses the -The author of the code in Listing 14-4, which uses the `art` crate, had to -figure out that `PrimaryColor` is in the `kinds` module and `mix` is in the -`utils` module. The module structure of the `art` crate is more relevant to -developers working on the `art` crate than to those using it. The internal -structure doesn’t contain any useful information for someone trying to -understand how to use the `art` crate, but rather causes confusion because -developers who use it have to figure out where to look, and must specify the -module names in the `use` statements. +نویسنده کدی که در لیستینگ 14-4 نشان داده شده و از crate `art` استفاده می‌کند، مجبور بوده متوجه شود که `PrimaryColor` در ماژول `kinds` و `mix` در ماژول `utils` قرار دارد. ساختار ماژول crate `art` بیشتر برای توسعه‌دهندگانی که روی این crate کار می‌کنند مرتبط است تا کسانی که از آن استفاده می‌کنند. ساختار داخلی اطلاعات مفیدی برای کسی که می‌خواهد نحوه استفاده از crate `art` را بفهمد ارائه نمی‌دهد، بلکه بیشتر باعث سردرگمی می‌شود، زیرا توسعه‌دهندگانی که از آن استفاده می‌کنند باید بفهمند کجا را باید جستجو کنند و نام‌های ماژول را در بیانیه‌های `use` مشخص کنند. +<<<<<<< HEAD +برای حذف سازمان‌دهی داخلی از API عمومی، می‌توانیم کد crate `art` را در لیستینگ 14-3 تغییر دهیم تا بیانیه‌های `pub use` را برای صادرات مجدد آیتم‌ها در سطح بالا اضافه کنیم، همان‌طور که در لیستینگ 14-5 نشان داده شده است: +======= To remove the internal organization from the public API, we can modify the `art` crate code in Listing 14-3 to add `pub use` statements to re-export the items at the top level, as shown in Listing 14-5. +>>>>>>> upstream/main -+ ```rust,ignore {{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-05/src/lib.rs:here}} @@ -225,20 +214,21 @@ items at the top level, as shown in Listing 14-5. -The API documentation that `cargo doc` generates for this crate will now list -and link re-exports on the front page, as shown in Figure 14-4, making the -`PrimaryColor` and `SecondaryColor` types and the `mix` function easier to find. +مستندات API که `cargo doc` برای این crate تولید می‌کند اکنون صادرات‌های مجدد را در صفحه اول لیست کرده و به آن‌ها لینک می‌دهد، همان‌طور که در شکل 14-4 نشان داده شده است. این کار پیدا کردن انواع `PrimaryColor` و `SecondaryColor` و تابع `mix` را آسان‌تر می‌کند. -Rendered documentation for the `art` crate with the re-exports on the front page +مستندات تولیدشده برای crate `art` با صادرات‌های مجدد در صفحه اول -Figure 14-4: The front page of the documentation for `art` -that lists the re-exports +شکل 14-4: صفحه اول مستندات برای crate `art` که صادرات‌های مجدد را لیست می‌کند +<<<<<<< HEAD +کاربران crate `art` همچنان می‌توانند ساختار داخلی را از لیستینگ 14-3 ببینند و استفاده کنند، همان‌طور که در لیستینگ 14-4 نشان داده شده است، یا می‌توانند از ساختار راحت‌تر در لیستینگ 14-5 استفاده کنند، همان‌طور که در لیستینگ 14-6 نشان داده شده است: +======= The `art` crate users can still see and use the internal structure from Listing 14-3 as demonstrated in Listing 14-4, or they can use the more convenient structure in Listing 14-5, as shown in Listing 14-6. +>>>>>>> upstream/main -+ ```rust,ignore {{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-06/src/main.rs:here}} @@ -246,55 +236,34 @@ structure in Listing 14-5, as shown in Listing 14-6. -In cases where there are many nested modules, re-exporting the types at the top -level with `pub use` can make a significant difference in the experience of -people who use the crate. Another common use of `pub use` is to re-export -definitions of a dependency in the current crate to make that crate's -definitions part of your crate’s public API. - -Creating a useful public API structure is more of an art than a science, and -you can iterate to find the API that works best for your users. Choosing `pub -use` gives you flexibility in how you structure your crate internally and -decouples that internal structure from what you present to your users. Look at -some of the code of crates you’ve installed to see if their internal structure -differs from their public API. - -### Setting Up a Crates.io Account - -Before you can publish any crates, you need to create an account on -[crates.io](https://crates.io/) and get an API token. To do so, -visit the home page at [crates.io](https://crates.io/) and log -in via a GitHub account. (The GitHub account is currently a requirement, but -the site might support other ways of creating an account in the future.) Once -you’re logged in, visit your account settings at -[https://crates.io/me/](https://crates.io/me/) and retrieve your -API key. Then run the `cargo login` command and paste your API key when prompted, like this: +در مواردی که ماژول‌های تو در تو زیادی وجود دارند، صادرات مجدد انواع در سطح بالا با `pub use` می‌تواند تفاوت بزرگی در تجربه افرادی که از crate استفاده می‌کنند ایجاد کند. یکی دیگر از استفاده‌های رایج `pub use`، صادرات مجدد تعاریف یک وابستگی در crate فعلی برای تبدیل تعاریف آن به بخشی از API عمومی crate شما است. + +ایجاد یک ساختار API عمومی مفید بیشتر شبیه یک هنر است تا یک علم، و می‌توانید با آزمون و خطا API‌ای پیدا کنید که بهترین کارکرد را برای کاربران شما داشته باشد. انتخاب `pub use` به شما انعطاف می‌دهد که چگونه crate خود را به صورت داخلی ساختار دهید و آن ساختار داخلی را از چیزی که به کاربران خود ارائه می‌دهید جدا کنید. به برخی از کدهای crate‌هایی که نصب کرده‌اید نگاهی بیندازید تا ببینید آیا ساختار داخلی آن‌ها با API عمومی آن‌ها تفاوت دارد یا خیر. + +### تنظیم یک حساب در Crates.io + +قبل از اینکه بتوانید هر crate‌ای را منتشر کنید، نیاز دارید که یک حساب در [crates.io](https://crates.io/) ایجاد کنید و یک توکن API دریافت کنید. برای این کار، به صفحه اصلی در [crates.io](https://crates.io/) بروید و از طریق حساب GitHub وارد شوید. (در حال حاضر حساب GitHub یک نیاز است، اما ممکن است سایت در آینده از روش‌های دیگری برای ایجاد حساب پشتیبانی کند.) پس از ورود به سیستم، به تنظیمات حساب خود در [https://crates.io/me/](https://crates.io/me/) بروید و کلید API خود را دریافت کنید. سپس دستور `cargo login` را اجرا کرده و کلید API خود را وارد کنید، مانند این: ```console $ cargo login abcdefghijklmnopqrstuvwxyz012345 ``` +<<<<<<< HEAD +این دستور Cargo را از توکن API شما مطلع کرده و آن را به صورت محلی در فایل _~/.cargo/credentials_ ذخیره می‌کند. توجه داشته باشید که این توکن یک _راز_ است: آن را با هیچ‌کس دیگری به اشتراک نگذارید. اگر به هر دلیلی این توکن را با کسی به اشتراک گذاشتید، باید آن را لغو کنید و یک توکن جدید در [crates.io](https://crates.io/) ایجاد کنید. +======= This command will inform Cargo of your API token and store it locally in _~/.cargo/credentials.toml_. Note that this token is a _secret_: do not share it with anyone else. If you do share it with anyone for any reason, you should revoke it and generate a new token on [crates.io](https://crates.io/). +>>>>>>> upstream/main -### Adding Metadata to a New Crate +### افزودن متادیتا به یک Crate جدید -Let’s say you have a crate you want to publish. Before publishing, you’ll need -to add some metadata in the `[package]` section of the crate’s _Cargo.toml_ -file. +فرض کنید یک crate دارید که می‌خواهید منتشر کنید. قبل از انتشار، نیاز دارید که برخی متادیتا را در بخش `[package]` فایل _Cargo.toml_ crate خود اضافه کنید. -Your crate will need a unique name. While you’re working on a crate locally, -you can name a crate whatever you’d like. However, crate names on -[crates.io](https://crates.io/) are allocated on a first-come, -first-served basis. Once a crate name is taken, no one else can publish a crate -with that name. Before attempting to publish a crate, search for the name you -want to use. If the name has been used, you will need to find another name and -edit the `name` field in the _Cargo.toml_ file under the `[package]` section to -use the new name for publishing, like so: +crate شما باید یک نام منحصر به فرد داشته باشد. در حالی که به صورت محلی روی یک crate کار می‌کنید، می‌توانید هر نامی که دوست دارید برای crate خود انتخاب کنید. با این حال، نام‌های crate در [crates.io](https://crates.io/) به صورت اولین درخواست‌کننده تخصیص داده می‌شوند. هنگامی که یک نام برای یک crate گرفته شود، هیچ کس دیگری نمی‌تواند یک crate با آن نام منتشر کند. قبل از تلاش برای انتشار یک crate، جستجو کنید که نامی که می‌خواهید استفاده کنید در دسترس است یا خیر. اگر نام استفاده شده باشد، باید یک نام دیگر پیدا کنید و فیلد `name` را در فایل _Cargo.toml_ در زیر بخش `[package]` ویرایش کنید تا از نام جدید برای انتشار استفاده کنید، مانند زیر: Filename: Cargo.toml @@ -303,6 +272,9 @@ use the new name for publishing, like so: name = "guessing_game" ``` +<<<<<<< HEAD +حتی اگر یک نام منحصر به فرد انتخاب کرده باشید، زمانی که `cargo publish` را برای انتشار crate در این مرحله اجرا کنید، یک هشدار و سپس یک خطا دریافت خواهید کرد: +======= Even if you’ve chosen a unique name, when you run `cargo publish` to publish the crate at this point, you’ll get a warning and then an error: @@ -312,6 +284,7 @@ Create a new package with an unregistered name, making no further modifications cargo publish copy just the relevant lines below --> +>>>>>>> upstream/main ```console $ cargo publish @@ -325,6 +298,9 @@ Caused by: the remote server responded with an error (status 400 Bad Request): missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for more information on configuring these fields ``` +<<<<<<< HEAD +این خطا به دلیل این است که شما برخی اطلاعات حیاتی را از دست داده‌اید: یک توضیح و یک مجوز مورد نیاز است تا افراد بدانند crate شما چه کاری انجام می‌دهد و تحت چه شرایطی می‌توانند از آن استفاده کنند. در فایل _Cargo.toml_، یک توضیح اضافه کنید که فقط یک یا دو جمله باشد، زیرا این توضیح همراه crate شما در نتایج جستجو ظاهر خواهد شد. برای فیلد `license`، باید یک _مقدار شناسگر مجوز_ ارائه دهید. [پروژه Software Package Data Exchange (SPDX)](http://spdx.org/licenses/) لیستی از شناسگرهایی که می‌توانید برای این مقدار استفاده کنید را ارائه می‌دهد. برای مثال، برای مشخص کردن اینکه crate خود را با استفاده از مجوز MIT منتشر کرده‌اید، شناسگر `MIT` را اضافه کنید: +======= This results in an error because you’re missing some crucial information: a description and license are required so people will know what your crate does and under what terms they can use it. In _Cargo.toml_, add a description that's @@ -333,6 +309,7 @@ results. For the `license` field, you need to give a _license identifier value_. The [Linux Foundation’s Software Package Data Exchange (SPDX)][spdx] lists the identifiers you can use for this value. For example, to specify that you’ve licensed your crate using the MIT License, add the `MIT` identifier: +>>>>>>> upstream/main Filename: Cargo.toml @@ -342,19 +319,11 @@ name = "guessing_game" license = "MIT" ``` -If you want to use a license that doesn’t appear in the SPDX, you need to place -the text of that license in a file, include the file in your project, and then -use `license-file` to specify the name of that file instead of using the -`license` key. +اگر می‌خواهید از مجوزی استفاده کنید که در لیست SPDX موجود نیست، باید متن آن مجوز را در یک فایل قرار دهید، فایل را در پروژه خود اضافه کنید و سپس از کلید `license-file` برای مشخص کردن نام آن فایل به جای استفاده از کلید `license` استفاده کنید. -Guidance on which license is appropriate for your project is beyond the scope -of this book. Many people in the Rust community license their projects in the -same way as Rust by using a dual license of `MIT OR Apache-2.0`. This practice -demonstrates that you can also specify multiple license identifiers separated -by `OR` to have multiple licenses for your project. +راهنمایی درباره اینکه کدام مجوز برای پروژه شما مناسب است، فراتر از محدوده این کتاب است. بسیاری از افراد در جامعه Rust پروژه‌های خود را به همان روشی که Rust مجوز داده است، با استفاده از یک مجوز دوگانه `MIT OR Apache-2.0` مجوز می‌دهند. این روش نشان می‌دهد که شما می‌توانید چندین شناسه مجوز را با جدا کردن آن‌ها با `OR` مشخص کنید تا چندین مجوز برای پروژه خود داشته باشید. -With a unique name, the version, your description, and a license added, the -_Cargo.toml_ file for a project that is ready to publish might look like this: +با یک نام منحصر به فرد، نسخه، توضیحات، و یک مجوز اضافه شده، فایل _Cargo.toml_ برای یک پروژه آماده انتشار ممکن است به این صورت باشد: Filename: Cargo.toml @@ -369,17 +338,21 @@ license = "MIT OR Apache-2.0" [dependencies] ``` +<<<<<<< HEAD +[مستندات Cargo](https://doc.rust-lang.org/cargo/) سایر متادیتاهایی که می‌توانید مشخص کنید تا دیگران بتوانند crate شما را راحت‌تر پیدا کرده و استفاده کنند توضیح می‌دهد. +======= [Cargo’s documentation](https://doc.rust-lang.org/cargo/) describes other metadata you can specify to ensure that others can discover and use your crate more easily. +>>>>>>> upstream/main -### Publishing to Crates.io +### انتشار در Crates.io -Now that you’ve created an account, saved your API token, chosen a name for -your crate, and specified the required metadata, you’re ready to publish! -Publishing a crate uploads a specific version to -[crates.io](https://crates.io/) for others to use. +اکنون که یک حساب ایجاد کرده‌اید، توکن API خود را ذخیره کرده‌اید، نامی برای crate خود انتخاب کرده‌اید، و متادیتای مورد نیاز را مشخص کرده‌اید، آماده انتشار هستید! انتشار یک crate نسخه‌ای خاص از آن را در [crates.io](https://crates.io/) آپلود می‌کند تا دیگران بتوانند از آن استفاده کنند. +<<<<<<< HEAD +دقت کنید، زیرا انتشار _دائمی_ است. نسخه هرگز نمی‌تواند بازنویسی شود، و کد نمی‌تواند حذف شود. یکی از اهداف اصلی [crates.io](https://crates.io/) این است که به عنوان یک آرشیو دائمی از کد عمل کند، به طوری که ساخت‌های همه پروژه‌هایی که به crates از [crates.io](https://crates.io/) وابسته هستند، همچنان کار کنند. اجازه حذف نسخه‌ها تحقق این هدف را غیرممکن می‌کند. با این حال، هیچ محدودیتی برای تعداد نسخه‌های crate که می‌توانید منتشر کنید وجود ندارد. +======= Be careful, because a publish is _permanent_. The version can never be overwritten, and the code cannot be deleted except in certain circumstances. One major goal of Crates.io is to act as a permanent archive of code so that @@ -387,8 +360,9 @@ builds of all projects that depend on crates from [crates.io](https://crates.io/) will continue to work. Allowing version deletions would make fulfilling that goal impossible. However, there is no limit to the number of crate versions you can publish. +>>>>>>> upstream/main -Run the `cargo publish` command again. It should succeed now: +دستور `cargo publish` را دوباره اجرا کنید. اکنون باید موفق شود: + -### Deprecating Versions from Crates.io with `cargo yank` +### از رده خارج کردن نسخه‌ها از Crates.io با استفاده از `cargo yank` + +<<<<<<< HEAD +اگرچه نمی‌توانید نسخه‌های قبلی یک crate را حذف کنید، می‌توانید از اضافه شدن آن‌ها به عنوان وابستگی جدید در پروژه‌های آینده جلوگیری کنید. این ویژگی زمانی مفید است که یک نسخه از crate به هر دلیلی خراب باشد. در چنین مواردی، Cargo از _یَنک کردن_ (yanking) یک نسخه از crate پشتیبانی می‌کند. +یَنک کردن یک نسخه باعث می‌شود که پروژه‌های جدید نتوانند به آن نسخه وابسته شوند، در حالی که تمام پروژه‌های موجود که به آن نسخه وابسته هستند به کار خود ادامه می‌دهند. به طور خلاصه، یَنک به این معناست که تمام پروژه‌هایی که دارای فایل _Cargo.lock_ هستند شکسته نخواهند شد و هر فایل _Cargo.lock_ جدیدی که تولید شود از نسخه یَنک‌شده استفاده نخواهد کرد. +======= Although you can’t remove previous versions of a crate, you can prevent any future projects from adding them as a new dependency. This is useful when a crate version is broken for one reason or another. In such situations, Cargo @@ -439,17 +421,9 @@ _Yanking_ a version prevents new projects from depending on that version while allowing all existing projects that depend on it to continue. Essentially, a yank means that all projects with a _Cargo.lock_ will not break, and any future _Cargo.lock_ files generated will not use the yanked version. +>>>>>>> upstream/main -To yank a version of a crate, in the directory of the crate that you’ve -previously published, run `cargo yank` and specify which version you want to -yank. For example, if we've published a crate named `guessing_game` version -1.0.1 and we want to yank it, in the project directory for `guessing_game` we'd -run: - - +برای یَنک کردن یک نسخه از یک crate، در دایرکتوری crate‌ای که قبلاً منتشر کرده‌اید، دستور `cargo yank` را اجرا کرده و نسخه‌ای که می‌خواهید یَنک کنید را مشخص کنید. به عنوان مثال، اگر ما یک crate به نام `guessing_game` نسخه 1.0.1 منتشر کرده باشیم و بخواهیم آن را یَنک کنیم، در دایرکتوری پروژه `guessing_game` این دستور را اجرا می‌کنیم: ```console $ cargo yank --vers 1.0.1 @@ -457,8 +431,7 @@ $ cargo yank --vers 1.0.1 Yank guessing_game@1.0.1 ``` -By adding `--undo` to the command, you can also undo a yank and allow projects -to start depending on a version again: +با افزودن گزینه `--undo` به دستور، می‌توانید یَنک را لغو کرده و به پروژه‌ها اجازه دهید دوباره به آن نسخه وابسته شوند: ```console $ cargo yank --vers 1.0.1 --undo @@ -466,8 +439,7 @@ $ cargo yank --vers 1.0.1 --undo Unyank guessing_game@1.0.1 ``` -A yank _does not_ delete any code. It cannot, for example, delete accidentally -uploaded secrets. If that happens, you must reset those secrets immediately. +یَنک _هیچ کدی را حذف نمی‌کند_. به عنوان مثال، نمی‌تواند اطلاعات حساسی که به طور تصادفی آپلود شده‌اند را حذف کند. اگر چنین اتفاقی افتاد، باید فوراً آن اطلاعات حساس را بازنشانی کنید. [spdx]: https://spdx.org/licenses/ [semver]: https://semver.org/ diff --git a/src/ch14-03-cargo-workspaces.md b/src/ch14-03-cargo-workspaces.md index cda379b38c..6a904d954d 100644 --- a/src/ch14-03-cargo-workspaces.md +++ b/src/ch14-03-cargo-workspaces.md @@ -1,10 +1,6 @@ -## Cargo Workspaces +## Workspaces در Cargo -In Chapter 12, we built a package that included a binary crate and a library -crate. As your project develops, you might find that the library crate -continues to get bigger and you want to split your package further into -multiple library crates. Cargo offers a feature called _workspaces_ that can -help manage multiple related packages that are developed in tandem. +در فصل 12، ما یک پکیج ساختیم که شامل یک crate باینری و یک crate کتابخانه‌ای بود. همان‌طور که پروژه شما توسعه می‌یابد، ممکن است متوجه شوید که crate کتابخانه‌ای همچنان بزرگ‌تر می‌شود و بخواهید پکیج خود را بیشتر به crate‌های کتابخانه‌ای چندگانه تقسیم کنید. Cargo یک ویژگی به نام _workspaces_ ارائه می‌دهد که می‌تواند به مدیریت پکیج‌های مرتبط که به صورت همزمان توسعه داده می‌شوند کمک کند. ### Creating a Workspace @@ -14,7 +10,7 @@ can concentrate on the structure of the workspace. There are multiple ways to structure a workspace, so we'll just show one common way. We’ll have a workspace containing a binary and two libraries. The binary, which will provide the main functionality, will depend on the two libraries. One library will -provide an `add_one` function and the other library an `add_two` function. +provide an `add_one` function, and a second library an `add_two` function. These three crates will be part of the same workspace. We’ll start by creating a new directory for the workspace: @@ -28,7 +24,7 @@ configure the entire workspace. This file won’t have a `[package]` section. Instead, it will start with a `[workspace]` section that will allow us to add members to the workspace. We also make a point to use the latest and greatest version of Cargo’s resolver algorithm in our workspace by setting the -`resolver` value to `"3"`. +`resolver` to `"2"`. Filename: Cargo.toml @@ -41,7 +37,6 @@ _add_ directory: +بیایید workspace را با اجرای دستور `cargo build` در دایرکتوری سطح بالای _add_ بسازیم! ```console $ cargo build @@ -178,15 +159,7 @@ $ cargo build Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s ``` -To run the binary crate from the _add_ directory, we can specify which -package in the workspace we want to run by using the `-p` argument and the -package name with `cargo run`: - - +برای اجرای crate باینری از دایرکتوری _add_، می‌توانیم با استفاده از آرگومان `-p` و نام پکیج همراه با دستور `cargo run` مشخص کنیم کدام پکیج در workspace اجرا شود: ```console $ cargo run -p adder @@ -195,19 +168,11 @@ $ cargo run -p adder Hello, world! 10 plus one is 11! ``` -This runs the code in _adder/src/main.rs_, which depends on the `add_one` crate. +این کد در فایل _adder/src/main.rs_ را اجرا می‌کند که به crate `add_one` وابسته است. -#### Depending on an External Package in a Workspace +#### وابستگی به یک پکیج خارجی در یک Workspace -Notice that the workspace has only one _Cargo.lock_ file at the top level, -rather than having a _Cargo.lock_ in each crate’s directory. This ensures that -all crates are using the same version of all dependencies. If we add the `rand` -package to the _adder/Cargo.toml_ and _add_one/Cargo.toml_ files, Cargo will -resolve both of those to one version of `rand` and record that in the one -_Cargo.lock_. Making all crates in the workspace use the same dependencies -means the crates will always be compatible with each other. Let’s add the -`rand` crate to the `[dependencies]` section in the _add_one/Cargo.toml_ file -so we can use the `rand` crate in the `add_one` crate: +توجه کنید که workspace فقط یک فایل _Cargo.lock_ در سطح بالا دارد، به جای اینکه هر crate دایرکتوری خود فایل _Cargo.lock_ داشته باشد. این اطمینان حاصل می‌کند که تمام crate‌ها از همان نسخه تمام وابستگی‌ها استفاده می‌کنند. اگر پکیج `rand` را به فایل‌های _adder/Cargo.toml_ و _add_one/Cargo.toml_ اضافه کنیم، Cargo هر دو را به یک نسخه از `rand` تبدیل می‌کند و آن را در فایل _Cargo.lock_ ثبت می‌کند. اطمینان از اینکه همه crate‌های موجود در workspace از همان وابستگی‌ها استفاده می‌کنند، به این معناست که crate‌ها همیشه با یکدیگر سازگار خواهند بود. بیایید پکیج `rand` را به بخش `[dependencies]` در فایل _add_one/Cargo.toml_ اضافه کنیم تا بتوانیم از crate `rand` در crate `add_one` استفاده کنیم: +حالا می‌توانیم `use rand;` را به فایل _add_one/src/lib.rs_ اضافه کنیم و با اجرای دستور `cargo build` در دایرکتوری _add_ کل workspace را بسازیم، که crate `rand` را وارد کرده و کامپایل خواهد کرد. یک هشدار دریافت خواهیم کرد زیرا به `rand` که به محدوده وارد شده است اشاره‌ای نمی‌کنیم: ```console $ cargo build @@ -252,17 +208,7 @@ warning: `add_one` (lib) generated 1 warning (run `cargo fix --lib -p add_one` t Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.95s ``` -The top-level _Cargo.lock_ now contains information about the dependency of -`add_one` on `rand`. However, even though `rand` is used somewhere in the -workspace, we can’t use it in other crates in the workspace unless we add -`rand` to their _Cargo.toml_ files as well. For example, if we add `use rand;` -to the _adder/src/main.rs_ file for the `adder` package, we’ll get an error: - - +فایل _Cargo.lock_ در سطح بالا اکنون اطلاعاتی درباره وابستگی `add_one` به `rand` دارد. با این حال، حتی اگر `rand` در جایی از workspace استفاده شود، نمی‌توانیم از آن در crate‌های دیگر workspace استفاده کنیم مگر اینکه `rand` را به فایل‌های _Cargo.toml_ آن‌ها نیز اضافه کنیم. برای مثال، اگر `use rand;` را به فایل _adder/src/main.rs_ برای پکیج `adder` اضافه کنیم، با خطا مواجه خواهیم شد: ```console $ cargo build @@ -279,19 +225,18 @@ To fix this, edit the _Cargo.toml_ file for the `adder` package and indicate that `rand` is a dependency for it as well. Building the `adder` package will add `rand` to the list of dependencies for `adder` in _Cargo.lock_, but no additional copies of `rand` will be downloaded. Cargo will ensure that every -crate in every package in the workspace using the `rand` package will use the -same version as long as they specify compatible versions of `rand`, saving us -space and ensuring that the crates in the workspace will be compatible with each -other. +crate in every package in the workspace using the `rand` package will be using +the same version as long as they specify compatible versions of `rand`, saving +us space and ensuring that the crates in the workspace will be compatible with +each other. If crates in the workspace specify incompatible versions of the same dependency, Cargo will resolve each of them, but will still try to resolve as few versions as possible. -#### Adding a Test to a Workspace +#### افزودن یک تست به یک Workspace -For another enhancement, let’s add a test of the `add_one::add_one` function -within the `add_one` crate: +برای یک بهبود دیگر، بیایید یک تست برای تابع `add_one::add_one` در crate `add_one` اضافه کنیم: Filename: add_one/src/lib.rs @@ -299,16 +244,7 @@ within the `add_one` crate: {{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/src/lib.rs}} ``` -Now run `cargo test` in the top-level _add_ directory. Running `cargo test` in -a workspace structured like this one will run the tests for all the crates in -the workspace: - - +حالا دستور `cargo test` را در دایرکتوری سطح بالای _add_ اجرا کنید. اجرای دستور `cargo test` در یک workspace با ساختاری مانند این، تست‌های تمام crate‌های موجود در workspace را اجرا خواهد کرد: ```console $ cargo test @@ -335,14 +271,9 @@ running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` -The first section of the output shows that the `it_works` test in the `add_one` -crate passed. The next section shows that zero tests were found in the `adder` -crate, and then the last section shows zero documentation tests were found in -the `add_one` crate. +بخش اول خروجی نشان می‌دهد که تست `it_works` در crate `add_one` پاس شده است. بخش بعدی نشان می‌دهد که هیچ تستی در crate `adder` پیدا نشده است، و سپس بخش آخر نشان می‌دهد که هیچ تست مستنداتی در crate `add_one` پیدا نشده است. -We can also run tests for one particular crate in a workspace from the -top-level directory by using the `-p` flag and specifying the name of the crate -we want to test: +ما همچنین می‌توانیم تست‌های یک crate خاص در workspace را از دایرکتوری سطح بالا با استفاده از گزینه `-p` و مشخص کردن نام crate‌ای که می‌خواهیم تست کنیم، اجرا کنیم: , each crate in the workspace -will need to be published separately. Like `cargo test`, we can publish a -particular crate in our workspace by using the `-p` flag and specifying the -name of the crate we want to publish. +If you publish the crates in the workspace to [crates.io](https://crates.io/), +each crate in the workspace will need to be published separately. Like `cargo +test`, we can publish a particular crate in our workspace by using the `-p` +flag and specifying the name of the crate we want to publish. For additional practice, add an `add_two` crate to this workspace in a similar way as the `add_one` crate! -As your project grows, consider using a workspace: it enables you to work with -smaller, easier-to-understand components than one big blob of code. Furthermore, -keeping the crates in a workspace can make coordination between crates easier if -they are often changed at the same time. +As your project grows, consider using a workspace: it’s easier to understand +smaller, individual components than one big blob of code. Furthermore, keeping +the crates in a workspace can make coordination between crates easier if they +are often changed at the same time. diff --git a/src/ch14-04-installing-binaries.md b/src/ch14-04-installing-binaries.md index 5259590c6b..ab0f36c806 100644 --- a/src/ch14-04-installing-binaries.md +++ b/src/ch14-04-installing-binaries.md @@ -1,33 +1,14 @@ - + -## Installing Binaries with `cargo install` +## نصب باینری‌ها با استفاده از `cargo install` -The `cargo install` command allows you to install and use binary crates -locally. This isn’t intended to replace system packages; it’s meant to be a -convenient way for Rust developers to install tools that others have shared on -[crates.io](https://crates.io/). Note that you can only install -packages that have binary targets. A _binary target_ is the runnable program -that is created if the crate has a _src/main.rs_ file or another file specified -as a binary, as opposed to a library target that isn’t runnable on its own but -is suitable for including within other programs. Usually, crates have -information in the _README_ file about whether a crate is a library, has a -binary target, or both. +دستور `cargo install` به شما این امکان را می‌دهد که crate‌های باینری را به صورت محلی نصب و استفاده کنید. این دستور به‌منظور جایگزینی بسته‌های سیستمی طراحی نشده است؛ بلکه یک راه آسان برای توسعه‌دهندگان Rust فراهم می‌کند تا ابزارهایی که دیگران در [crates.io](https://crates.io/) به اشتراک گذاشته‌اند را نصب کنند. توجه داشته باشید که فقط پکیج‌هایی را که دارای هدف باینری هستند می‌توانید نصب کنید. _هدف باینری_ برنامه قابل‌اجرا است که در صورتی ایجاد می‌شود که crate شامل یک فایل _src/main.rs_ یا فایل دیگری باشد که به عنوان باینری مشخص شده است. این در مقابل هدف کتابخانه‌ای قرار دارد که به تنهایی قابل اجرا نیست، اما برای استفاده در سایر برنامه‌ها مناسب است. معمولاً crate‌ها در فایل _README_ اطلاعاتی در مورد اینکه آیا یک crate کتابخانه است، دارای هدف باینری است یا هر دو، ارائه می‌دهند. -All binaries installed with `cargo install` are stored in the installation -root’s _bin_ folder. If you installed Rust using _rustup.rs_ and don’t have any -custom configurations, this directory will be *$HOME/.cargo/bin*. Ensure that -directory is in your `$PATH` to be able to run programs you’ve installed with -`cargo install`. +تمام باینری‌هایی که با دستور `cargo install` نصب می‌شوند، در پوشه‌ی *bin* مسیر نصب ذخیره می‌شوند. اگر Rust را با استفاده از *rustup.rs* نصب کرده باشید و هیچ پیکربندی خاصی انجام نداده باشید، این دایرکتوری به صورت *\$HOME/.cargo/bin* خواهد بود. اطمینان حاصل کنید که این دایرکتوری در متغیر محیطی `$PATH` شما قرار دارد تا بتوانید برنامه‌هایی را که با `cargo install` نصب کرده‌اید اجرا کنید. -For example, in Chapter 12 we mentioned that there’s a Rust implementation of -the `grep` tool called `ripgrep` for searching files. To install `ripgrep`, we -can run the following: - - +برای مثال، در فصل 12 اشاره کردیم که یک پیاده‌سازی Rust از ابزار `grep` به نام `ripgrep` وجود دارد که برای جستجوی فایل‌ها استفاده می‌شود. برای نصب `ripgrep` می‌توانیم دستور زیر را اجرا کنیم: ```console $ cargo install ripgrep @@ -42,7 +23,4 @@ $ cargo install ripgrep Installed package `ripgrep v14.1.1` (executable `rg`) ``` -The second-to-last line of the output shows the location and the name of the -installed binary, which in the case of `ripgrep` is `rg`. As long as the -installation directory is in your `$PATH`, as mentioned previously, you can -then run `rg --help` and start using a faster, Rustier tool for searching files! +خط ماقبل آخر خروجی، محل و نام باینری نصب‌شده را نشان می‌دهد که در مورد `ripgrep`، نام آن `rg` است. تا زمانی که دایرکتوری نصب در متغیر محیطی `$PATH` شما قرار داشته باشد ــ همان‌طور که پیش‌تر ذکر شد ــ می‌توانید دستور `rg --help` را اجرا کرده و استفاده از یک ابزار جستجوی فایل سریع‌تر و Rust-محور را آغاز کنید! \ No newline at end of file diff --git a/src/ch14-05-extending-cargo.md b/src/ch14-05-extending-cargo.md index b9a68b032e..a363f7564d 100644 --- a/src/ch14-05-extending-cargo.md +++ b/src/ch14-05-extending-cargo.md @@ -1,17 +1,13 @@ -## Extending Cargo with Custom Commands +## گسترش Cargo با دستورات سفارشی -Cargo is designed so you can extend it with new subcommands without having to -modify it. If a binary in your `$PATH` is named `cargo-something`, you can run -it as if it were a Cargo subcommand by running `cargo something`. Custom -commands like this are also listed when you run `cargo --list`. Being able to -use `cargo install` to install extensions and then run them just like the -built-in Cargo tools is a super-convenient benefit of Cargo’s design! +با این حال، همچنان باید به‌صورت صریح نوع رفرنس پین‌شده را مشخص کنیم؛ +در غیر این صورت، Rust نمی‌داند که این‌ها باید به عنوان trait objectهای داینامیک تفسیر شوند، +که این همان چیزی است که در `Vec` به آن نیاز داریم. +بنابراین، `pin` را به لیست واردات‌مان از `std::pin` اضافه می‌کنیم. +سپس می‌توانیم هر future را هنگام تعریف آن با `pin!` پین کنیم +و `futures` را به‌صورت یک `Vec` شامل رفرنس‌های mutable پین‌شده به نوع dynamic future تعریف کنیم، +همان‌طور که در لیستینگ 17-19 نشان داده شده است. -## Summary +## خلاصه -Sharing code with Cargo and [crates.io](https://crates.io/) is -part of what makes the Rust ecosystem useful for many different tasks. Rust’s -standard library is small and stable, but crates are easy to share, use, and -improve on a timeline different from that of the language. Don’t be shy about -sharing code that’s useful to you on [crates.io](https://crates.io/); it’s likely that it will be useful to someone else as well! +اشتراک‌گذاری کد با Cargo و [crates.io](https://crates.io/) بخشی از عواملی است که اکوسیستم Rust را برای بسیاری از وظایف مختلف مفید می‌کند. کتابخانه استاندارد Rust کوچک و پایدار است، اما crate‌ها به راحتی قابل اشتراک‌گذاری، استفاده و بهبود هستند و با یک خط زمانی متفاوت از زبان توسعه می‌یابند. از اشتراک‌گذاری کدی که برای شما مفید است در [crates.io](https://crates.io/) خجالت نکشید؛ احتمالاً برای دیگران نیز مفید خواهد بود! diff --git a/src/ch15-00-smart-pointers.md b/src/ch15-00-smart-pointers.md index 5f86585fd5..b3f39354f3 100644 --- a/src/ch15-00-smart-pointers.md +++ b/src/ch15-00-smart-pointers.md @@ -1,46 +1,27 @@ -# Smart Pointers - -A _pointer_ is a general concept for a variable that contains an address in -memory. This address refers to, or “points at,” some other data. The most -common kind of pointer in Rust is a reference, which you learned about in -Chapter 4. References are indicated by the `&` symbol and borrow the value they -point to. They don’t have any special capabilities other than referring to -data, and they have no overhead. - -_Smart pointers_, on the other hand, are data structures that act like a -pointer but also have additional metadata and capabilities. The concept of -smart pointers isn’t unique to Rust: smart pointers originated in C++ and exist -in other languages as well. Rust has a variety of smart pointers defined in the -standard library that provide functionality beyond that provided by references. -To explore the general concept, we’ll look at a couple of different examples of -smart pointers, including a _reference counting_ smart pointer type. This -pointer enables you to allow data to have multiple owners by keeping track of -the number of owners and, when no owners remain, cleaning up the data. - -Rust, with its concept of ownership and borrowing, has an additional difference -between references and smart pointers: while references only borrow data, in -many cases smart pointers _own_ the data they point to. - -Smart pointers are usually implemented using structs. Unlike an ordinary -struct, smart pointers implement the `Deref` and `Drop` traits. The `Deref` -trait allows an instance of the smart pointer struct to behave like a reference -so you can write your code to work with either references or smart pointers. -The `Drop` trait allows you to customize the code that’s run when an instance -of the smart pointer goes out of scope. In this chapter, we’ll discuss both of -these traits and demonstrate why they’re important to smart pointers. - -Given that the smart pointer pattern is a general design pattern used -frequently in Rust, this chapter won’t cover every existing smart pointer. Many -libraries have their own smart pointers, and you can even write your own. We’ll -cover the most common smart pointers in the standard library: - -- `Box`, for allocating values on the heap -- `Rc`, a reference counting type that enables multiple ownership -- `Ref` and `RefMut`, accessed through `RefCell`, a type that enforces - the borrowing rules at runtime instead of compile time - -In addition, we’ll cover the _interior mutability_ pattern where an immutable -type exposes an API for mutating an interior value. We’ll also discuss -reference cycles: how they can leak memory and how to prevent them. - -Let’s dive in! +# اشاره‌گر (Pointer)های هوشمند (Smart Pointers) + +یک *pointer* یک مفهوم عمومی برای متغیری است که یک آدرس در حافظه را در خود نگه می‌دارد. این آدرس به داده‌ای دیگر اشاره می‌کند یا به‌عبارتی «نشان می‌دهد». رایج‌ترین نوع pointer در Rust یک رفرنس است، که در فصل ۴ با آن آشنا شدید. رفرنس‌ها با نماد `&` مشخص می‌شوند و مقدار مورد اشاره را قرض می‌گیرند. آن‌ها هیچ قابلیت ویژه‌ای جز اشاره به داده ندارند و هیچ سرباری نیز ندارند. + +از سوی دیگر، _اشاره‌گر (Pointer)های هوشمند_ ساختارهای داده‌ای هستند که مانند یک اشاره‌گر (Pointer) عمل می‌کنند، اما همچنین دارای +فرا داده و قابلیت‌های اضافی هستند. مفهوم اشاره‌گر (Pointer)های هوشمند منحصراً به Rust اختصاص ندارد: اشاره‌گر (Pointer)های هوشمند +در ابتدا در C++ معرفی شدند و در زبان‌های دیگر نیز وجود دارند. Rust مجموعه‌ای از اشاره‌گر (Pointer)های هوشمند در کتابخانه +استاندارد خود دارد که عملکردی فراتر از آنچه که ارجاعات فراهم می‌کنند، ارائه می‌دهند. برای بررسی مفهوم کلی، به چند +مثال مختلف از اشاره‌گر (Pointer)های هوشمند نگاهی خواهیم انداخت، از جمله نوع اشاره‌گر (Pointer) هوشمند _شمارش ارجاعات_. این اشاره‌گر (Pointer) +به شما امکان می‌دهد تا داده‌ها مالکیت‌های متعددی داشته باشند، با ردیابی تعداد مالکان و پاک کردن داده هنگامی که +هیچ مالکی باقی نماند. + +زبان Rust با معرفی مفاهیمی مانند مالکیت و قرض‌گرفتن، تفاوت مهمی بین رفرنس‌ها و smart pointerها ایجاد کرده است: در حالی‌که رفرنس‌ها تنها داده را قرض می‌گیرند، در بسیاری از موارد smart pointerها *مالکیت* داده‌ای را که به آن اشاره می‌کنند در اختیار دارند. + +smart pointerها معمولاً با استفاده از `struct`ها پیاده‌سازی می‌شوند. برخلاف `struct`های معمولی، smart pointerها `trait`های `Deref` و `Drop` را پیاده‌سازی می‌کنند. `trait` مربوط به `Deref` این امکان را فراهم می‌کند که یک نمونه از smart pointer مانند یک رفرنس رفتار کند، به‌طوری‌که بتوانید کد خود را به‌گونه‌ای بنویسید که با هر دو ــ یعنی هم رفرنس‌ها و هم smart pointerها ــ کار کند. `trait` مربوط به `Drop` نیز به شما اجازه می‌دهد کدی را شخصی‌سازی کنید که هنگام خروج یک نمونه از smart pointer از حوزه (scope) اجرا می‌شود. در این فصل، هر دو `trait` را بررسی خواهیم کرد و نشان خواهیم داد که چرا این ویژگی‌ها برای smart pointerها اهمیت دارند. + +از آنجا که الگوی اشاره‌گر (Pointer) هوشمند یک الگوی طراحی کلی است که به‌طور مکرر در Rust استفاده می‌شود، این فصل تمام +اشاره‌گر (Pointer)های هوشمند موجود را پوشش نمی‌دهد. بسیاری از کتابخانه‌ها اشاره‌گر (Pointer)های هوشمند خاص خود را دارند و حتی می‌توانید +اشاره‌گر (Pointer) هوشمند خود را بنویسید. ما رایج‌ترین اشاره‌گر (Pointer)های هوشمند در کتابخانه استاندارد را پوشش خواهیم داد: + +* `Box` برای تخصیص مقادیر در heap +* `Rc`، یک نوع شمارش‌گر رفرنس که امکان مالکیت چندگانه را فراهم می‌کند +* `Ref` و `RefMut`، که از طریق `RefCell` قابل دسترسی هستند؛ نوعی که قوانین قرض‌گرفتن را در زمان اجرا به‌جای زمان کامپایل اعمال می‌کند + +علاوه بر این، الگوی *تغییرپذیری درونی* (interior mutability) را بررسی خواهیم کرد، جایی که یک نوع غیرقابل‌تغییر، یک API برای تغییر مقدار درونی خود فراهم می‌کند. همچنین به چرخه‌های رفرنس (reference cycles) می‌پردازیم: اینکه چگونه می‌توانند باعث نشت حافظه شوند و چگونه می‌توان از آن‌ها جلوگیری کرد. + +بیایید شروع کنیم! diff --git a/src/ch15-01-box.md b/src/ch15-01-box.md index d48b7b7863..8478f657c0 100644 --- a/src/ch15-01-box.md +++ b/src/ch15-01-box.md @@ -1,40 +1,35 @@ -## Using `Box` to Point to Data on the Heap +## استفاده از `Box` برای اشاره به داده‌ها در Heap -The most straightforward smart pointer is a box, whose type is written -`Box`. _Boxes_ allow you to store data on the heap rather than the stack. -What remains on the stack is the pointer to the heap data. Refer to Chapter 4 -to review the difference between the stack and the heap. +ساده‌ترین اشاره‌گر (Pointer) هوشمند یک _جعبه_ است که نوع آن به صورت `Box` نوشته می‌شود. جعبه‌ها به شما امکان می‌دهند +داده‌ها را در heap ذخیره کنید به جای stack. چیزی که در stack باقی می‌ماند، اشاره‌گر (Pointer)ی به داده‌های heap است. برای +مرور تفاوت بین stack و heap به فصل ۴ مراجعه کنید. -Boxes don’t have performance overhead, other than storing their data on the -heap instead of on the stack. But they don’t have many extra capabilities -either. You’ll use them most often in these situations: +جعبه‌ها هیچ سربار عملکردی ندارند، به‌جز ذخیره داده‌های خود در heap به جای stack. اما آن‌ها قابلیت‌های اضافی زیادی +ندارند. شما اغلب آن‌ها را در این موقعیت‌ها استفاده خواهید کرد: -- When you have a type whose size can’t be known at compile time and you want - to use a value of that type in a context that requires an exact size -- When you have a large amount of data and you want to transfer ownership but - ensure the data won’t be copied when you do so -- When you want to own a value and you care only that it’s a type that - implements a particular trait rather than being of a specific type +- هنگامی که نوعی دارید که اندازه آن در زمان کامپایل مشخص نیست و می‌خواهید از مقداری از آن نوع در محیطی که نیاز + به اندازه دقیق دارد استفاده کنید. +- هنگامی که مقدار زیادی داده دارید و می‌خواهید مالکیت را انتقال دهید، اما اطمینان حاصل کنید که داده‌ها هنگام انجام + این کار کپی نمی‌شوند. +- هنگامی که می‌خواهید مالک یک مقدار باشید و فقط اهمیت می‌دهید که آن نوع، یک صفت خاص را پیاده‌سازی کرده باشد + نه اینکه از یک نوع خاص باشد. -We’ll demonstrate the first situation in [“Enabling Recursive Types with -Boxes”](#enabling-recursive-types-with-boxes). In the second -case, transferring ownership of a large amount of data can take a long time -because the data is copied around on the stack. To improve performance in this -situation, we can store the large amount of data on the heap in a box. Then, -only the small amount of pointer data is copied around on the stack, while the -data it references stays in one place on the heap. The third case is known as a -_trait object_, and [“Using Trait Objects That Allow for Values of Different -Types,”][trait-objects] in Chapter 18 is devoted to that topic. -So what you learn here you’ll apply again in that section! +اولین حالت را در بخش [“فعال‌سازی انواع بازگشتی با استفاده از جعبه‌ها”](#enabling-recursive-types-with-boxes) بررسی خواهیم کرد. در حالت دوم، انتقال مالکیت مقدار زیادی داده می‌تواند زمان زیادی بگیرد زیرا داده‌ها +در stack کپی می‌شوند. برای بهبود عملکرد در این حالت، می‌توانیم مقدار زیادی داده را در heap و در یک جعبه ذخیره +کنیم. سپس، تنها مقدار کمی از داده‌های اشاره‌گر (Pointer) در stack کپی می‌شود، در حالی که داده‌هایی که به آن‌ها اشاره می‌کند +در یک مکان در heap باقی می‌مانند. حالت سوم به نام _شیء صفت_ شناخته می‌شود و فصل ۱۸ بخشی کامل به نام [“استفاده +از اشیای صفت که به شما اجازه می‌دهند مقادیر از انواع مختلف داشته باشید”][trait-objects] به این +موضوع اختصاص داده است. بنابراین چیزی که اینجا یاد می‌گیرید، دوباره در فصل ۱۸ استفاده خواهید کرد! -### Using `Box` to Store Data on the Heap +### استفاده از `Box` برای ذخیره داده‌ها در Heap -Before we discuss the heap storage use case for `Box`, we’ll cover the -syntax and how to interact with values stored within a `Box`. +قبل از اینکه مورد استفاده ذخیره در heap برای `Box` را بحث کنیم، نحو و نحوه تعامل با مقادیر ذخیره‌شده در +یک `Box` را پوشش خواهیم داد. -Listing 15-1 shows how to use a box to store an `i32` value on the heap. +لیستینگ ۱۵-۱ نشان می‌دهد چگونه می‌توان از یک جعبه برای ذخیره مقدار `i32` در heap استفاده کرد: -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-01/src/main.rs}} @@ -42,69 +37,56 @@ Listing 15-1 shows how to use a box to store an `i32` value on the heap. -We define the variable `b` to have the value of a `Box` that points to the -value `5`, which is allocated on the heap. This program will print `b = 5`; in -this case, we can access the data in the box similarly to how we would if this -data were on the stack. Just like any owned value, when a box goes out of -scope, as `b` does at the end of `main`, it will be deallocated. The -deallocation happens both for the box (stored on the stack) and the data it -points to (stored on the heap). - -Putting a single value on the heap isn’t very useful, so you won’t use boxes by -themselves in this way very often. Having values like a single `i32` on the -stack, where they’re stored by default, is more appropriate in the majority of -situations. Let’s look at a case where boxes allow us to define types that we -wouldn’t be allowed to define if we didn’t have boxes. - -### Enabling Recursive Types with Boxes - -A value of a _recursive type_ can have another value of the same type as part of -itself. Recursive types pose an issue because Rust needs to know at compile time -how much space a type takes up. However, the nesting of values of recursive -types could theoretically continue infinitely, so Rust can’t know how much space -the value needs. Because boxes have a known size, we can enable recursive types -by inserting a box in the recursive type definition. - -As an example of a recursive type, let’s explore the _cons list_. This is a data -type commonly found in functional programming languages. The cons list type -we’ll define is straightforward except for the recursion; therefore, the -concepts in the example we’ll work with will be useful any time you get into -more complex situations involving recursive types. - -#### More Information About the Cons List - -A _cons list_ is a data structure that comes from the Lisp programming language -and its dialects, is made up of nested pairs, and is the Lisp version of a -linked list. Its name comes from the `cons` function (short for _construct -function_) in Lisp that constructs a new pair from its two arguments. By -calling `cons` on a pair consisting of a value and another pair, we can -construct cons lists made up of recursive pairs. - -For example, here’s a pseudocode representation of a cons list containing the -list `1, 2, 3` with each pair in parentheses: +ما متغیر `b` را تعریف می‌کنیم تا مقدار یک `Box` که به مقدار `5` اشاره می‌کند را داشته باشد، که در heap تخصیص +داده شده است. این برنامه `b = 5` را چاپ می‌کند؛ در این حالت، می‌توانیم به داده‌های موجود در جعبه دسترسی داشته +باشیم، مشابه حالتی که این داده‌ها در stack بودند. درست مثل هر مقدار مالک، وقتی یک جعبه از دامنه خارج می‌شود، همان +طور که `b` در پایان `main` این کار را می‌کند، آزاد می‌شود. آزادسازی هم برای جعبه (ذخیره‌شده در stack) و هم داده‌هایی +که به آن اشاره می‌کند (ذخیره‌شده در heap) اتفاق می‌افتد. + +قرار دادن یک مقدار واحد در heap خیلی مفید نیست، بنابراین جعبه‌ها را به‌تنهایی به این شکل خیلی استفاده نخواهید +کرد. داشتن مقادیری مانند یک `i32` در stack، جایی که به‌طور پیش‌فرض ذخیره می‌شوند، در اکثر موارد مناسب‌تر است. بیایید +به حالتی نگاه کنیم که جعبه‌ها به ما امکان می‌دهند انواعی را تعریف کنیم که بدون آن‌ها نمی‌توانستیم. + +### فعال‌سازی انواع بازگشتی با استفاده از جعبه‌ها + +یک مقدار از نوع _بازگشتی_ می‌تواند مقدار دیگری از همان نوع را به‌عنوان بخشی از خود داشته باشد. انواع بازگشتی +یک مسئله ایجاد می‌کنند زیرا در زمان کامپایل، Rust باید بداند یک نوع چقدر فضا اشغال می‌کند. با این حال، تودرتویی +مقادیر انواع بازگشتی می‌تواند به‌طور نظری بی‌نهایت ادامه یابد، بنابراین Rust نمی‌تواند بداند که مقدار چقدر فضا +نیاز دارد. چون جعبه‌ها یک اندازه مشخص دارند، می‌توانیم انواع بازگشتی را با قرار دادن یک جعبه در تعریف نوع بازگشتی +فعال کنیم. + +به‌عنوان مثالی از یک نوع بازگشتی، بیایید به _لیست cons_ نگاه کنیم. این یک نوع داده است که معمولاً در زبان‌های +برنامه‌نویسی تابعی یافت می‌شود. نوع لیست cons که تعریف خواهیم کرد ساده است به جز بازگشت؛ بنابراین، مفاهیم موجود +در مثالی که با آن کار خواهیم کرد، هر زمان که وارد موقعیت‌های پیچیده‌تری با انواع بازگشتی شوید مفید خواهند بود. + +#### اطلاعات بیشتر درباره لیست Cons + +یک _لیست cons_ یک ساختار داده‌ای است که از زبان برنامه‌نویسی Lisp و گویش‌های آن می‌آید و از جفت‌های تودرتو تشکیل +شده است و نسخه Lisp از یک لیست پیوندی است. نام آن از تابع `cons` (مخفف "تابع ساخت" یا Construct Function) در Lisp +گرفته شده است که یک جفت جدید را از دو آرگومان خود می‌سازد. با فراخوانی `cons` روی یک جفت که شامل یک مقدار و یک جفت +دیگر است، می‌توانیم لیست‌های cons ساخته‌شده از جفت‌های بازگشتی را ایجاد کنیم. + +برای مثال، در اینجا یک نمایش شبه‌کد از یک لیست cons که شامل لیست ۱، ۲، ۳ است آورده شده است که هر جفت در داخل پرانتز +قرار دارد: ```text (1, (2, (3, Nil))) ``` -Each item in a cons list contains two elements: the value of the current item -and the next item. The last item in the list contains only a value called `Nil` -without a next item. A cons list is produced by recursively calling the `cons` -function. The canonical name to denote the base case of the recursion is `Nil`. -Note that this is not the same as the “null” or “nil” concept discussed in -Chapter 6, which is an invalid or absent value. +هر آیتم در یک لیست cons شامل دو عنصر است: مقدار آیتم فعلی و آیتم بعدی. آخرین آیتم در لیست تنها شامل مقداری به نام +`Nil` است و آیتم بعدی ندارد. یک لیست cons با فراخوانی بازگشتی تابع `cons` تولید می‌شود. نام متعارف برای نشان دادن +حالت پایه بازگشت، `Nil` است. توجه داشته باشید که این با مفهوم "null" یا "nil" در فصل ۶ که یک مقدار نامعتبر یا غایب +است، متفاوت است. -The cons list isn’t a commonly used data structure in Rust. Most of the time -when you have a list of items in Rust, `Vec` is a better choice to use. -Other, more complex recursive data types _are_ useful in various situations, -but by starting with the cons list in this chapter, we can explore how boxes -let us define a recursive data type without much distraction. +لیست cons یک ساختار داده‌ای نیست که به‌طور معمول در Rust استفاده شود. در اکثر مواقع وقتی یک لیست از آیتم‌ها در +Rust دارید، استفاده از `Vec` انتخاب بهتری است. سایر انواع بازگشتی پیچیده‌تر _در_ موقعیت‌های مختلف مفید هستند، +اما با شروع از لیست cons در این فصل، می‌توانیم بررسی کنیم که چگونه جعبه‌ها به ما اجازه می‌دهند یک نوع داده بازگشتی +را بدون حواس‌پرتی زیاد تعریف کنیم. -Listing 15-2 contains an enum definition for a cons list. Note that this code -won’t compile yet because the `List` type doesn’t have a known size, which -we’ll demonstrate. +لیستینگ ۱۵-۲ حاوی یک تعریف enum برای یک لیست cons است. توجه داشته باشید که این کد هنوز کامپایل نمی‌شود زیرا نوع +`List` اندازه شناخته‌شده‌ای ندارد، که آن را توضیح خواهیم داد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-02/src/main.rs:here}} @@ -112,13 +94,11 @@ we’ll demonstrate. -> Note: We’re implementing a cons list that holds only `i32` values for the -> purposes of this example. We could have implemented it using generics, as we -> discussed in Chapter 10, to define a cons list type that could store values of -> any type. +> توجه: ما در حال پیاده‌سازی یک لیست cons هستیم که تنها مقادیر `i32` را نگه می‌دارد، برای اهداف این مثال. می‌توانستیم +> آن را با استفاده از جنریک‌ها، همان‌طور که در فصل ۱۰ بحث کردیم، پیاده‌سازی کنیم تا یک نوع لیست cons تعریف کنیم +> که بتواند مقادیر هر نوعی را ذخیره کند. -Using the `List` type to store the list `1, 2, 3` would look like the code in -Listing 15-3. +استفاده از نوع `List` برای ذخیره لیست `1, 2, 3` شبیه به کدی خواهد بود که در لیستینگ ۱۵-۳ آورده شده است: @@ -128,15 +108,14 @@ Listing 15-3. -The first `Cons` value holds `1` and another `List` value. This `List` value is -another `Cons` value that holds `2` and another `List` value. This `List` value -is one more `Cons` value that holds `3` and a `List` value, which is finally -`Nil`, the non-recursive variant that signals the end of the list. +اولین مقدار `Cons` مقدار `1` و یک مقدار دیگر از نوع `List` را نگه می‌دارد. این مقدار `List` یک مقدار دیگر از نوع +`Cons` است که مقدار `2` و یک مقدار دیگر از نوع `List` را نگه می‌دارد. این مقدار `List` یک مقدار دیگر از نوع `Cons` +را نگه می‌دارد که مقدار `3` و یک مقدار دیگر از نوع `List` را دارد که در نهایت `Nil`، متغیر غیر بازگشتی که پایان +لیست را نشان می‌دهد، است. -If we try to compile the code in Listing 15-3, we get the error shown in -Listing 15-4. +اگر سعی کنیم کد در لیستینگ ۱۵-۳ را کامپایل کنیم، خطایی را دریافت می‌کنیم که در لیستینگ ۱۵-۴ نشان داده شده است: -+ ```console {{#include ../listings/ch15-smart-pointers/listing-15-03/output.txt}} @@ -144,46 +123,40 @@ Listing 15-4. -The error shows this type “has infinite size.” The reason is that we’ve defined -`List` with a variant that is recursive: it holds another value of itself -directly. As a result, Rust can’t figure out how much space it needs to store a -`List` value. Let’s break down why we get this error. First we’ll look at how -Rust decides how much space it needs to store a value of a non-recursive type. +خطا نشان می‌دهد که این نوع "اندازه بی‌نهایت" دارد. دلیل این است که ما `List` را با یک متغیر تعریف کرده‌ایم که +بازگشتی است: به‌طور مستقیم یک مقدار دیگر از نوع خود را نگه می‌دارد. در نتیجه، Rust نمی‌تواند بفهمد چقدر فضا نیاز +دارد تا یک مقدار از نوع `List` را ذخیره کند. بیایید بررسی کنیم چرا این خطا را دریافت می‌کنیم. ابتدا، نگاهی به این +می‌اندازیم که Rust چگونه تصمیم می‌گیرد چه مقدار فضا برای ذخیره یک مقدار از نوع غیر بازگشتی نیاز دارد. -#### Computing the Size of a Non-Recursive Type +#### محاسبه اندازه یک نوع غیر بازگشتی -Recall the `Message` enum we defined in Listing 6-2 when we discussed enum -definitions in Chapter 6: +ساختار `Message` را که در لیستینگ ۶-۲ تعریف کرده‌ایم، به‌خاطر بیاورید وقتی که در فصل ۶ در مورد تعریف‌های enum +بحث کردیم: ```rust {{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-02/src/main.rs:here}} ``` -To determine how much space to allocate for a `Message` value, Rust goes -through each of the variants to see which variant needs the most space. Rust -sees that `Message::Quit` doesn’t need any space, `Message::Move` needs enough -space to store two `i32` values, and so forth. Because only one variant will be -used, the most space a `Message` value will need is the space it would take to -store the largest of its variants. +برای تعیین اینکه چقدر فضا برای یک مقدار از نوع `Message` اختصاص داده شود، Rust هر یک از متغیرها را بررسی می‌کند +تا ببیند کدام متغیر بیشترین فضا را نیاز دارد. Rust می‌بیند که `Message::Quit` نیازی به فضا ندارد، `Message::Move` +نیاز به فضای کافی برای ذخیره دو مقدار `i32` دارد، و همین‌طور ادامه می‌دهد. چون تنها یک متغیر استفاده خواهد شد، +بیشترین فضای مورد نیاز برای یک مقدار `Message` فضایی است که بزرگ‌ترین متغیر آن اشغال می‌کند. -Contrast this with what happens when Rust tries to determine how much space a -recursive type like the `List` enum in Listing 15-2 needs. The compiler starts -by looking at the `Cons` variant, which holds a value of type `i32` and a value -of type `List`. Therefore, `Cons` needs an amount of space equal to the size of -an `i32` plus the size of a `List`. To figure out how much memory the `List` -type needs, the compiler looks at the variants, starting with the `Cons` -variant. The `Cons` variant holds a value of type `i32` and a value of type -`List`, and this process continues infinitely, as shown in Figure 15-1. +این را با حالتی مقایسه کنید که Rust سعی می‌کند تعیین کند چه مقدار فضا برای یک نوع بازگشتی مانند enum `List` در +لیستینگ ۱۵-۲ نیاز است. کامپایلر با نگاه کردن به متغیر `Cons` شروع می‌کند که یک مقدار از نوع `i32` و یک مقدار از نوع +`List` را نگه می‌دارد. بنابراین، `Cons` به فضایی معادل اندازه یک `i32` به‌علاوه اندازه یک `List` نیاز دارد. برای +فهمیدن اینکه نوع `List` به چه مقدار حافظه نیاز دارد، کامپایلر متغیرها را بررسی می‌کند و از متغیر `Cons` شروع می‌کند. +متغیر `Cons` یک مقدار از نوع `i32` و یک مقدار از نوع `List` را نگه می‌دارد، و این فرآیند به‌طور بی‌نهایت ادامه +می‌یابد، همان‌طور که در شکل ۱۵-۱ نشان داده شده است. -An infinite Cons list: a rectangle labeled 'Cons' split into two smaller rectangles. The first smaller rectangle holds the label 'i32', and the second smaller rectangle holds the label 'Cons' and a smaller version of the outer 'Cons' rectangle. The 'Cons' rectangles continue to hold smaller and smaller versions of themselves until the smallest comfortably-sized rectangle holds an infinity symbol, indicating that this repetition goes on forever +یک لیست Cons بی‌نهایت: یک مستطیل با برچسب 'Cons' که به دو مستطیل کوچکتر تقسیم شده است. مستطیل اول دارای برچسب 'i32' و مستطیل دوم دارای برچسب 'Cons' است و نسخه‌ای کوچکتر از مستطیل بیرونی 'Cons' را در خود دارد. این مستطیل‌های 'Cons' همچنان نسخه‌های کوچکتری از خود را درون خود نگه می‌دارند تا زمانی که کوچک‌ترین مستطیل قابل‌نمایش یک نماد بی‌نهایت را در خود دارد، که نشان می‌دهد این تکرار تا بی‌نهایت ادامه می‌یابد. -Figure 15-1: An infinite `List` consisting of infinite -`Cons` variants +شکل ۱۵-۱: یک `List` بی‌نهایت شامل متغیرهای `Cons` بی‌نهایت -#### Using `Box` to Get a Recursive Type with a Known Size +#### استفاده از `Box` برای به‌دست آوردن یک نوع بازگشتی با اندازه شناخته‌شده -Because Rust can’t figure out how much space to allocate for recursively -defined types, the compiler gives an error with this helpful suggestion: +چون Rust نمی‌تواند بفهمد چه مقدار فضا باید برای انواع تعریف‌شده به‌صورت بازگشتی تخصیص دهد، کامپایلر با این پیشنهاد +کمکی خطا می‌دهد: -Implementing the `Deref` trait allows you to customize the behavior of the -_dereference operator_ `*` (not to be confused with the multiplication or glob -operator). By implementing `Deref` in such a way that a smart pointer can be -treated like a regular reference, you can write code that operates on -references and use that code with smart pointers too. +پیاده‌سازی ویژگی `Deref` به شما امکان می‌دهد رفتار _عملگر اشاره‌گر (Pointer)‌زدایی_ `*` را سفارشی کنید (این را با عملگر +ضرب یا glob اشتباه نگیرید). با پیاده‌سازی `Deref` به گونه‌ای که یک اشاره‌گر (Pointer) هوشمند بتواند مانند یک ارجاع معمولی +رفتار کند، می‌توانید کدی بنویسید که روی ارجاعات عمل می‌کند و از آن کد با اشاره‌گر (Pointer)های هوشمند نیز استفاده کنید. -Let’s first look at how the dereference operator works with regular references. -Then we’ll try to define a custom type that behaves like `Box`, and see why -the dereference operator doesn’t work like a reference on our newly defined -type. We’ll explore how implementing the `Deref` trait makes it possible for -smart pointers to work in ways similar to references. Then we’ll look at -Rust’s _deref coercion_ feature and how it lets us work with either references -or smart pointers. +ابتدا بیایید نگاهی به این بیندازیم که چگونه عملگر اشاره‌گر (Pointer)‌زدایی با ارجاعات معمولی کار می‌کند. سپس سعی می‌کنیم یک +نوع سفارشی تعریف کنیم که مانند `Box` رفتار کند، و بررسی کنیم چرا عملگر اشاره‌گر (Pointer)‌زدایی مانند یک ارجاع روی نوع +جدید ما عمل نمی‌کند. ما بررسی می‌کنیم که چگونه پیاده‌سازی ویژگی `Deref` امکان‌پذیر می‌سازد که اشاره‌گر (Pointer)های هوشمند +به شیوه‌ای مشابه ارجاعات عمل کنند. سپس نگاهی به ویژگی _فشار اشاره‌گر (Pointer)‌زدایی_ (deref coercion) در Rust می‌اندازیم و +اینکه چگونه به ما اجازه می‌دهد با ارجاعات یا اشاره‌گر (Pointer)های هوشمند کار کنیم. - +> توجه: یک تفاوت بزرگ بین نوع `MyBox` که قرار است بسازیم و `Box` واقعی وجود دارد: نسخه ما داده‌های خود +> را در heap ذخیره نمی‌کند. ما این مثال را بر روی `Deref` متمرکز کرده‌ایم، بنابراین مکانی که داده‌ها واقعاً در +> آن ذخیره می‌شوند کمتر از رفتار اشاره‌گر (Pointer)گونه اهمیت دارد. + + -### Following the Reference to the Value +### دنبال کردن اشاره‌گر (Pointer) به مقدار -A regular reference is a type of pointer, and one way to think of a pointer is -as an arrow to a value stored somewhere else. In Listing 15-6, we create a -reference to an `i32` value and then use the dereference operator to follow the -reference to the value. +یک ارجاع معمولی نوعی اشاره‌گر (Pointer) است، و یکی از راه‌های فکر کردن به یک اشاره‌گر (Pointer) این است که به عنوان یک فلش به یک +مقدار ذخیره‌شده در جای دیگری در نظر گرفته شود. در لیستینگ ۱۵-۶، ما یک ارجاع به یک مقدار `i32` ایجاد می‌کنیم و +سپس از عملگر اشاره‌گر (Pointer)‌زدایی برای دنبال کردن ارجاع به مقدار استفاده می‌کنیم: -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-06/src/main.rs}} @@ -38,32 +37,28 @@ reference to the value. -The variable `x` holds an `i32` value `5`. We set `y` equal to a reference to -`x`. We can assert that `x` is equal to `5`. However, if we want to make an -assertion about the value in `y`, we have to use `*y` to follow the reference -to the value it’s pointing to (hence _dereference_) so the compiler can compare -the actual value. Once we dereference `y`, we have access to the integer value -`y` is pointing to that we can compare with `5`. +متغیر `x` مقدار `i32` برابر با `5` را نگه می‌دارد. ما `y` را برابر با یک ارجاع به `x` تنظیم می‌کنیم. می‌توانیم +تایید کنیم که `x` برابر با `5` است. با این حال، اگر بخواهیم یک تایید روی مقدار داخل `y` انجام دهیم، باید از +`*y` برای دنبال کردن ارجاع به مقداری که به آن اشاره می‌کند استفاده کنیم (بنابراین _اشاره‌گر (Pointer)‌زدایی_) تا کامپایلر +بتواند مقدار واقعی را مقایسه کند. وقتی `y` را اشاره‌گر (Pointer)‌زدایی می‌کنیم، به مقدار صحیحی که `y` به آن اشاره می‌کند +دسترسی داریم و می‌توانیم آن را با `5` مقایسه کنیم. -If we tried to write `assert_eq!(5, y);` instead, we would get this compilation -error: +اگر بخواهیم `assert_eq!(5, y);` بنویسیم، خطای کامپایل زیر را دریافت می‌کنیم: ```console {{#include ../listings/ch15-smart-pointers/output-only-01-comparing-to-reference/output.txt}} ``` -Comparing a number and a reference to a number isn’t allowed because they’re -different types. We must use the dereference operator to follow the reference -to the value it’s pointing to. +مقایسه یک عدد با یک ارجاع به عدد مجاز نیست زیرا آن‌ها انواع متفاوتی هستند. ما باید از عملگر اشاره‌گر (Pointer)‌زدایی برای +دنبال کردن ارجاع به مقداری که به آن اشاره می‌کند استفاده کنیم. -### Using `Box` Like a Reference +### استفاده از `Box` مانند یک ارجاع -We can rewrite the code in Listing 15-6 to use a `Box` instead of a -reference; the dereference operator used on the `Box` in Listing 15-7 -functions in the same way as the dereference operator used on the reference in -Listing 15-6. +ما می‌توانیم کد لیستینگ ۱۵-۶ را برای استفاده از یک `Box` به‌جای یک ارجاع بازنویسی کنیم؛ عملگر اشاره‌گر (Pointer)‌زدایی +که روی `Box` در لیستینگ ۱۵-۷ استفاده شده است، به همان شیوه‌ای عمل می‌کند که روی ارجاع در لیستینگ ۱۵-۶ عمل +می‌کرد: -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-07/src/main.rs}} @@ -71,30 +66,21 @@ Listing 15-6. -The main difference between Listing 15-7 and Listing 15-6 is that here we set -`y` to be an instance of a box pointing to a copied value of `x` rather than a -reference pointing to the value of `x`. In the last assertion, we can use the -dereference operator to follow the box’s pointer in the same way that we did -when `y` was a reference. Next, we’ll explore what is special about `Box` -that enables us to use the dereference operator by defining our own box type. +تفاوت اصلی بین لیستینگ ۱۵-۷ و لیستینگ ۱۵-۶ این است که در اینجا `y` را به‌عنوان یک نمونه از `Box` تنظیم می‌کنیم +که به یک مقدار کپی‌شده از `x` اشاره می‌کند، به‌جای یک ارجاع که به مقدار `x` اشاره می‌کند. در تایید نهایی، +می‌توانیم از عملگر اشاره‌گر (Pointer)‌زدایی برای دنبال کردن اشاره‌گر (Pointer) `Box` به همان شیوه‌ای که زمانی که `y` یک ارجاع +بود استفاده کردیم. در ادامه بررسی می‌کنیم چه چیزی در مورد `Box` خاص است که به ما اجازه می‌دهد از عملگر +اشاره‌گر (Pointer)‌زدایی استفاده کنیم، با تعریف نوع خودمان. -### Defining Our Own Smart Pointer +### تعریف اشاره‌گر (Pointer) هوشمند خودمان -Let’s build a wrapper type similar to the `Box` type provided by the -standard library to experience how smart pointer types behave differently from -references by default. Then we’ll look at how to add the ability to use the -dereference operator. +بیایید یک نوع پوشاننده (wrapper type) مشابه با نوع `Box` که توسط کتابخانه استاندارد ارائه شده است بسازیم تا تجربه کنیم که چگونه انواع اشاره‌گر هوشمند به‌طور پیش‌فرض رفتاری متفاوت از رفرنس‌ها دارند. سپس بررسی خواهیم کرد که چگونه می‌توان قابلیت استفاده از عملگر dereference را به آن افزود. -> Note: There’s one big difference between the `MyBox` type we’re about to -> build and the real `Box`: our version will not store its data on the heap. -> We are focusing this example on `Deref`, so where the data is actually stored -> is less important than the pointer-like behavior. +> نکته: یک تفاوت بزرگ بین نوع `MyBox` که در شرف ساخت آن هستیم و `Box` واقعی وجود دارد: نسخه‌ی ما داده‌ها را در heap ذخیره نخواهد کرد. ما در این مثال بر `Deref` تمرکز داریم، بنابراین محل واقعی ذخیره‌سازی داده‌ها اهمیت کمتری نسبت به رفتار مشابه با اشاره‌گر دارد. -The `Box` type is ultimately defined as a tuple struct with one element, so -Listing 15-8 defines a `MyBox` type in the same way. We’ll also define a -`new` function to match the `new` function defined on `Box`. +نوع `Box` در نهایت به‌صورت یک `tuple struct` با یک عضو تعریف شده است، بنابراین در لیست 15-8 نوع `MyBox` را به همان شیوه تعریف می‌کنیم. همچنین تابعی با نام `new` تعریف خواهیم کرد تا با تابع `new` که روی `Box` تعریف شده، مطابقت داشته باشد. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-08/src/main.rs:here}} @@ -102,17 +88,15 @@ Listing 15-8 defines a `MyBox` type in the same way. We’ll also define a -We define a struct named `MyBox` and declare a generic parameter `T` because -we want our type to hold values of any type. The `MyBox` type is a tuple struct -with one element of type `T`. The `MyBox::new` function takes one parameter of -type `T` and returns a `MyBox` instance that holds the value passed in. +ما یک ساختار با نام `MyBox` تعریف می‌کنیم و یک پارامتر جنریک `T` اعلام می‌کنیم، زیرا می‌خواهیم نوع ما مقادیر +از هر نوعی را نگه دارد. نوع `MyBox` یک ساختار tuple با یک عنصر از نوع `T` است. تابع `MyBox::new` یک پارامتر از نوع +`T` می‌گیرد و یک نمونه از `MyBox` که مقدار ورودی را نگه می‌دارد برمی‌گرداند. -Let’s try adding the `main` function in Listing 15-7 to Listing 15-8 and -changing it to use the `MyBox` type we’ve defined instead of `Box`. The -code in Listing 15-9 won’t compile because Rust doesn’t know how to dereference -`MyBox`. +بیایید تابع `main` در لیستینگ ۱۵-۷ را به لیستینگ ۱۵-۸ اضافه کنیم و آن را برای استفاده از نوع `MyBox` که +تعریف کرده‌ایم، به جای `Box` تغییر دهیم. کد موجود در لیستینگ ۱۵-۹ کامپایل نخواهد شد، زیرا Rust نمی‌داند +چگونه `MyBox` را اشاره‌گر (Pointer)‌زدایی کند. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-09/src/main.rs:here}} @@ -120,30 +104,24 @@ code in Listing 15-9 won’t compile because Rust doesn’t know how to derefere -Here’s the resultant compilation error: +در اینجا خطای کامپایل که نتیجه می‌شود: ```console {{#include ../listings/ch15-smart-pointers/listing-15-09/output.txt}} ``` -Our `MyBox` type can’t be dereferenced because we haven’t implemented that -ability on our type. To enable dereferencing with the `*` operator, we -implement the `Deref` trait. +نوع `MyBox` ما نمی‌تواند اشاره‌گر (Pointer)‌زدایی شود زیرا ما این قابلیت را روی نوع خود پیاده‌سازی نکرده‌ایم. برای فعال +کردن اشاره‌گر (Pointer)‌زدایی با عملگر `*`، ما ویژگی `Deref` را پیاده‌سازی می‌کنیم. -### Implementing the `Deref` Trait +### پیاده‌سازی `Deref` Trait -As discussed in [“Implementing a Trait on a Type”][impl-trait] in -Chapter 10, to implement a trait we need to provide implementations for the -trait’s required methods. The `Deref` trait, provided by the standard library, -requires us to implement one method named `deref` that borrows `self` and -returns a reference to the inner data. Listing 15-10 contains an implementation -of `Deref` to add to the definition of `MyBox`. +همان‌طور که در بخش [«پیاده‌سازی یک Trait روی یک نوع»][impl-trait] در فصل ۱۰ بحث شد، برای پیاده‌سازی یک trait باید پیاده‌سازی‌هایی برای متدهای موردنیاز آن trait ارائه دهیم. Trait به نام `Deref` که توسط کتابخانه استاندارد ارائه شده است، از ما می‌خواهد که یک متد به نام `deref` پیاده‌سازی کنیم که `self` را به‌صورت وام‌گرفته دریافت کرده و یک رفرنس به داده درونی بازمی‌گرداند. لیست 15-10 پیاده‌سازی‌ای از `Deref` را نشان می‌دهد که باید به تعریف `MyBox` اضافه شود. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-10/src/main.rs:here}} @@ -151,72 +129,47 @@ of `Deref` to add to the definition of `MyBox`. -The `type Target = T;` syntax defines an associated type for the `Deref` -trait to use. Associated types are a slightly different way of declaring a -generic parameter, but you don’t need to worry about them for now; we’ll cover -them in more detail in Chapter 20. +سینتکس `type Target = T;` یک نوع مرتبط برای ویژگی `Deref` تعریف می‌کند تا از آن استفاده کند. نوع‌های مرتبط +یک روش کمی متفاوت برای اعلام یک پارامتر جنریک هستند، اما نیازی نیست در حال حاضر نگران آن‌ها باشید؛ ما در فصل ۲۰ +جزئیات بیشتری درباره آن‌ها ارائه خواهیم داد. -We fill in the body of the `deref` method with `&self.0` so `deref` returns a -reference to the value we want to access with the `*` operator; recall from -[“Using Tuple Structs Without Named Fields to Create Different -Types”][tuple-structs] in Chapter 5 that `.0` accesses the first -value in a tuple struct. The `main` function in Listing 15-9 that calls `*` on -the `MyBox` value now compiles, and the assertions pass! +بدنه‌ی متد `deref` را با `&self.0` پر می‌کنیم تا `deref` یک رفرنس به مقداری که می‌خواهیم با عملگر `*` به آن دسترسی پیدا کنیم، برگرداند؛ به یاد داشته باشید از بخش \[«استفاده از tuple struct‌ها بدون فیلدهای نام‌دار برای ایجاد نوع‌های مختلف»]\[tuple-structs] در فصل ۵ که `.0` به اولین مقدار در یک `tuple struct` دسترسی پیدا می‌کند. تابع `main` در لیست ۱۵-۹ که روی مقدار `MyBox` عمل `*` را فراخوانی می‌کند اکنون کامپایل می‌شود و عبارت‌های `assert` نیز با موفقیت عبور می‌کنند! -Without the `Deref` trait, the compiler can only dereference `&` references. -The `deref` method gives the compiler the ability to take a value of any type -that implements `Deref` and call the `deref` method to get an `&` reference that -it knows how to dereference. +بدون `trait` به نام `Deref`، کامپایلر فقط می‌تواند رفرنس‌های `&` را dereference کند. متد `deref` این امکان را به کامپایلر می‌دهد که بتواند یک مقدار از هر نوعی که `Deref` را پیاده‌سازی کرده بگیرد و متد `deref` را روی آن صدا بزند تا یک رفرنس `&` دریافت کند که بتواند آن را dereference کند. -When we entered `*y` in Listing 15-9, behind the scenes Rust actually ran this -code: +وقتی که در لیستینگ ۱۵-۹ `*y` وارد کردیم، پشت صحنه Rust در واقع این کد را اجرا کرد: ```rust,ignore *(y.deref()) ``` -Rust substitutes the `*` operator with a call to the `deref` method and then a -plain dereference so we don’t have to think about whether or not we need to -call the `deref` method. This Rust feature lets us write code that functions -identically whether we have a regular reference or a type that implements -`Deref`. - -The reason the `deref` method returns a reference to a value, and that the -plain dereference outside the parentheses in `*(y.deref())` is still necessary, -has to do with the ownership system. If the `deref` method returned the value -directly instead of a reference to the value, the value would be moved out of -`self`. We don’t want to take ownership of the inner value inside `MyBox` in -this case or in most cases where we use the dereference operator. - -Note that the `*` operator is replaced with a call to the `deref` method and -then a call to the `*` operator just once, each time we use a `*` in our code. -Because the substitution of the `*` operator does not recurse infinitely, we -end up with data of type `i32`, which matches the `5` in `assert_eq!` in -Listing 15-9. - -### Implicit Deref Coercions with Functions and Methods - -_Deref coercion_ converts a reference to a type that implements the `Deref` -trait into a reference to another type. For example, deref coercion can convert -`&String` to `&str` because `String` implements the `Deref` trait such that it -returns `&str`. Deref coercion is a convenience Rust performs on arguments to -functions and methods, and works only on types that implement the `Deref` -trait. It happens automatically when we pass a reference to a particular type’s -value as an argument to a function or method that doesn’t match the parameter -type in the function or method definition. A sequence of calls to the `deref` -method converts the type we provided into the type the parameter needs. - -Deref coercion was added to Rust so that programmers writing function and -method calls don’t need to add as many explicit references and dereferences -with `&` and `*`. The deref coercion feature also lets us write more code that -can work for either references or smart pointers. - -To see deref coercion in action, let’s use the `MyBox` type we defined in -Listing 15-8 as well as the implementation of `Deref` that we added in Listing -15-10. Listing 15-11 shows the definition of a function that has a string slice -parameter. - -+Rust عملگر `*` را با یک فراخوانی به متد `deref` و سپس یک اشاره‌گر (Pointer)زدایی ساده جایگزین می‌کند، بنابراین لازم نیست +درباره این فکر کنیم که آیا نیاز به فراخوانی متد `deref` داریم یا نه. این ویژگی Rust به ما اجازه می‌دهد کدی بنویسیم که +خواه ارجاع معمولی باشد یا نوعی که `Deref` را پیاده‌سازی کرده باشد، به طور یکسان عمل کند. + +دلیلی که متد `deref` یک رفرنس به یک مقدار بازمی‌گرداند، و این‌که هنوز هم نیاز داریم از عملگر dereference ساده خارج از پرانتزها در `*(y.deref())` استفاده کنیم، به سیستم مالکیت مربوط می‌شود. اگر متد `deref` مقدار را به‌صورت مستقیم بازمی‌گرداند به‌جای بازگرداندن یک رفرنس به مقدار، آنگاه آن مقدار از `self` خارج (move) می‌شد. ما نمی‌خواهیم در این حالت، یا در بیشتر حالت‌هایی که از عملگر dereference استفاده می‌کنیم، مالکیت مقدار درونی در `MyBox` را به‌دست بگیریم. + +توجه داشته باشید که عملگر `*` با یک فراخوانی به متد `deref` و سپس یک فراخوانی به عملگر `*` فقط یک بار جایگزین +می‌شود، هر بار که از `*` در کدمان استفاده می‌کنیم. از آنجایی که جایگزینی عملگر `*` بی‌نهایت تکرار نمی‌شود، در +نهایت به داده‌ای از نوع `i32` می‌رسیم که با `5` در `assert_eq!` در لیستینگ ۱۵-۹ مطابقت دارد. + +### فشار اشاره‌گر (Pointer)زدایی ضمنی با توابع و متدها + +_فشار اشاره‌گر (Pointer)زدایی_ (Deref coercion) یک ارجاع به نوعی که ویژگی `Deref` را پیاده‌سازی کرده است به یک ارجاع به +نوعی دیگر تبدیل می‌کند. برای مثال، فشار اشاره‌گر (Pointer)زدایی می‌تواند `&String` را به `&str` تبدیل کند، زیرا +`String` ویژگی `Deref` را به گونه‌ای پیاده‌سازی کرده است که `&str` بازمی‌گرداند. فشار اشاره‌گر (Pointer)زدایی یک +ویژگی کاربردی در Rust است که روی آرگومان‌های توابع و متدها اعمال می‌شود و فقط روی انواعی که ویژگی `Deref` +را پیاده‌سازی کرده‌اند عمل می‌کند. این ویژگی به‌صورت خودکار زمانی که یک ارجاع به مقدار یک نوع خاص به‌عنوان +آرگومان به یک تابع یا متدی که نوع پارامتر آن با تعریف تابع یا متد مطابقت ندارد، اتفاق می‌افتد. یک توالی از +فراخوانی‌های متد `deref` نوعی را که ارائه داده‌ایم به نوعی که پارامتر نیاز دارد تبدیل می‌کند. + +فشار اشاره‌گر (Pointer)زدایی به Rust اضافه شد تا برنامه‌نویسانی که توابع و متدها را می‌نویسند نیاز نداشته باشند +مرجع‌دهی‌ها و اشاره‌گر (Pointer)زدایی‌های واضح زیادی با `&` و `*` اضافه کنند. این ویژگی همچنین به ما امکان می‌دهد +کدی بنویسیم که می‌تواند برای ارجاعات یا اشاره‌گر (Pointer)های هوشمند کار کند. + +برای دیدن عملکرد *تبدیل خودکار با استفاده از `deref`* (deref coercion) در عمل، بیایید از نوع `MyBox` که در لیستینگ 15-8 تعریف کردیم، همراه با پیاده‌سازی `Deref` که در لیستینگ 15-10 اضافه کردیم، استفاده کنیم. لیستینگ 15-11 تعریفی از یک تابع را نشان می‌دهد که یک پارامتر از نوع اسلایس رشته (`&str`) دارد. + + ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-11/src/main.rs:here}} @@ -224,11 +177,11 @@ parameter. -We can call the `hello` function with a string slice as an argument, such as -`hello("Rust");`, for example. Deref coercion makes it possible to call `hello` -with a reference to a value of type `MyBox`, as shown in Listing 15-12. +می‌توانیم تابع `hello` را با یک اسلایس رشته به‌عنوان آرگومان فراخوانی کنیم، مانند `hello("Rust");` برای مثال. +فشار اشاره‌گر (Pointer)زدایی این امکان را فراهم می‌کند که `hello` را با یک ارجاع به یک مقدار از نوع `MyBox` +فراخوانی کنیم، همان‌طور که در لیستینگ ۱۵-۱۲ نشان داده شده است: -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-12/src/main.rs:here}} @@ -236,19 +189,16 @@ with a reference to a value of type `MyBox`, as shown in Listing 15-12. -Here we’re calling the `hello` function with the argument `&m`, which is a -reference to a `MyBox` value. Because we implemented the `Deref` trait -on `MyBox` in Listing 15-10, Rust can turn `&MyBox` into `&String` -by calling `deref`. The standard library provides an implementation of `Deref` -on `String` that returns a string slice, and this is in the API documentation -for `Deref`. Rust calls `deref` again to turn the `&String` into `&str`, which -matches the `hello` function’s definition. +در اینجا ما تابع `hello` را با آرگومان `&m` که یک ارجاع به یک مقدار `MyBox` است فراخوانی می‌کنیم. +از آنجا که ما ویژگی `Deref` را روی `MyBox` در لیستینگ ۱۵-۱۰ پیاده‌سازی کردیم، Rust می‌تواند `&MyBox` +را به `&String` با فراخوانی `deref` تبدیل کند. کتابخانه استاندارد پیاده‌سازی ویژگی `Deref` روی `String` +را ارائه می‌دهد که یک اسلایس رشته بازمی‌گرداند، و این در مستندات API برای `Deref` ذکر شده است. Rust متد +`deref` را دوباره فراخوانی می‌کند تا `&String` را به `&str` تبدیل کند که با تعریف تابع `hello` مطابقت دارد. -If Rust didn’t implement deref coercion, we would have to write the code in -Listing 15-13 instead of the code in Listing 15-12 to call `hello` with a value -of type `&MyBox`. +اگر Rust فشار اشاره‌گر (Pointer)زدایی را پیاده‌سازی نکرده بود، مجبور بودیم کدی مانند لیستینگ ۱۵-۱۳ را به‌جای کد +لیستینگ ۱۵-۱۲ بنویسیم تا `hello` را با یک مقدار از نوع `&MyBox` فراخوانی کنیم. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-13/src/main.rs:here}} @@ -256,47 +206,38 @@ of type `&MyBox`. -The `(*m)` dereferences the `MyBox` into a `String`. Then the `&` and -`[..]` take a string slice of the `String` that is equal to the whole string to -match the signature of `hello`. This code without deref coercions is harder to -read, write, and understand with all of these symbols involved. Deref coercion -allows Rust to handle these conversions for us automatically. - -When the `Deref` trait is defined for the types involved, Rust will analyze the -types and use `Deref::deref` as many times as necessary to get a reference to -match the parameter’s type. The number of times that `Deref::deref` needs to be -inserted is resolved at compile time, so there is no runtime penalty for taking -advantage of deref coercion! - -### How Deref Coercion Interacts with Mutability - -Similar to how you use the `Deref` trait to override the `*` operator on -immutable references, you can use the `DerefMut` trait to override the `*` -operator on mutable references. - -Rust does deref coercion when it finds types and trait implementations in three -cases: - -1. From `&T` to `&U` when `T: Deref` -2. From `&mut T` to `&mut U` when `T: DerefMut` -3. From `&mut T` to `&U` when `T: Deref` - -The first two cases are the same except that the second implements mutability. -The first case states that if you have a `&T`, and `T` implements `Deref` to -some type `U`, you can get a `&U` transparently. The second case states that the -same deref coercion happens for mutable references. - -The third case is trickier: Rust will also coerce a mutable reference to an -immutable one. But the reverse is _not_ possible: immutable references will -never coerce to mutable references. Because of the borrowing rules, if you have -a mutable reference, that mutable reference must be the only reference to that -data (otherwise, the program wouldn’t compile). Converting one mutable -reference to one immutable reference will never break the borrowing rules. -Converting an immutable reference to a mutable reference would require that the -initial immutable reference is the only immutable reference to that data, but -the borrowing rules don’t guarantee that. Therefore, Rust can’t make the -assumption that converting an immutable reference to a mutable reference is -possible. +عملگر `(*m)` مقدار `MyBox` را به یک `String` اشاره‌گر (Pointer) زدایی می‌کند. سپس `&` و `[..]` یک برش رشته‌ای از +`String` می‌گیرند که برابر با کل رشته است تا با امضای تابع `hello` تطابق داشته باشد. این کد بدون فشار +اشاره‌گر (Pointer)زدایی با تمام این نمادها دشوارتر برای خواندن، نوشتن و درک است. فشار اشاره‌گر (Pointer)زدایی به Rust اجازه می‌دهد +این تبدیل‌ها را به‌صورت خودکار برای ما انجام دهد. + +وقتی ویژگی `Deref` برای انواع درگیر تعریف شود، Rust انواع را تحلیل می‌کند و از `Deref::deref` به دفعات لازم +استفاده می‌کند تا یک ارجاع برای مطابقت با نوع پارامتر به دست آید. تعداد دفعاتی که نیاز به فراخوانی +`Deref::deref` است در زمان کامپایل حل می‌شود، بنابراین هیچ هزینه‌ای در زمان اجرا برای استفاده از فشار +اشاره‌گر (Pointer)زدایی وجود ندارد! + +### نحوه تعامل فشار اشاره‌گر (Pointer)زدایی با قابلیت تغییرپذیری + +مشابه نحوه استفاده از ویژگی `Deref` برای بازنویسی عملگر `*` روی ارجاعات غیرقابل تغییر، می‌توانید از ویژگی +`DerefMut` برای بازنویسی عملگر `*` روی ارجاعات قابل تغییر استفاده کنید. + +Rust هنگام پیدا کردن انواع و پیاده‌سازی‌های ویژگی در سه حالت فشار اشاره‌گر (Pointer)زدایی را انجام می‌دهد: + +۱. از `&T` به `&U` زمانی که `T: Deref` باشد +۲. از `&mut T` به `&mut U` زمانی که `T: DerefMut` باشد +۳. از `&mut T` به `&U` زمانی که `T: Deref` باشد + +دو مورد اول مشابه یکدیگر هستند، با این تفاوت که مورد دوم، قابلیت تغییر (`mutability`) را نیز پیاده‌سازی می‌کند. +مورد اول بیان می‌کند که اگر یک `&T` داشته باشید و `T` پیاده‌ساز `Deref` برای نوعی `U` باشد، می‌توانید به‌صورت شفاف (بدون نیاز به تبدیل دستی) یک `&U` دریافت کنید. +مورد دوم نیز بیان می‌کند که همین تبدیل `deref coercion` برای رفرنس‌های قابل تغییر نیز اعمال می‌شود. + +حالت سوم پیچیده‌تر است: Rust همچنین یک ارجاع قابل تغییر را به یک ارجاع غیرقابل تغییر تبدیل می‌کند. اما +عکس آن ممکن _نیست_: ارجاعات غیرقابل تغییر هرگز به ارجاعات قابل تغییر تبدیل نمی‌شوند. به دلیل قوانین +قرض‌گیری، اگر یک ارجاع قابل تغییر داشته باشید، آن ارجاع قابل تغییر باید تنها ارجاع به آن داده باشد (در غیر +این صورت، برنامه کامپایل نمی‌شد). تبدیل یک ارجاع قابل تغییر به یک ارجاع غیرقابل تغییر هرگز قوانین قرض‌گیری +را نمی‌شکند. تبدیل یک ارجاع غیرقابل تغییر به یک ارجاع قابل تغییر نیازمند این است که ارجاع غیرقابل تغییر اولیه +تنها ارجاع غیرقابل تغییر به آن داده باشد، اما قوانین قرض‌گیری این را تضمین نمی‌کنند. بنابراین، Rust نمی‌تواند +فرض کند که تبدیل یک ارجاع غیرقابل تغییر به یک ارجاع قابل تغییر امکان‌پذیر است. [impl-trait]: ch10-02-traits.html#implementing-a-trait-on-a-type -[tuple-structs]: ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types +[tuple-structs]: ch05-01-defining-structs.html#using-tuple-structs-without-named-fields-to-create-different-types \ No newline at end of file diff --git a/src/ch15-03-drop.md b/src/ch15-03-drop.md index 53352f7ed2..b00030dd06 100644 --- a/src/ch15-03-drop.md +++ b/src/ch15-03-drop.md @@ -1,34 +1,22 @@ -## Running Code on Cleanup with the `Drop` Trait - -The second trait important to the smart pointer pattern is `Drop`, which lets -you customize what happens when a value is about to go out of scope. You can -provide an implementation for the `Drop` trait on any type, and that code can -be used to release resources like files or network connections. - -We’re introducing `Drop` in the context of smart pointers because the -functionality of the `Drop` trait is almost always used when implementing a -smart pointer. For example, when a `Box` is dropped it will deallocate the -space on the heap that the box points to. - -In some languages, for some types, the programmer must call code to free memory -or resources every time they finish using an instance of those types. Examples -include file handles, sockets, and locks. If they forget, the system might -become overloaded and crash. In Rust, you can specify that a particular bit of -code be run whenever a value goes out of scope, and the compiler will insert -this code automatically. As a result, you don’t need to be careful about -placing cleanup code everywhere in a program that an instance of a particular -type is finished with—you still won’t leak resources! - -You specify the code to run when a value goes out of scope by implementing the -`Drop` trait. The `Drop` trait requires you to implement one method named -`drop` that takes a mutable reference to `self`. To see when Rust calls `drop`, -let’s implement `drop` with `println!` statements for now. - -Listing 15-14 shows a `CustomSmartPointer` struct whose only custom -functionality is that it will print `Dropping CustomSmartPointer!` when the -instance goes out of scope, to show when Rust runs the `drop` method. - -+## اجرای کد هنگام پاکسازی با ویژگی `Drop` + +ویژگی دوم که برای الگوی اشاره‌گر (Pointer) هوشمند مهم است، `Drop` است که به شما امکان می‌دهد سفارشی کنید که وقتی یک مقدار +قرار است از دامنه خارج شود، چه اتفاقی بیفتد. می‌توانید یک پیاده‌سازی برای ویژگی `Drop` روی هر نوعی ارائه دهید و +این کد می‌تواند برای آزادسازی منابعی مانند فایل‌ها یا اتصالات شبکه استفاده شود. + +ما ویژگی `Drop` را در زمینه اشاره‌گر (Pointer)های هوشمند معرفی می‌کنیم زیرا عملکرد ویژگی `Drop` تقریباً همیشه هنگام +پیاده‌سازی یک اشاره‌گر (Pointer) هوشمند استفاده می‌شود. برای مثال، وقتی یک `Box` حذف می‌شود، فضای موجود روی پشته‌ای +که باکس به آن اشاره می‌کند، آزاد خواهد شد. + +در برخی زبان‌ها، برای برخی نوع‌ها، برنامه‌نویس باید هر بار که استفاده از یک نمونه از آن نوع‌ها تمام می‌شود، کدی را برای آزادسازی حافظه یا منابع اجرا کند. نمونه‌هایی از این نوع شامل فایل هندل‌ها، سوکت‌ها و لاک‌ها هستند. اگر برنامه‌نویس این کار را فراموش کند، ممکن است سیستم دچار بار اضافی شده و از کار بیفتد. در Rust، می‌توانید مشخص کنید که قطعه کد خاصی هنگام خارج شدن یک مقدار از حوزه‌ی دید (scope) اجرا شود، و کامپایلر این کد را به‌صورت خودکار درج خواهد کرد. در نتیجه، نیازی نیست نگران این باشید که در تمام بخش‌های برنامه، کد پاک‌سازی (cleanup) را درج کنید؛ حتی با این وجود نیز دچار نشت منابع نخواهید شد! + +شما کدی که باید هنگام خروج مقدار از دامنه اجرا شود را با پیاده‌سازی ویژگی `Drop` مشخص می‌کنید. ویژگی `Drop` +نیازمند این است که یک متد به نام `drop` را پیاده‌سازی کنید که یک مرجع متغیر به `self` می‌گیرد. برای دیدن زمانی +که Rust فراخوانی `drop` را انجام می‌دهد، بیایید `drop` را با جملات `println!` برای اکنون پیاده‌سازی کنیم. + +لیستینگ 15-14 یک `struct` به‌نام `CustomSmartPointer` را نشان می‌دهد که تنها عملکرد سفارشی آن این است که هنگام خارج شدن نمونه از حوزه‌ی دید (scope)، پیام `Dropping CustomSmartPointer!` را چاپ می‌کند تا نشان دهد چه زمانی Rust متد `drop` را اجرا می‌کند. + + ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-14/src/main.rs}} @@ -36,51 +24,35 @@ instance goes out of scope, to show when Rust runs the `drop` method. -The `Drop` trait is included in the prelude, so we don’t need to bring it into -scope. We implement the `Drop` trait on `CustomSmartPointer` and provide an -implementation for the `drop` method that calls `println!`. The body of the -`drop` method is where you would place any logic that you wanted to run when an -instance of your type goes out of scope. We’re printing some text here to -demonstrate visually when Rust will call `drop`. +`trait` مربوط به `Drop` در prelude زبان Rust گنجانده شده است، بنابراین نیازی نیست آن را به‌طور جداگانه به حوزه‌ی دید (scope) وارد کنیم. ما `trait` `Drop` را برای `CustomSmartPointer` پیاده‌سازی کرده‌ایم و برای متد `drop` یک پیاده‌سازی ارائه داده‌ایم که در آن از `println!` استفاده می‌شود. بدنه‌ی متد `drop` جایی است که می‌توانید هر منطقی را که می‌خواهید هنگام خارج شدن یک نمونه از نوع‌تان از scope اجرا شود، قرار دهید. ما در این‌جا صرفاً با چاپ یک متن، به‌صورت بصری نشان می‌دهیم که Rust چه زمانی متد `drop` را فراخوانی می‌کند. -In `main`, we create two instances of `CustomSmartPointer` and then print -`CustomSmartPointers created`. At the end of `main`, our instances of -`CustomSmartPointer` will go out of scope, and Rust will call the code we put -in the `drop` method, printing our final message. Note that we didn’t need to -call the `drop` method explicitly. +در تابع `main`، دو نمونه از `CustomSmartPointer` ایجاد می‌کنیم و سپس `CustomSmartPointers created` را چاپ +می‌کنیم. در پایان `main`، نمونه‌های ما از `CustomSmartPointer` از دامنه خارج خواهند شد و Rust کدی که در متد +`drop` قرار داده‌ایم را فراخوانی خواهد کرد و پیام نهایی ما را چاپ می‌کند. توجه کنید که نیازی به فراخوانی صریح متد +`drop` نداشتیم. -When we run this program, we’ll see the following output: +وقتی این برنامه را اجرا می‌کنیم، خروجی زیر را مشاهده خواهیم کرد: ```console {{#include ../listings/ch15-smart-pointers/listing-15-14/output.txt}} ``` -Rust automatically called `drop` for us when our instances went out of scope, -calling the code we specified. Variables are dropped in the reverse order of -their creation, so `d` was dropped before `c`. This example’s purpose is to -give you a visual guide to how the `drop` method works; usually you would -specify the cleanup code that your type needs to run rather than a print -message. +Rust به صورت خودکار `drop` را برای ما فراخوانی کرد وقتی که نمونه‌های ما از دامنه خارج شدند و کدی که مشخص کرده بودیم +را اجرا کرد. متغیرها به ترتیب معکوس ایجادشان حذف می‌شوند، بنابراین `d` قبل از `c` حذف شد. هدف این مثال این است +که یک راهنمای بصری برای نحوه کارکرد متد `drop` به شما بدهد؛ معمولاً شما کد پاکسازی که نوع شما نیاز دارد را مشخص +می‌کنید نه یک پیام چاپ. -Unfortunately, it’s not straightforward to disable the automatic `drop` -functionality. Disabling `drop` isn’t usually necessary; the whole point of the -`Drop` trait is that it’s taken care of automatically. Occasionally, however, -you might want to clean up a value early. One example is when using smart -pointers that manage locks: you might want to force the `drop` method that -releases the lock so that other code in the same scope can acquire the lock. -Rust doesn’t let you call the `Drop` trait’s `drop` method manually; instead, -you have to call the `std::mem::drop` function provided by the standard library -if you want to force a value to be dropped before the end of its scope. +متأسفانه غیرفعال‌کردن عملکرد خودکار `drop` کار ساده‌ای نیست. در اغلب موارد نیز نیازی به غیرفعال‌کردن آن نیست؛ تمام هدف `trait` مربوط به `Drop` این است که فرآیند پاک‌سازی به‌طور خودکار مدیریت شود. با این حال، گاهی ممکن است بخواهید یک مقدار را زودتر از زمان معمول پاک‌سازی کنید. یکی از نمونه‌ها زمانی است که از smart pointerهایی استفاده می‌کنید که قفل‌ها (locks) را مدیریت می‌کنند: ممکن است بخواهید متد `drop` که قفل را آزاد می‌کند را به‌صورت دستی فراخوانی کنید تا سایر کدهای همان scope بتوانند قفل را در اختیار بگیرند. +Rust اجازه نمی‌دهد متد `drop` مربوط به `trait` `Drop` را به‌صورت دستی فراخوانی کنید؛ در عوض، اگر می‌خواهید یک مقدار را قبل از پایان حوزه‌ی دیدش پاک‌سازی کنید، باید از تابع `std::mem::drop` که در کتابخانه‌ی استاندارد فراهم شده استفاده کنید. + +اگر تلاش کنیم تا متد `drop` مربوط به `trait` `Drop` را به‌صورت دستی فراخوانی کنیم و تابع `main` موجود در لیستینگ 15-14 را تغییر دهیم، همان‌طور که در لیستینگ 15-15 نشان داده شده است، با خطای کامپایل مواجه خواهیم شد. -If we try to call the `Drop` trait’s `drop` method manually by modifying the -`main` function from Listing 15-14, as shown in Listing 15-15, we’ll get a -compiler error. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-15/src/main.rs:here}} @@ -88,33 +60,27 @@ compiler error. -When we try to compile this code, we’ll get this error: +وقتی سعی کنیم این کد را کامپایل کنیم، با این خطا مواجه می‌شویم: ```console {{#include ../listings/ch15-smart-pointers/listing-15-15/output.txt}} ``` -This error message states that we’re not allowed to explicitly call `drop`. The -error message uses the term _destructor_, which is the general programming term -for a function that cleans up an instance. A _destructor_ is analogous to a -_constructor_, which creates an instance. The `drop` function in Rust is one -particular destructor. +این پیام خطا نشان می‌دهد که ما اجازه نداریم به‌طور صریح `drop` را فراخوانی کنیم. پیام خطا از اصطلاح _تخریب‌گر_ +(Destructor) استفاده می‌کند که اصطلاحی کلی برای تابعی است که یک نمونه را تمیز می‌کند. یک تخریب‌گر مشابه یک +_سازنده_ (Constructor) است که یک نمونه را ایجاد می‌کند. تابع `drop` در Rust یک تخریب‌گر خاص است. -Rust doesn’t let us call `drop` explicitly because Rust would still -automatically call `drop` on the value at the end of `main`. This would cause a -_double free_ error because Rust would be trying to clean up the same value -twice. +Rust به ما اجازه نمی‌دهد `drop` را به صورت صریح فراخوانی کنیم زیرا Rust به‌طور خودکار `drop` را در انتهای تابع +`main` فراخوانی می‌کند. این موضوع می‌تواند باعث خطای _آزادسازی دوگانه_ شود زیرا Rust سعی می‌کند همان مقدار را دو بار +تمیز کند. -We can’t disable the automatic insertion of `drop` when a value goes out of -scope, and we can’t call the `drop` method explicitly. So, if we need to force -a value to be cleaned up early, we use the `std::mem::drop` function. +ما نمی‌توانیم قرار دادن خودکار `drop` را هنگام خروج یک مقدار از حوزه غیرفعال کنیم و همچنین نمی‌توانیم متد `drop` +را به صورت صریح فراخوانی کنیم. بنابراین، اگر نیاز به حذف زودهنگام یک مقدار داشته باشیم، باید از تابع `std::mem::drop` +استفاده کنیم. -The `std::mem::drop` function is different from the `drop` method in the `Drop` -trait. We call it by passing as an argument the value we want to force-drop. -The function is in the prelude, so we can modify `main` in Listing 15-15 to -call the `drop` function, as shown in Listing 15-16. +تابع `std::mem::drop` با متد `drop` در `trait` `Drop` متفاوت است. این تابع را با ارسال مقداری که می‌خواهیم به‌صورت اجباری drop شود، فراخوانی می‌کنیم. این تابع در prelude قرار دارد، بنابراین می‌توانیم تابع `main` در لیستینگ 15-15 را تغییر دهیم تا به‌جای فراخوانی مستقیم متد `drop`، تابع `drop` را فراخوانی کند؛ همان‌طور که در لیستینگ 15-16 نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-16/src/main.rs:here}} @@ -122,27 +88,23 @@ call the `drop` function, as shown in Listing 15-16. -Running this code will print the following: +اجرای این کد خروجی زیر را چاپ خواهد کرد: ```console {{#include ../listings/ch15-smart-pointers/listing-15-16/output.txt}} ``` -The text ``Dropping CustomSmartPointer with data `some data`!`` is printed -between the `CustomSmartPointer created.` and `CustomSmartPointer dropped -before the end of main.` text, showing that the `drop` method code is called to -drop `c` at that point. +متن `Dropping CustomSmartPointer with data 'some data'!` بین متون `CustomSmartPointer created.` و +`CustomSmartPointer dropped before the end of main.` چاپ می‌شود و نشان می‌دهد که کد متد `drop` برای حذف +`c` در آن نقطه فراخوانی شده است. -You can use code specified in a `Drop` trait implementation in many ways to -make cleanup convenient and safe: for instance, you could use it to create your -own memory allocator! With the `Drop` trait and Rust’s ownership system, you -don’t have to remember to clean up because Rust does it automatically. +شما می‌توانید از کدی که در پیاده‌سازی ویژگی `Drop` مشخص کرده‌اید، به روش‌های مختلفی برای ساده و امن کردن عملیات +پاکسازی استفاده کنید: برای مثال، می‌توانید از آن برای ایجاد تخصیص‌دهنده حافظه خودتان استفاده کنید! با ویژگی `Drop` و +سیستم مالکیت Rust، نیازی به یادآوری پاکسازی ندارید، زیرا Rust این کار را به‌طور خودکار انجام می‌دهد. -You also don’t have to worry about problems resulting from accidentally -cleaning up values still in use: the ownership system that makes sure -references are always valid also ensures that `drop` gets called only once when -the value is no longer being used. +همچنین نیازی به نگرانی در مورد مشکلات ناشی از پاکسازی اشتباهی مقادیری که هنوز در حال استفاده هستند، ندارید: سیستم مالکیت +که اطمینان می‌دهد ارجاعات همیشه معتبر هستند، همچنین تضمین می‌کند که `drop` فقط یک بار و زمانی که مقدار دیگر استفاده نمی‌شود، +فراخوانی شود. -Now that we’ve examined `Box` and some of the characteristics of smart -pointers, let’s look at a few other smart pointers defined in the standard -library. +اکنون که `Box` و برخی از ویژگی‌های اشاره‌گر (Pointer)های هوشمند را بررسی کردیم، بیایید به چند اشاره‌گر (Pointer) هوشمند دیگر که در کتابخانه +استاندارد تعریف شده‌اند، نگاهی بیندازیم. \ No newline at end of file diff --git a/src/ch15-04-rc.md b/src/ch15-04-rc.md index bd743c000b..9c8f514601 100644 --- a/src/ch15-04-rc.md +++ b/src/ch15-04-rc.md @@ -1,54 +1,39 @@ -## `Rc`, the Reference Counted Smart Pointer - -In the majority of cases, ownership is clear: you know exactly which variable -owns a given value. However, there are cases when a single value might have -multiple owners. For example, in graph data structures, multiple edges might -point to the same node, and that node is conceptually owned by all of the edges -that point to it. A node shouldn’t be cleaned up unless it doesn’t have any -edges pointing to it and so has no owners. - -You have to enable multiple ownership explicitly by using the Rust type -`Rc`, which is an abbreviation for _reference counting_. The `Rc` type -keeps track of the number of references to a value to determine whether or not -the value is still in use. If there are zero references to a value, the value -can be cleaned up without any references becoming invalid. - -Imagine `Rc` as a TV in a family room. When one person enters to watch TV, -they turn it on. Others can come into the room and watch the TV. When the last -person leaves the room, they turn off the TV because it’s no longer being used. -If someone turns off the TV while others are still watching it, there would be -an uproar from the remaining TV watchers! - -We use the `Rc` type when we want to allocate some data on the heap for -multiple parts of our program to read and we can’t determine at compile time -which part will finish using the data last. If we knew which part would finish -last, we could just make that part the data’s owner, and the normal ownership -rules enforced at compile time would take effect. - -Note that `Rc` is only for use in single-threaded scenarios. When we discuss -concurrency in Chapter 16, we’ll cover how to do reference counting in -multithreaded programs. - -### Using `Rc` to Share Data - -Let’s return to our cons list example in Listing 15-5. Recall that we defined -it using `Box`. This time, we’ll create two lists that both share ownership -of a third list. Conceptually, this looks similar to Figure 15-3. +## `Rc`، اشاره‌گر (Pointer) هوشمند با شمارش مرجع -A linked list with the label 'a' pointing to three elements: the first element contains the integer 5 and points to the second element. The second element contains the integer 10 and points to the third element. The third element contains the value 'Nil' that signifies the end of the list; it does not point anywhere. A linked list with the label 'b' points to an element that contains the integer 3 and points to the first element of list 'a'. A linked list with the label 'c' points to an element that contains the integer 4 and also points to the first element of list 'a', so that the tail of lists 'b' and 'c' are both list 'a' +در بیشتر موارد، مالکیت واضح است: شما دقیقاً می‌دانید که کدام متغیر مالک یک مقدار مشخص است. با این حال، در مواردی +ممکن است یک مقدار چندین مالک داشته باشد. برای مثال، در ساختارهای داده گراف، چندین یال ممکن است به یک گره +اشاره کنند و آن گره از نظر مفهومی متعلق به تمام یال‌هایی است که به آن اشاره دارند. یک گره نباید پاکسازی شود مگر +اینکه هیچ یالی به آن اشاره نکند و در نتیجه مالکیتی نداشته باشد. + +برای فعال‌سازی مالکیت چندگانه باید به صورت صریح از نوع `Rc` در Rust استفاده کنید که مخفف +_reference counting_ یا شمارش مرجع است. نوع `Rc` تعداد مراجعات به یک مقدار را دنبال می‌کند تا مشخص کند که آیا +آن مقدار هنوز در حال استفاده است یا خیر. اگر هیچ مرجعی به یک مقدار وجود نداشته باشد، مقدار می‌تواند بدون اینکه +هیچ مرجعی نامعتبر شود، پاکسازی شود. + +تصور کنید `Rc` مانند یک تلویزیون در اتاق نشیمن است. وقتی یک نفر وارد اتاق می‌شود تا تلویزیون تماشا کند، آن را +روشن می‌کند. افراد دیگری هم می‌توانند وارد اتاق شوند و تلویزیون تماشا کنند. وقتی آخرین نفر اتاق را ترک می‌کند، +تلویزیون را خاموش می‌کند زیرا دیگر استفاده نمی‌شود. اگر کسی تلویزیون را در حالی که دیگران هنوز در حال تماشای آن +هستند خاموش کند، اعتراض تماشاگران باقی‌مانده بلند خواهد شد! -Figure 15-3: Two lists, `b` and `c`, sharing ownership of -a third list, `a` +ما از نوع `Rc` استفاده می‌کنیم وقتی می‌خواهیم مقداری را در هیپ تخصیص دهیم که توسط چندین بخش از برنامه ما +خوانده شود و نمی‌توانیم در زمان کامپایل تعیین کنیم که کدام بخش استفاده از داده را زودتر به پایان می‌رساند. اگر +می‌دانستیم کدام بخش زودتر تمام می‌شود، می‌توانستیم آن بخش را مالک داده کنیم و قوانین معمول مالکیت که در زمان +کامپایل اعمال می‌شود، اعمال می‌شد. -We’ll create list `a` that contains `5` and then `10`. Then we’ll make two more -lists: `b` that starts with `3` and `c` that starts with `4`. Both `b` and `c` -lists will then continue on to the first `a` list containing `5` and `10`. In -other words, both lists will share the first list containing `5` and `10`. +توجه داشته باشید که `Rc` فقط برای استفاده در سناریوهای تک‌ریسمانی است. هنگامی که در فصل 16 در مورد +هم‌زمانی بحث می‌کنیم، نحوه انجام شمارش مرجع در برنامه‌های چندریسمانی را پوشش خواهیم داد. -Trying to implement this scenario using our definition of `List` with `Box` -won’t work, as shown in Listing 15-17. +### استفاده از `Rc` برای اشتراک‌گذاری داده + +بیایید به مثال لیست cons که در لیست 15-5 دیدیم برگردیم. به خاطر دارید که این لیست را با استفاده از `Box` تعریف کرده بودیم. این بار، قصد داریم دو لیست ایجاد کنیم که هر دو مالکیت مشترکی از یک لیست سوم داشته باشند. از نظر مفهومی، این ساختار شبیه به تصویر 15-3 است. + +A linked list with the label 'a' pointing to three elements: the first element contains the integer 5 and points to the second element. The second element contains the integer 10 and points to the third element. The third element contains the value 'Nil' that signifies the end of the list; it does not point anywhere. A linked list with the label 'b' points to an element that contains the integer 3 and points to the first element of list 'a'. A linked list with the label 'c' points to an element that contains the integer 4 and also points to the first element of list 'a', so that the tail of lists 'b' and 'c' are both list 'a' -+شکل 15-3: دو لیست، `b` و `c`، که مالکیت یک لیست سوم، `a` را به اشتراک می‌گذارند + +ما ابتدا لیستی به نام `a` ایجاد می‌کنیم که شامل مقادیر `5` و سپس `10` است. سپس دو لیست دیگر می‌سازیم: لیست `b` که با مقدار `3` شروع می‌شود و لیست `c` که با مقدار `4` شروع می‌شود. هر دو لیست `b` و `c` در ادامه به لیست `a` که شامل `5` و `10` است اشاره خواهند کرد. به عبارت دیگر، هر دو لیست `b` و `c` لیست `a` را به صورت مشترک استفاده می‌کنند. + +تلاش برای پیاده‌سازی این سناریو با استفاده از تعریف فعلی `List` که از `Box` استفاده می‌کند، امکان‌پذیر نیست؛ همان‌طور که در لیست 15-17 نشان داده شده است. ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-17/src/main.rs}} @@ -56,34 +41,30 @@ won’t work, as shown in Listing 15-17. -When we compile this code, we get this error: +هنگامی که این کد را کامپایل می‌کنیم، با این خطا مواجه می‌شویم: ```console {{#include ../listings/ch15-smart-pointers/listing-15-17/output.txt}} ``` -The `Cons` variants own the data they hold, so when we create the `b` list, `a` -is moved into `b` and `b` owns `a`. Then, when we try to use `a` again when -creating `c`, we’re not allowed to because `a` has been moved. - -We could change the definition of `Cons` to hold references instead, but then -we would have to specify lifetime parameters. By specifying lifetime -parameters, we would be specifying that every element in the list will live at -least as long as the entire list. This is the case for the elements and lists -in Listing 15-17, but not in every scenario. - -Instead, we’ll change our definition of `List` to use `Rc` in place of -`Box`, as shown in Listing 15-18. Each `Cons` variant will now hold a value -and an `Rc` pointing to a `List`. When we create `b`, instead of taking -ownership of `a`, we’ll clone the `Rc` that `a` is holding, thereby -increasing the number of references from one to two and letting `a` and `b` -share ownership of the data in that `Rc`. We’ll also clone `a` when -creating `c`, increasing the number of references from two to three. Every time -we call `Rc::clone`, the reference count to the data within the `Rc` will -increase, and the data won’t be cleaned up unless there are zero references to -it. - -+متغیرهای `Cons` مالک داده‌هایی هستند که در خود نگه می‌دارند. بنابراین، هنگامی که لیست `b` را ایجاد می‌کنیم، +`a` به `b` منتقل می‌شود و `b` مالک `a` می‌شود. سپس، هنگامی که سعی می‌کنیم دوباره از `a` برای ایجاد `c` استفاده +کنیم، این کار مجاز نیست زیرا `a` قبلاً منتقل شده است. + +ما می‌توانیم تعریف `Cons` را به گونه‌ای تغییر دهیم که به جای نگهداری داده‌ها، ارجاع به آنها را نگه دارد. اما در +این صورت باید پارامترهای طول عمر (lifetime parameters) را مشخص کنیم. با مشخص کردن پارامترهای طول عمر، مشخص +می‌کنیم که هر عنصر در لیست حداقل به اندازه کل لیست زنده خواهد بود. این موضوع در مورد عناصر و لیست‌های موجود در +لیست 15-17 صدق می‌کند، اما در همه سناریوها چنین نیست. + +در عوض، تعریف `List` خود را تغییر می‌دهیم تا به جای `Box` از `Rc` استفاده کند، همان‌طور که در لیست 15-18 +نشان داده شده است. هر متغیر `Cons` اکنون یک مقدار و یک `Rc` اشاره‌کننده به یک `List` را نگه می‌دارد. وقتی `b` +را ایجاد می‌کنیم، به جای تصاحب مالکیت `a`، `Rc` که `a` نگه می‌دارد را کلون می‌کنیم، بنابراین تعداد +ارجاعات از یک به دو افزایش می‌یابد و به `a` و `b` اجازه می‌دهیم مالکیت داده‌های موجود در آن `Rc` را به +اشتراک بگذارند. همچنین هنگام ایجاد `c`، `a` را کلون می‌کنیم و تعداد ارجاعات از دو به سه افزایش می‌یابد. هر بار +که `Rc::clone` را فراخوانی می‌کنیم، تعداد ارجاعات به داده‌های موجود در `Rc` افزایش می‌یابد و داده‌ها تا +زمانی که هیچ ارجاعی به آنها باقی نماند پاک نمی‌شوند. + + ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-18/src/main.rs}} @@ -91,32 +72,25 @@ it. -We need to add a `use` statement to bring `Rc` into scope because it’s not -in the prelude. In `main`, we create the list holding `5` and `10` and store it -in a new `Rc` in `a`. Then, when we create `b` and `c`, we call the -`Rc::clone` function and pass a reference to the `Rc` in `a` as an -argument. +ما نیاز داریم یک دستور `use` اضافه کنیم تا `Rc` را وارد دامنه کنیم، زیرا این نوع در prelude قرار ندارد. در تابع `main`، یک لیست که مقادیر `5` و `10` را نگه می‌دارد ایجاد می‌کنیم و آن را در یک `Rc` جدید به نام `a` ذخیره می‌کنیم. سپس، زمانی که `b` و `c` را می‌سازیم، تابع `Rc::clone` را فراخوانی می‌کنیم و یک رفرنس به `Rc` موجود در `a` را به عنوان آرگومان به آن پاس می‌دهیم. -We could have called `a.clone()` rather than `Rc::clone(&a)`, but Rust’s -convention is to use `Rc::clone` in this case. The implementation of -`Rc::clone` doesn’t make a deep copy of all the data like most types’ -implementations of `clone` do. The call to `Rc::clone` only increments the -reference count, which doesn’t take much time. Deep copies of data can take a -lot of time. By using `Rc::clone` for reference counting, we can visually -distinguish between the deep-copy kinds of clones and the kinds of clones that -increase the reference count. When looking for performance problems in the -code, we only need to consider the deep-copy clones and can disregard calls to -`Rc::clone`. +می‌توانستیم `a.clone()` را به جای `Rc::clone(&a)` فراخوانی کنیم، اما طبق قرارداد Rust در این موارد از +`Rc::clone` استفاده می‌شود. پیاده‌سازی `Rc::clone` یک کپی عمیق از تمام داده‌ها ایجاد نمی‌کند، همان‌طور که +پیاده‌سازی اکثر انواع دیگر `clone` این کار را انجام می‌دهد. فراخوانی `Rc::clone` فقط تعداد ارجاعات را افزایش +می‌دهد، که زمان زیادی نمی‌برد. کپی عمیق داده‌ها ممکن است زمان زیادی ببرد. با استفاده از `Rc::clone` برای شمارش +مرجع، می‌توانیم بین کپی‌های عمیق و کپی‌هایی که تعداد ارجاعات را افزایش می‌دهند تمایز بصری قائل شویم. هنگام +جستجوی مشکلات عملکرد در کد، فقط لازم است به کپی‌های عمیق توجه کنیم و می‌توانیم فراخوانی‌های `Rc::clone` را +نادیده بگیریم. -### Cloning an `Rc` Increases the Reference Count +### کلون کردن یک `Rc` تعداد ارجاعات را افزایش می‌دهد -Let’s change our working example in Listing 15-18 so we can see the reference -counts changing as we create and drop references to the `Rc` in `a`. +اجازه دهید مثال کاری خود را در لیست 15-18 تغییر دهیم تا بتوانیم تغییرات تعداد ارجاعات را هنگام ایجاد و حذف +ارجاعات به `Rc` در `a` مشاهده کنیم. -In Listing 15-19, we’ll change `main` so it has an inner scope around list `c`; -then we can see how the reference count changes when `c` goes out of scope. +در لیست 15-19، `main` را تغییر خواهیم داد تا یک محدوده داخلی (inner scope) در اطراف لیست `c` داشته باشد؛ +سپس می‌توانیم ببینیم که چگونه تعداد ارجاعات زمانی که `c` از محدوده خارج می‌شود تغییر می‌کند. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-19/src/main.rs:here}} @@ -124,38 +98,33 @@ then we can see how the reference count changes when `c` goes out of scope. -At each point in the program where the reference count changes, we print the -reference count, which we get by calling the `Rc::strong_count` function. This -function is named `strong_count` rather than `count` because the `Rc` type -also has a `weak_count`; we’ll see what `weak_count` is used for in [“Preventing -Reference Cycles Using `Weak`”][preventing-ref-cycles]. +در هر نقطه از برنامه که تعداد ارجاعات تغییر می‌کند، تعداد ارجاعات را چاپ می‌کنیم که از طریق فراخوانی تابع +`Rc::strong_count` دریافت می‌شود. این تابع به جای `count`، `strong_count` نام‌گذاری شده است زیرا نوع `Rc` +همچنین دارای `weak_count` است؛ در بخش [“جلوگیری از چرخه‌های مرجع: تبدیل یک `Rc` به یک +`Weak`”][preventing-ref-cycles] با کاربرد `weak_count` آشنا خواهیم شد. -This code prints the following: +این کد خروجی زیر را تولید می‌کند: ```console {{#include ../listings/ch15-smart-pointers/listing-15-19/output.txt}} ``` -We can see that the `Rc` in `a` has an initial reference count of 1; then -each time we call `clone`, the count goes up by 1. When `c` goes out of scope, -the count goes down by 1. We don’t have to call a function to decrease the -reference count like we have to call `Rc::clone` to increase the reference -count: the implementation of the `Drop` trait decreases the reference count -automatically when an `Rc` value goes out of scope. - -What we can’t see in this example is that when `b` and then `a` go out of scope -at the end of `main`, the count is then 0, and the `Rc` is cleaned up -completely. Using `Rc` allows a single value to have multiple owners, and -the count ensures that the value remains valid as long as any of the owners -still exist. - -Via immutable references, `Rc` allows you to share data between multiple -parts of your program for reading only. If `Rc` allowed you to have multiple -mutable references too, you might violate one of the borrowing rules discussed -in Chapter 4: multiple mutable borrows to the same place can cause data races -and inconsistencies. But being able to mutate data is very useful! In the next -section, we’ll discuss the interior mutability pattern and the `RefCell` -type that you can use in conjunction with an `Rc` to work with this -immutability restriction. - -[preventing-ref-cycles]: ch15-06-reference-cycles.html#preventing-reference-cycles-turning-an-rct-into-a-weakt +می‌بینیم که `Rc` در `a` تعداد ارجاع اولیه برابر با 1 دارد؛ سپس هر بار که `clone` را فراخوانی می‌کنیم، +تعداد ارجاعات 1 واحد افزایش می‌یابد. هنگامی که `c` از محدوده خارج می‌شود، تعداد ارجاعات 1 واحد کاهش می‌یابد. +لازم نیست تابعی برای کاهش تعداد ارجاعات فراخوانی کنیم، همان‌طور که باید `Rc::clone` را برای افزایش تعداد +ارجاعات فراخوانی کنیم: پیاده‌سازی ویژگی `Drop` تعداد ارجاعات را به طور خودکار کاهش می‌دهد وقتی که یک مقدار +`Rc` از محدوده خارج می‌شود. + +آنچه در این مثال نمی‌توانیم ببینیم این است که وقتی `b` و سپس `a` در انتهای `main` از محدوده خارج می‌شوند، تعداد +ارجاعات به 0 می‌رسد و `Rc` به طور کامل پاک‌سازی می‌شود. استفاده از `Rc` به یک مقدار اجازه می‌دهد که +چندین مالک داشته باشد، و تعداد ارجاعات تضمین می‌کند که مقدار تا زمانی که هر یک از مالکان هنوز وجود دارند، معتبر +باقی می‌ماند. + +از طریق ارجاعات غیرقابل تغییر، `Rc` به شما اجازه می‌دهد داده‌ها را بین بخش‌های مختلف برنامه خود برای +خواندن به اشتراک بگذارید. اگر `Rc` به شما اجازه می‌داد که چندین ارجاع قابل تغییر نیز داشته باشید، ممکن بود +یکی از قوانین قرض‌گیری که در فصل 4 بحث شد را نقض کنید: چندین قرض قابل تغییر به یک مکان می‌تواند باعث ایجاد +تناقضات و مسابقه داده‌ها شود. اما توانایی تغییر داده‌ها بسیار مفید است! در بخش بعدی، به الگوی تغییر‌پذیری داخلی +(interior mutability) و نوع `RefCell` که می‌توانید همراه با `Rc` برای کار با این محدودیت عدم تغییر‌پذیری +استفاده کنید، خواهیم پرداخت. + +[preventing-ref-cycles]: ch15-06-reference-cycles.html#preventing-reference-cycles-turning-an-rct-into-a-weakt \ No newline at end of file diff --git a/src/ch15-05-interior-mutability.md b/src/ch15-05-interior-mutability.md index 2e02265857..ee9b56627b 100644 --- a/src/ch15-05-interior-mutability.md +++ b/src/ch15-05-interior-mutability.md @@ -1,135 +1,110 @@ -## `RefCell` and the Interior Mutability Pattern - -_Interior mutability_ is a design pattern in Rust that allows you to mutate -data even when there are immutable references to that data; normally, this -action is disallowed by the borrowing rules. To mutate data, the pattern uses -`unsafe` code inside a data structure to bend Rust’s usual rules that govern -mutation and borrowing. Unsafe code indicates to the compiler that we’re -checking the rules manually instead of relying on the compiler to check them -for us; we will discuss unsafe code more in Chapter 20. - -We can use types that use the interior mutability pattern only when we can -ensure that the borrowing rules will be followed at runtime, even though the -compiler can’t guarantee that. The `unsafe` code involved is then wrapped in a -safe API, and the outer type is still immutable. - -Let’s explore this concept by looking at the `RefCell` type that follows the -interior mutability pattern. - -### Enforcing Borrowing Rules at Runtime with `RefCell` - -Unlike `Rc`, the `RefCell` type represents single ownership over the data -it holds. So what makes `RefCell` different from a type like `Box`? -Recall the borrowing rules you learned in Chapter 4: - -- At any given time, you can have _either_ one mutable reference or any number - of immutable references (but not both). -- References must always be valid. - -With references and `Box`, the borrowing rules’ invariants are enforced at -compile time. With `RefCell`, these invariants are enforced _at runtime_. -With references, if you break these rules, you’ll get a compiler error. With -`RefCell`, if you break these rules, your program will panic and exit. - -The advantages of checking the borrowing rules at compile time are that errors -will be caught sooner in the development process, and there is no impact on -runtime performance because all the analysis is completed beforehand. For those -reasons, checking the borrowing rules at compile time is the best choice in the -majority of cases, which is why this is Rust’s default. - -The advantage of checking the borrowing rules at runtime instead is that -certain memory-safe scenarios are then allowed, where they would’ve been -disallowed by the compile-time checks. Static analysis, like the Rust compiler, -is inherently conservative. Some properties of code are impossible to detect by -analyzing the code: the most famous example is the Halting Problem, which is -beyond the scope of this book but is an interesting topic to research. - -Because some analysis is impossible, if the Rust compiler can’t be sure the -code complies with the ownership rules, it might reject a correct program; in -this way, it’s conservative. If Rust accepted an incorrect program, users -wouldn’t be able to trust the guarantees Rust makes. However, if Rust rejects a -correct program, the programmer will be inconvenienced, but nothing -catastrophic can occur. The `RefCell` type is useful when you’re sure your -code follows the borrowing rules but the compiler is unable to understand and -guarantee that. - -Similar to `Rc`, `RefCell` is only for use in single-threaded scenarios -and will give you a compile-time error if you try using it in a multithreaded -context. We’ll talk about how to get the functionality of `RefCell` in a -multithreaded program in Chapter 16. - -Here is a recap of the reasons to choose `Box`, `Rc`, or `RefCell`: - -- `Rc` enables multiple owners of the same data; `Box` and `RefCell` - have single owners. -- `Box` allows immutable or mutable borrows checked at compile time; `Rc` - allows only immutable borrows checked at compile time; `RefCell` allows - immutable or mutable borrows checked at runtime. -- Because `RefCell` allows mutable borrows checked at runtime, you can - mutate the value inside the `RefCell` even when the `RefCell` is - immutable. - -Mutating the value inside an immutable value is the _interior mutability_ -pattern. Let’s look at a situation in which interior mutability is useful and -examine how it’s possible. - -### Interior Mutability: A Mutable Borrow to an Immutable Value - -A consequence of the borrowing rules is that when you have an immutable value, -you can’t borrow it mutably. For example, this code won’t compile: +## `RefCell` و الگوی تغییرپذیری داخلی + +_تغییرپذیری داخلی_ یک الگوی طراحی در راست است که به شما اجازه می‌دهد داده‌ها را حتی زمانی که +ارجاع‌های غیرقابل‌تغییر به آن داده‌ها وجود دارد، تغییر دهید؛ معمولاً این عمل توسط قوانین وام‌دهی +(‌borrowing rules) ممنوع است. برای تغییر داده‌ها، این الگو از کد `unsafe` درون یک ساختار داده +برای تغییر قوانین معمول راست که کنترل تغییرپذیری و وام‌دهی را بر عهده دارند، استفاده می‌کند. کد +`unsafe` به کامپایلر نشان می‌دهد که ما قوانین را به صورت دستی بررسی می‌کنیم و دیگر به کامپایلر +اعتماد نداریم که این کار را برای ما انجام دهد؛ ما در فصل 20 بیشتر درباره کد `unsafe` صحبت خواهیم کرد. + +ما می‌توانیم از انواعی که از الگوی تغییرپذیری داخلی استفاده می‌کنند تنها در صورتی استفاده کنیم که +بتوانیم اطمینان حاصل کنیم که قوانین وام‌دهی در زمان اجرا رعایت خواهند شد، حتی اگر کامپایلر نتواند +این را تضمین کند. کد `unsafe` مرتبط سپس در یک API ایمن پیچیده شده و نوع بیرونی همچنان +غیرقابل‌تغییر باقی می‌ماند. + +بیایید این مفهوم را با بررسی نوع `RefCell` که از الگوی تغییرپذیری داخلی پیروی می‌کند، +بیشتر بررسی کنیم. + +### اجرای قوانین وام‌دهی در زمان اجرا با `RefCell` + +بر خلاف `Rc`، نوع `RefCell` نشان‌دهنده‌ی مالکیت یکتا (single ownership) بر داده‌ای است که در خود نگه می‌دارد. پس چه چیزی `RefCell` را از نوعی مانند `Box` متمایز می‌کند؟ قوانین قرض‌گیری (borrowing) را که در فصل ۴ یاد گرفتید به‌خاطر بیاورید: + +- در هر لحظه فقط می‌توانید *یا* یک رفرنس قابل‌تغییر داشته باشید *یا* هر تعداد رفرنس تغییرناپذیر (اما نه هر دو همزمان). +- رفرنس‌ها باید همواره معتبر باشند. + +با استفاده از ارجاع‌ها و `Box`، ثابت‌های قوانین وام‌دهی در زمان کامپایل اعمال می‌شوند. +اما با `RefCell`، این ثابت‌ها در _زمان اجرا_ اعمال می‌شوند. با ارجاع‌ها، اگر این قوانین +را بشکنید، یک خطای کامپایل دریافت خواهید کرد. اما با `RefCell`، اگر این قوانین را بشکنید، +برنامه شما دچار وحشت (panic) می‌شود و متوقف می‌شود. + +مزیت بررسی قوانین وام‌دهی در زمان کامپایل این است که خطاها زودتر در فرایند توسعه شناسایی +می‌شوند، و هیچ تأثیری بر عملکرد زمان اجرا وجود ندارد زیرا تمام تحلیل‌ها پیشاپیش انجام شده‌اند. +به همین دلایل، بررسی قوانین وام‌دهی در زمان کامپایل بهترین انتخاب در اکثر موارد است، که به +همین دلیل این روش پیش‌فرض راست است. + +مزیت بررسی قوانین وام‌دهی در زمان اجرا این است که سناریوهایی که ایمن از نظر حافظه هستند +اجازه می‌یابند، در حالی که ممکن است توسط بررسی‌های زمان کامپایل مجاز نباشند. تحلیل +ایستا (static analysis)، مانند کامپایلر راست، به‌طور ذاتی محافظه‌کارانه است. برخی +خصوصیات کد غیرممکن است که با تحلیل کد شناسایی شوند: معروف‌ترین مثال، مشکل توقف +(Halting Problem) است که فراتر از محدوده این کتاب است اما موضوع جالبی برای تحقیق +می‌باشد. + +از آن‌جا که برخی تحلیل‌ها غیرممکن هستند، اگر کامپایلر Rust نتواند مطمئن شود که کد با قوانین مالکیت سازگار است، ممکن است یک برنامه‌ی درست را رد کند؛ به این ترتیب، کامپایلر محافظه‌کارانه عمل می‌کند. اگر Rust یک برنامه‌ی نادرست را بپذیرد، کاربران دیگر نمی‌توانند به تضمین‌هایی که Rust ارائه می‌دهد اعتماد کنند. اما اگر Rust یک برنامه‌ی درست را رد کند، نهایتاً برنامه‌نویس دچار زحمت می‌شود، اما اتفاق فاجعه‌باری رخ نخواهد داد. نوع `RefCell` زمانی مفید است که شما اطمینان دارید کدتان از قوانین قرض‌گیری پیروی می‌کند، اما کامپایلر قادر به درک و تضمین این موضوع نیست. + +مشابه `Rc`، `RefCell` تنها برای استفاده در سناریوهای تک‌ریسمانی (single-threaded) +است و اگر بخواهید آن را در یک بافت چندریسمانی (multithreaded) استفاده کنید، یک خطای زمان +کامپایل به شما خواهد داد. ما در فصل 16 درباره نحوه دریافت عملکرد `RefCell` در یک برنامه +چندریسمانی صحبت خواهیم کرد. + +در اینجا مروری بر دلایلی برای انتخاب `Box`، `Rc` یا `RefCell` آمده است: + +- `Rc` امکان چندین مالک برای یک داده را فراهم می‌کند؛ در حالی که `Box` و + `RefCell` تنها یک مالک دارند. +- `Box` اجازه می‌دهد که وام‌دهی‌های غیرقابل‌تغییر یا قابل‌تغییر در زمان کامپایل + بررسی شوند؛ `Rc` تنها وام‌دهی‌های غیرقابل‌تغییر را در زمان کامپایل بررسی + می‌کند؛ `RefCell` اجازه می‌دهد که وام‌دهی‌های غیرقابل‌تغییر یا قابل‌تغییر در + زمان اجرا بررسی شوند. +- از آنجا که `RefCell` اجازه می‌دهد وام‌دهی‌های قابل‌تغییر در زمان اجرا بررسی شوند، + شما می‌توانید مقدار درون `RefCell` را حتی زمانی که خود `RefCell` غیرقابل‌تغییر + است، تغییر دهید. + +تغییر مقدار درون یک مقدار غیرقابل‌تغییر همان الگوی _تغییرپذیری داخلی_ است. بیایید به +یک موقعیت که در آن تغییرپذیری داخلی مفید است نگاهی بیندازیم و بررسی کنیم چگونه این +امر ممکن است. + +### تغییرپذیری داخلی: وام‌دهی قابل‌تغییر به یک مقدار غیرقابل‌تغییر + +یکی از پیامدهای قوانین وام‌دهی این است که وقتی شما یک مقدار غیرقابل‌تغییر دارید، +نمی‌توانید آن را به صورت قابل‌تغییر وام دهید. برای مثال، این کد کامپایل نخواهد شد: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch15-smart-pointers/no-listing-01-cant-borrow-immutable-as-mutable/src/main.rs}} ``` -If you tried to compile this code, you’d get the following error: +اگر سعی کنید این کد را کامپایل کنید، خطای زیر را دریافت خواهید کرد: ```console {{#include ../listings/ch15-smart-pointers/no-listing-01-cant-borrow-immutable-as-mutable/output.txt}} ``` -However, there are situations in which it would be useful for a value to mutate -itself in its methods but appear immutable to other code. Code outside the -value’s methods would not be able to mutate the value. Using `RefCell` is -one way to get the ability to have interior mutability, but `RefCell` -doesn’t get around the borrowing rules completely: the borrow checker in the -compiler allows this interior mutability, and the borrowing rules are checked -at runtime instead. If you violate the rules, you’ll get a `panic!` instead of -a compiler error. - -Let’s work through a practical example where we can use `RefCell` to mutate -an immutable value and see why that is useful. - -#### A Use Case for Interior Mutability: Mock Objects - -Sometimes during testing a programmer will use a type in place of another type, -in order to observe particular behavior and assert that it’s implemented -correctly. This placeholder type is called a _test double_. Think of it in the -sense of a stunt double in filmmaking, where a person steps in and substitutes -for an actor to do a particularly tricky scene. Test doubles stand in for other -types when we’re running tests. _Mock objects_ are specific types of test -doubles that record what happens during a test so you can assert that the -correct actions took place. - -Rust doesn’t have objects in the same sense as other languages have objects, -and Rust doesn’t have mock object functionality built into the standard library -as some other languages do. However, you can definitely create a struct that -will serve the same purposes as a mock object. - -Here’s the scenario we’ll test: we’ll create a library that tracks a value -against a maximum value and sends messages based on how close to the maximum -value the current value is. This library could be used to keep track of a -user’s quota for the number of API calls they’re allowed to make, for example. - -Our library will only provide the functionality of tracking how close to the -maximum a value is and what the messages should be at what times. Applications -that use our library will be expected to provide the mechanism for sending the -messages: the application could put a message in the application, send an email, -send a text message, or do something else. The library doesn’t need to know that -detail. All it needs is something that implements a trait we’ll provide called -`Messenger`. Listing 15-20 shows the library code. - -+با این حال، موقعیت‌هایی وجود دارند که در آن‌ها مفید است یک مقدار بتواند خود را در +متدهایش تغییر دهد اما برای کد دیگر غیرقابل‌تغییر به نظر برسد. کدی که خارج از متدهای +مقدار قرار دارد نمی‌تواند مقدار را تغییر دهد. استفاده از `RefCell` یکی از +راه‌هایی است که می‌توانید قابلیت تغییرپذیری داخلی را به دست آورید، اما `RefCell` +به طور کامل قوانین وام‌دهی را دور نمی‌زند: کنترل‌کننده وام‌دهی در کامپایلر این +تغییرپذیری داخلی را مجاز می‌کند و قوانین وام‌دهی در عوض در زمان اجرا بررسی می‌شوند. +اگر این قوانین را نقض کنید، به جای خطای کامپایل، یک `panic!` دریافت خواهید کرد. + +بیایید با یک مثال عملی کار کنیم که در آن از `RefCell` برای تغییر مقدار غیرقابل‌تغییر +استفاده کنیم و ببینیم چرا این کار مفید است. + +#### یک کاربرد برای تغییرپذیری داخلی: Mock Objects + +گاهی اوقات در زمان تست، یک برنامه‌نویس نوعی را به‌جای نوعی دیگر استفاده می‌کند تا بتواند رفتار خاصی را مشاهده کرده و بررسی کند که آن رفتار به‌درستی پیاده‌سازی شده است. این نوع جایگزین را *test double* می‌نامند. می‌توانید آن را مشابه *بدل‌کار* در صنعت فیلم‌سازی در نظر بگیرید، جایی که فردی به‌جای بازیگر اصلی برای اجرای یک صحنه‌ی دشوار وارد عمل می‌شود. Test doubleها به‌عنوان جایگزین نوع‌های دیگر هنگام اجرای تست‌ها عمل می‌کنند. _Mock object_ها نوع خاصی از test doubleها هستند که اتفاقات رخ‌داده در طول تست را ثبت می‌کنند تا بتوانید بررسی کنید که اقدامات موردنظر به‌درستی انجام شده‌اند. + +راست اشیاء را به همان شکلی که زبان‌های دیگر دارند، ندارد و قابلیت‌های اشیاء Mock +را نیز در کتابخانه استاندارد، مانند برخی زبان‌های دیگر، ارائه نمی‌دهد. با این حال، +شما می‌توانید یک ساختار (struct) ایجاد کنید که همان مقاصد اشیاء Mock را فراهم کند. + +در اینجا سناریویی که قصد تست آن را داریم آورده شده است: ما یک کتابخانه ایجاد +خواهیم کرد که یک مقدار را نسبت به یک مقدار حداکثری ردیابی می‌کند و بر اساس +نزدیکی مقدار فعلی به مقدار حداکثری پیام‌هایی ارسال می‌کند. به عنوان مثال، این +کتابخانه می‌تواند برای پیگیری سهمیه تعداد درخواست‌های API که یک کاربر مجاز است +انجام دهد، استفاده شود. + +کتابخانه‌ی ما تنها وظیفه‌ی پیگیری میزان نزدیکی یک مقدار به مقدار حداکثری و تعیین این‌که در چه زمان‌هایی چه پیام‌هایی باید نمایش داده شوند را بر عهده دارد. برنامه‌هایی که از کتابخانه‌ی ما استفاده می‌کنند، باید مکانیزم ارسال پیام را فراهم کنند: این برنامه می‌تواند پیام را درون رابط کاربری نمایش دهد، یک ایمیل ارسال کند، پیامک بفرستد، یا کار دیگری انجام دهد. کتابخانه نیازی به دانستن جزئیات این فرآیند ندارد. تنها چیزی که نیاز دارد، یک چیزی است که `trait`ای که ما تعریف خواهیم کرد به‌نام `Messenger` را پیاده‌سازی کند. لیستینگ 15-20 کد کتابخانه را نشان می‌دهد. + + ```rust,noplayground {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-20/src/lib.rs}} @@ -137,25 +112,11 @@ detail. All it needs is something that implements a trait we’ll provide called -One important part of this code is that the `Messenger` trait has one method -called `send` that takes an immutable reference to `self` and the text of the -message. This trait is the interface our mock object needs to implement so that -the mock can be used in the same way a real object is. The other important part -is that we want to test the behavior of the `set_value` method on the -`LimitTracker`. We can change what we pass in for the `value` parameter, but -`set_value` doesn’t return anything for us to make assertions on. We want to be -able to say that if we create a `LimitTracker` with something that implements -the `Messenger` trait and a particular value for `max`, when we pass different -numbers for `value` the messenger is told to send the appropriate messages. - -We need a mock object that, instead of sending an email or text message when we -call `send`, will only keep track of the messages it’s told to send. We can -create a new instance of the mock object, create a `LimitTracker` that uses the -mock object, call the `set_value` method on `LimitTracker`, and then check that -the mock object has the messages we expect. Listing 15-21 shows an attempt to -implement a mock object to do just that, but the borrow checker won’t allow it. - -+یکی از بخش‌های مهم این کد آن است که `trait` به‌نام `Messenger` یک متد به‌نام `send` دارد که یک رفرنس تغییرناپذیر به `self` و متن پیام را می‌گیرد. این `trait` رابطی است که شیء mock ما باید آن را پیاده‌سازی کند تا بتواند درست مانند یک شیء واقعی مورد استفاده قرار گیرد. بخش مهم دیگر این است که ما می‌خواهیم رفتار متد `set_value` روی `LimitTracker` را تست کنیم. ما می‌توانیم مقادیری که به پارامتر `value` می‌دهیم را تغییر دهیم، اما `set_value` چیزی را باز نمی‌گرداند که بتوانیم روی آن assertion انجام دهیم. ما می‌خواهیم بتوانیم بگوییم که اگر یک `LimitTracker` با چیزی که `trait` `Messenger` را پیاده‌سازی می‌کند و یک مقدار مشخص برای `max` ایجاد کنیم، آنگاه با ارسال اعداد مختلف به‌عنوان `value`، پیام‌های مناسب از طریق `messenger` ارسال شوند. + +ما به یک شیء mock نیاز داریم که به‌جای ارسال ایمیل یا پیامک هنگام فراخوانی `send`، فقط پیام‌هایی را که قرار است ارسال شوند ذخیره کند. می‌توانیم یک نمونه‌ی جدید از شیء mock ایجاد کنیم، یک `LimitTracker` بسازیم که از این mock استفاده می‌کند، متد `set_value` را روی `LimitTracker` فراخوانی کنیم، و سپس بررسی کنیم که شیء mock پیام‌هایی را که انتظار داشتیم در خود ذخیره کرده است. لیستینگ 15-21 تلاشی برای پیاده‌سازی چنین شیء mockی را نشان می‌دهد، اما *borrow checker* اجازه‌ی انجام آن را نمی‌دهد. + + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-21/src/lib.rs:here}} @@ -163,43 +124,28 @@ implement a mock object to do just that, but the borrow checker won’t allow it -This test code defines a `MockMessenger` struct that has a `sent_messages` -field with a `Vec` of `String` values to keep track of the messages it’s told -to send. We also define an associated function `new` to make it convenient to -create new `MockMessenger` values that start with an empty list of messages. We -then implement the `Messenger` trait for `MockMessenger` so we can give a -`MockMessenger` to a `LimitTracker`. In the definition of the `send` method, we -take the message passed in as a parameter and store it in the `MockMessenger` -list of `sent_messages`. - -In the test, we’re testing what happens when the `LimitTracker` is told to set -`value` to something that is more than 75 percent of the `max` value. First we -create a new `MockMessenger`, which will start with an empty list of messages. -Then we create a new `LimitTracker` and give it a reference to the new -`MockMessenger` and a `max` value of `100`. We call the `set_value` method on -the `LimitTracker` with a value of `80`, which is more than 75 percent of 100. -Then we assert that the list of messages that the `MockMessenger` is keeping -track of should now have one message in it. - -However, there’s one problem with this test, as shown here: +این کد تست یک ساختار `MockMessenger` تعریف می‌کند که یک فیلد `sent_messages` با یک +`Vec` از مقادیر `String` دارد تا پیام‌هایی را که به آن گفته شده است ارسال کند، +پیگیری کند. ما همچنین یک تابع مرتبط `new` تعریف می‌کنیم تا ایجاد مقادیر +`MockMessenger` جدید که با یک لیست خالی از پیام‌ها شروع می‌شود، راحت باشد. سپس +ویژگی `Messenger` را برای `MockMessenger` پیاده‌سازی می‌کنیم تا بتوانیم یک +`MockMessenger` را به یک `LimitTracker` بدهیم. در تعریف متد `send`، ما پیام +ارسال‌شده به عنوان یک پارامتر را می‌گیریم و آن را در لیست `sent_messages` +درون `MockMessenger` ذخیره می‌کنیم. + +در این تست، در حال بررسی این هستیم که وقتی به `LimitTracker` گفته می‌شود مقدار `value` را به چیزی بیشتر از ۷۵٪ مقدار `max` تنظیم کند، چه اتفاقی می‌افتد. ابتدا یک `MockMessenger` جدید می‌سازیم که با یک لیست خالی از پیام‌ها شروع می‌کند. سپس یک `LimitTracker` جدید ایجاد می‌کنیم و یک رفرنس به `MockMessenger` جدید و همچنین مقدار `max` برابر با `100` به آن می‌دهیم. متد `set_value` را با مقدار `80` روی `LimitTracker` فراخوانی می‌کنیم، که بیش از ۷۵٪ عدد ۱۰۰ است. سپس بررسی می‌کنیم (assert) که لیست پیام‌هایی که `MockMessenger` پیگیری می‌کند، اکنون باید شامل یک پیام باشد. + +با این حال، یک مشکل با این تست وجود دارد، همانطور که در اینجا نشان داده شده است: ```console {{#include ../listings/ch15-smart-pointers/listing-15-21/output.txt}} ``` -We can’t modify the `MockMessenger` to keep track of the messages because the -`send` method takes an immutable reference to `self`. We also can’t take the -suggestion from the error text to use `&mut self` in both the `impl` method and -the trait definition. We do not want to change the `Messenger` trait solely for -the sake of testing. Instead, we need to find a way to make our test code work -correctly with our existing design. +ما نمی‌توانیم `MockMessenger` را طوری تغییر دهیم که پیام‌ها را دنبال کند، چون متد `send` یک رفرنس تغییرناپذیر به `self` دریافت می‌کند. همچنین نمی‌توانیم پیشنهاد پیام خطا را بپذیریم و `&mut self` را هم در متد `impl` و هم در تعریف `trait` قرار دهیم، زیرا نمی‌خواهیم فقط به‌خاطر تست، `trait` `Messenger` را تغییر دهیم. در عوض، باید راهی پیدا کنیم که کد تست ما با طراحی فعلی به‌درستی کار کند. -This is a situation in which interior mutability can help! We’ll store the -`sent_messages` within a `RefCell`, and then the `send` method will be -able to modify `sent_messages` to store the messages we’ve seen. Listing 15-22 -shows what that looks like. +در چنین وضعیتی، *تغییرپذیری درونی* (interior mutability) می‌تواند به کمک ما بیاید! ما فیلد `sent_messages` را درون یک `RefCell` ذخیره می‌کنیم، و سپس متد `send` می‌تواند مقدار `sent_messages` را تغییر دهد تا پیام‌هایی را که دیده‌ایم ذخیره کند. لیستینگ 15-22 نشان می‌دهد که این کار چگونه انجام می‌شود. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-22/src/lib.rs:here}} @@ -207,47 +153,41 @@ shows what that looks like. -The `sent_messages` field is now of type `RefCell>` instead of -`Vec`. In the `new` function, we create a new `RefCell>` -instance around the empty vector. +فیلد `sent_messages` اکنون از نوع `RefCell>` به جای `Vec` است. +در تابع `new`، یک نمونه جدید از `RefCell>` را در اطراف وکتور خالی ایجاد +می‌کنیم. -For the implementation of the `send` method, the first parameter is still an -immutable borrow of `self`, which matches the trait definition. We call -`borrow_mut` on the `RefCell>` in `self.sent_messages` to get a -mutable reference to the value inside the `RefCell>`, which is the -vector. Then we can call `push` on the mutable reference to the vector to keep -track of the messages sent during the test. +برای پیاده‌سازی متد `send`، پارامتر اول همچنان یک وام‌دهی غیرقابل‌تغییر به `self` +است، که با تعریف ویژگی مطابقت دارد. ما متد `borrow_mut` را روی `RefCell>` +در `self.sent_messages` فراخوانی می‌کنیم تا یک ارجاع متغیر به مقدار درون +`RefCell>`، که همان وکتور است، دریافت کنیم. سپس می‌توانیم روی ارجاع +متغیر به وکتور، متد `push` را فراخوانی کنیم تا پیام‌های ارسال‌شده در طول تست را پیگیری +کنیم. -The last change we have to make is in the assertion: to see how many items are -in the inner vector, we call `borrow` on the `RefCell>` to get an -immutable reference to the vector. +آخرین تغییری که باید انجام دهیم در ادعا (assertion) است: برای دیدن تعداد آیتم‌های +درون وکتور داخلی، ما متد `borrow` را روی `RefCell>` فراخوانی می‌کنیم تا +یک ارجاع غیرقابل‌تغییر به وکتور دریافت کنیم. -Now that you’ve seen how to use `RefCell`, let’s dig into how it works! +حالا که دیدید چگونه از `RefCell` استفاده کنید، بیایید به نحوه کار آن بپردازیم! -#### Keeping Track of Borrows at Runtime with `RefCell` +#### پیگیری وام‌ها در زمان اجرا با `RefCell` -When creating immutable and mutable references, we use the `&` and `&mut` -syntax, respectively. With `RefCell`, we use the `borrow` and `borrow_mut` -methods, which are part of the safe API that belongs to `RefCell`. The -`borrow` method returns the smart pointer type `Ref`, and `borrow_mut` -returns the smart pointer type `RefMut`. Both types implement `Deref`, so we -can treat them like regular references. +هنگام ایجاد ارجاع‌های غیرقابل‌تغییر و قابل‌تغییر، ما از سینتکس `&` و `&mut` استفاده +می‌کنیم. با `RefCell`، از متدهای `borrow` و `borrow_mut` استفاده می‌کنیم، که +بخشی از API ایمن متعلق به `RefCell` هستند. متد `borrow` نوع اسمارت پوینتر +`Ref` را برمی‌گرداند، و `borrow_mut` نوع اسمارت پوینتر `RefMut` را برمی‌گرداند. +هر دو نوع، `Deref` را پیاده‌سازی می‌کنند، بنابراین می‌توانیم با آن‌ها مثل ارجاع‌های +معمولی رفتار کنیم. -The `RefCell` keeps track of how many `Ref` and `RefMut` smart -pointers are currently active. Every time we call `borrow`, the `RefCell` -increases its count of how many immutable borrows are active. When a `Ref` -value goes out of scope, the count of immutable borrows goes down by 1. Just -like the compile-time borrowing rules, `RefCell` lets us have many immutable -borrows or one mutable borrow at any point in time. +`RefCell` تعداد `Ref` و `RefMut`هایی را که در حال حاضر فعال هستند، دنبال می‌کند. هر بار که متد `borrow` را فراخوانی می‌کنیم، `RefCell` شمارنده‌ی رفرنس‌های تغییرناپذیر فعال را افزایش می‌دهد. زمانی که یک مقدار `Ref` از حوزه‌ی دید (scope) خارج می‌شود، این شمارنده یک واحد کاهش می‌یابد. درست مانند قوانین قرض‌گیری در زمان کامپایل، `RefCell` نیز به ما اجازه می‌دهد که در هر لحظه *یا* چندین رفرنس تغییرناپذیر داشته باشیم *یا* یک رفرنس قابل‌تغییر، اما نه هر دو به‌طور همزمان. -If we try to violate these rules, rather than getting a compiler error as we -would with references, the implementation of `RefCell` will panic at -runtime. Listing 15-23 shows a modification of the implementation of `send` in -Listing 15-22. We’re deliberately trying to create two mutable borrows active -for the same scope to illustrate that `RefCell` prevents us from doing this -at runtime. +اگر سعی کنیم این قوانین را نقض کنیم، به جای دریافت یک خطای کامپایل مثل ارجاع‌ها، +پیاده‌سازی `RefCell` در زمان اجرا دچار وحشت (panic) خواهد شد. فهرست 15-23 +اصلاحی از پیاده‌سازی متد `send` در فهرست 15-22 را نشان می‌دهد. ما به عمد سعی داریم +دو وام‌دهی قابل‌تغییر در یک دامنه ایجاد کنیم تا نشان دهیم `RefCell` از انجام +این کار در زمان اجرا جلوگیری می‌کند. -+ ```rust,ignore,panics {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-23/src/lib.rs:here}} @@ -255,50 +195,46 @@ at runtime. -We create a variable `one_borrow` for the `RefMut` smart pointer returned -from `borrow_mut`. Then we create another mutable borrow in the same way in the -variable `two_borrow`. This makes two mutable references in the same scope, -which isn’t allowed. When we run the tests for our library, the code in Listing -15-23 will compile without any errors, but the test will fail: +ما یک متغیر به نام `one_borrow` برای اسمارت پوینتر `RefMut` که از `borrow_mut` +بازگردانده شده است، ایجاد می‌کنیم. سپس یک وام‌دهی متغیر دیگر به همان روش در +متغیر `two_borrow` ایجاد می‌کنیم. این کار دو ارجاع متغیر در یک دامنه ایجاد می‌کند، +که مجاز نیست. هنگامی که تست‌ها را برای کتابخانه خود اجرا می‌کنیم، کد در فهرست +15-23 بدون هیچ خطایی کامپایل می‌شود، اما تست شکست خواهد خورد: ```console {{#include ../listings/ch15-smart-pointers/listing-15-23/output.txt}} ``` -Notice that the code panicked with the message `already borrowed: -BorrowMutError`. This is how `RefCell` handles violations of the borrowing -rules at runtime. - -Choosing to catch borrowing errors at runtime rather than compile time, as -we’ve done here, means you’d potentially be finding mistakes in your code later -in the development process: possibly not until your code was deployed to -production. Also, your code would incur a small runtime performance penalty as -a result of keeping track of the borrows at runtime rather than compile time. -However, using `RefCell` makes it possible to write a mock object that can -modify itself to keep track of the messages it has seen while you’re using it -in a context where only immutable values are allowed. You can use `RefCell` -despite its trade-offs to get more functionality than regular references -provide. +توجه داشته باشید که کد با پیام `already borrowed: BorrowMutError` دچار وحشت +(panic) شد. این نحوه عملکرد `RefCell` برای مدیریت نقض قوانین وام‌دهی در زمان +اجرا است. + +انتخاب اینکه خطاهای وام‌دهی در زمان اجرا و نه در زمان کامپایل بررسی شوند، همانطور +که در اینجا انجام دادیم، به این معنا است که ممکن است اشتباهات در کد شما در مراحل +بعدی فرآیند توسعه کشف شوند: شاید حتی تا زمانی که کد شما به محیط تولید +(production) استقرار یابد. همچنین، کد شما جریمه عملکردی کوچکی را به دلیل پیگیری +وام‌ها در زمان اجرا به جای زمان کامپایل متحمل خواهد شد. با این حال، استفاده از +`RefCell` امکان نوشتن یک شیء Mock را فراهم می‌کند که می‌تواند خود را تغییر +دهد تا پیام‌هایی که مشاهده کرده است را پیگیری کند، در حالی که شما آن را در یک +زمینه که تنها مقادیر غیرقابل‌تغییر مجاز هستند استفاده می‌کنید. شما می‌توانید +با وجود این مبادلات، از `RefCell` برای دریافت عملکرد بیشتری نسبت به +ارجاع‌های معمولی استفاده کنید. -### Allowing Multiple Owners of Mutable Data with `Rc` and `RefCell` +### اجازه‌دادن به چند مالک برای داده‌ی قابل‌تغییر با استفاده از `Rc` و `RefCell` -A common way to use `RefCell` is in combination with `Rc`. Recall that -`Rc` lets you have multiple owners of some data, but it only gives immutable -access to that data. If you have an `Rc` that holds a `RefCell`, you can -get a value that can have multiple owners _and_ that you can mutate! +یک روش رایج برای استفاده از `RefCell` ترکیب آن با `Rc` است. به خاطر +بیاورید که `Rc` به شما اجازه می‌دهد چندین مالک برای برخی داده‌ها داشته +باشید، اما فقط دسترسی غیرقابل‌تغییر به آن داده‌ها را می‌دهد. اگر یک `Rc` +داشته باشید که یک `RefCell` را نگه می‌دارد، می‌توانید یک مقداری داشته باشید +که می‌تواند چندین مالک داشته باشد _و_ شما بتوانید آن را تغییر دهید! -For example, recall the cons list example in Listing 15-18 where we used `Rc` -to allow multiple lists to share ownership of another list. Because `Rc` -holds only immutable values, we can’t change any of the values in the list once -we’ve created them. Let’s add in `RefCell` for its ability to change the -values in the lists. Listing 15-24 shows that by using a `RefCell` in the -`Cons` definition, we can modify the value stored in all the lists. +برای مثال، لیست cons در لیستینگ 15-18 را به‌خاطر بیاورید که در آن از `Rc` استفاده کردیم تا چند لیست بتوانند مالکیت مشترک روی یک لیست دیگر داشته باشند. از آن‌جا که `Rc` فقط مقادیر تغییرناپذیر را نگه می‌دارد، پس از ایجاد لیست‌ها دیگر نمی‌توانیم هیچ‌یک از مقادیر درون آن‌ها را تغییر دهیم. بیایید `RefCell` را به خاطر توانایی‌اش در تغییر مقادیر، به ترکیب اضافه کنیم. لیستینگ 15-24 نشان می‌دهد که با استفاده از `RefCell` در تعریف `Cons`، می‌توانیم مقدار ذخیره‌شده در تمام لیست‌ها را تغییر دهیم. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-24/src/main.rs}} @@ -306,38 +242,24 @@ values in the lists. Listing 15-24 shows that by using a `RefCell` in the -We create a value that is an instance of `Rc>` and store it in a -variable named `value` so we can access it directly later. Then we create a -`List` in `a` with a `Cons` variant that holds `value`. We need to clone -`value` so both `a` and `value` have ownership of the inner `5` value rather -than transferring ownership from `value` to `a` or having `a` borrow from -`value`. +ما مقداری که نمونه‌ای از `Rc>` است ایجاد می‌کنیم و آن را در یک +متغیر به نام `value` ذخیره می‌کنیم تا بتوانیم بعداً به طور مستقیم به آن دسترسی +داشته باشیم. سپس یک `List` در `a` با یک متغیر `Cons` که `value` را نگه می‌دارد +ایجاد می‌کنیم. ما نیاز داریم `value` را کلون کنیم تا هر دو `a` و `value` مالک +مقدار داخلی `5` باشند، به جای انتقال مالکیت از `value` به `a` یا اینکه `a` از +`value` وام بگیرد. -We wrap the list `a` in an `Rc` so that when we create lists `b` and `c`, -they can both refer to `a`, which is what we did in Listing 15-18. +لیست `a` را در یک `Rc` قرار می‌دهیم تا زمانی که لیست‌های `b` و `c` را ایجاد می‌کنیم، هر دو بتوانند به `a` اشاره کنند؛ کاری که در لیستینگ 15-18 نیز انجام دادیم. -After we’ve created the lists in `a`, `b`, and `c`, we want to add 10 to the -value in `value`. We do this by calling `borrow_mut` on `value`, which uses the -automatic dereferencing feature we discussed in [“Where’s the `->` -Operator?”][wheres-the---operator]) in Chapter 5 to dereference -the `Rc` to the inner `RefCell` value. The `borrow_mut` method returns a -`RefMut` smart pointer, and we use the dereference operator on it and change -the inner value. +پس از آن‌که لیست‌های `a`، `b` و `c` ایجاد شدند، می‌خواهیم عدد ۱۰ را به مقدار موجود در `value` اضافه کنیم. این کار را با فراخوانی متد `borrow_mut` روی `value` انجام می‌دهیم؛ این متد از قابلیت dereferencing خودکار (که در فصل ۵ در بخش [«عملگر `->` کجاست؟»][wheres-the---operator] درباره‌اش صحبت کردیم) استفاده می‌کند تا `Rc` را به مقدار درونی از نوع `RefCell` dereference کند. متد `borrow_mut` یک smart pointer از نوع `RefMut` برمی‌گرداند، و ما با استفاده از عملگر `*` (dereference) مقدار درونی را تغییر می‌دهیم. -When we print `a`, `b`, and `c`, we can see that they all have the modified -value of `15` rather than `5`: +وقتی `a`، `b` و `c` را چاپ می‌کنیم، می‌بینیم که همه‌ی آن‌ها مقدار تغییر یافته‌ی `15` را دارند، نه مقدار اولیه‌ی `5`: ```console {{#include ../listings/ch15-smart-pointers/listing-15-24/output.txt}} ``` -This technique is pretty neat! By using `RefCell`, we have an outwardly -immutable `List` value. But we can use the methods on `RefCell` that provide -access to its interior mutability so we can modify our data when we need to. -The runtime checks of the borrowing rules protect us from data races, and it’s -sometimes worth trading a bit of speed for this flexibility in our data -structures. Note that `RefCell` does not work for multithreaded code! -`Mutex` is the thread-safe version of `RefCell`, and we’ll discuss -`Mutex` in Chapter 16. +این تکنیک واقعاً جالب است! با استفاده از `RefCell`، یک مقدار `List` داریم که از بیرون تغییرناپذیر به‌نظر می‌رسد. اما می‌توانیم با استفاده از متدهای `RefCell` که دسترسی به *تغییرپذیری درونی* را فراهم می‌کنند، داده‌های خود را در مواقع نیاز تغییر دهیم. بررسی‌های زمان اجرا (runtime) برای قوانین قرض‌گیری از بروز *data race* جلوگیری می‌کنند، و گاهی ارزش دارد که اندکی از سرعت را فدای این انعطاف‌پذیری در ساختارهای داده کنیم. +توجه داشته باشید که `RefCell` برای کد چندنخی (multithreaded) قابل‌استفاده نیست! `Mutex` نسخه‌ی *ایمن در برابر نخ* (thread-safe) از `RefCell` است، و ما در فصل ۱۶ درباره‌ی `Mutex` صحبت خواهیم کرد. [wheres-the---operator]: ch05-03-method-syntax.html#wheres-the---operator diff --git a/src/ch15-06-reference-cycles.md b/src/ch15-06-reference-cycles.md index 5402e16bc5..24012a1c15 100644 --- a/src/ch15-06-reference-cycles.md +++ b/src/ch15-06-reference-cycles.md @@ -1,21 +1,18 @@ -## Reference Cycles Can Leak Memory +## چرخه‌های ارجاعی می‌توانند منجر به نشت حافظه شوند -Rust’s memory safety guarantees make it difficult, but not impossible, to -accidentally create memory that is never cleaned up (known as a _memory leak_). -Preventing memory leaks entirely is not one of Rust’s guarantees, meaning -memory leaks are memory safe in Rust. We can see that Rust allows memory leaks -by using `Rc` and `RefCell`: it’s possible to create references where -items refer to each other in a cycle. This creates memory leaks because the -reference count of each item in the cycle will never reach 0, and the values -will never be dropped. +تضمین‌های ایمنی حافظه راست ایجاد حافظه‌ای که هرگز پاک نمی‌شود (که به عنوان _نشت حافظه_ +شناخته می‌شود) را دشوار می‌کنند، اما غیرممکن نمی‌کنند. جلوگیری کامل از نشت حافظه +یکی از تضمین‌های راست نیست، به این معنی که نشت حافظه در راست ایمن است. ما می‌توانیم +ببینیم که راست اجازه نشت حافظه را می‌دهد با استفاده از `Rc` و `RefCell`: +امکان ایجاد ارجاع‌هایی وجود دارد که آیتم‌ها در آن به یکدیگر در یک چرخه ارجاع می‌دهند. +این باعث نشت حافظه می‌شود، زیرا شمارش ارجاع هر آیتم در چرخه هرگز به 0 نمی‌رسد و +مقادیر هرگز حذف نمی‌شوند. -### Creating a Reference Cycle +### ایجاد یک چرخه ارجاعی -Let’s look at how a reference cycle might happen and how to prevent it, -starting with the definition of the `List` enum and a `tail` method in Listing -15-25. +بیایید بررسی کنیم که چگونه ممکن است یک چرخه‌ی رفرنس (reference cycle) به‌وجود بیاید و چگونه می‌توان از آن جلوگیری کرد. این بررسی را با تعریف `enum`ی به نام `List` و متدی به نام `tail` در لیستینگ 15-25 آغاز می‌کنیم. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-25/src/main.rs}} @@ -23,20 +20,19 @@ starting with the definition of the `List` enum and a `tail` method in Listing -We’re using another variation of the `List` definition from Listing 15-5. The -second element in the `Cons` variant is now `RefCell>`, meaning that -instead of having the ability to modify the `i32` value as we did in Listing -15-24, we want to modify the `List` value a `Cons` variant is pointing to. -We’re also adding a `tail` method to make it convenient for us to access the -second item if we have a `Cons` variant. +ما از یک نسخه دیگر از تعریف `List` که در فهرست 15-5 آمده بود استفاده می‌کنیم. +عنصر دوم در متغیر `Cons` اکنون `RefCell>` است، به این معنی که به جای +توانایی تغییر مقدار `i32` که در فهرست 15-24 داشتیم، می‌خواهیم مقدار `List` را که +یک متغیر `Cons` به آن اشاره می‌کند، تغییر دهیم. همچنین، یک متد `tail` اضافه +می‌کنیم تا دسترسی به آیتم دوم را در صورتی که یک متغیر `Cons` داریم، راحت‌تر کنیم. -In Listing 15-26, we’re adding a `main` function that uses the definitions in -Listing 15-25. This code creates a list in `a` and a list in `b` that points to -the list in `a`. Then it modifies the list in `a` to point to `b`, creating a -reference cycle. There are `println!` statements along the way to show what the -reference counts are at various points in this process. +در فهرست 15-26، یک تابع `main` اضافه می‌کنیم که از تعاریف فهرست 15-25 استفاده می‌کند. +این کد لیستی در `a` و لیستی در `b` ایجاد می‌کند که به لیست `a` اشاره می‌کند. +سپس لیست در `a` را تغییر می‌دهد تا به `b` اشاره کند و یک چرخه ارجاعی ایجاد کند. +در طول این فرآیند، اظهارات `println!` وجود دارند که نشان می‌دهند شمارش ارجاع +در نقاط مختلف چه مقدار است. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-26/src/main.rs:here}} @@ -44,110 +40,59 @@ reference counts are at various points in this process. -We create an `Rc` instance holding a `List` value in the variable `a` -with an initial list of `5, Nil`. We then create an `Rc` instance holding -another `List` value in the variable `b` that contains the value `10` and points -to the list in `a`. +ما یک نمونه‌ی `Rc` ایجاد می‌کنیم که یک مقدار از نوع `List` را در متغیر `a` نگه می‌دارد و لیست اولیه‌ای به شکل `5, Nil` دارد. سپس یک نمونه‌ی دیگر از `Rc` در متغیر `b` می‌سازیم که مقداری برابر با `10` دارد و به لیست موجود در `a` اشاره می‌کند. -We modify `a` so it points to `b` instead of `Nil`, creating a cycle. We do -that by using the `tail` method to get a reference to the `RefCell>` -in `a`, which we put in the variable `link`. Then we use the `borrow_mut` -method on the `RefCell>` to change the value inside from an `Rc` -that holds a `Nil` value to the `Rc` in `b`. +ما `a` را تغییر می‌دهیم تا به جای `Nil` به `b` اشاره کند، و یک چرخه ایجاد می‌کنیم. +این کار را با استفاده از متد `tail` انجام می‌دهیم تا یک ارجاع به `RefCell>` +در `a` بگیریم، که آن را در متغیر `link` قرار می‌دهیم. سپس از متد `borrow_mut` +روی `RefCell>` استفاده می‌کنیم تا مقدار داخلی را از یک `Rc` که +مقدار `Nil` را نگه می‌دارد به `Rc` در `b` تغییر دهیم. -When we run this code, keeping the last `println!` commented out for the -moment, we’ll get this output: +وقتی این کد را اجرا می‌کنیم و `println!` آخر را به طور موقت کامنت می‌کنیم، خروجی زیر را دریافت می‌کنیم: ```console {{#include ../listings/ch15-smart-pointers/listing-15-26/output.txt}} ``` -The reference count of the `Rc` instances in both `a` and `b` is 2 after -we change the list in `a` to point to `b`. At the end of `main`, Rust drops the -variable `b`, which decreases the reference count of the `b` `Rc` instance -from 2 to 1. The memory that `Rc` has on the heap won’t be dropped at -this point because its reference count is 1, not 0. Then Rust drops `a`, which -decreases the reference count of the `a` `Rc` instance from 2 to 1 as -well. This instance’s memory can’t be dropped either, because the other -`Rc` instance still refers to it. The memory allocated to the list will -remain uncollected forever. To visualize this reference cycle, we’ve created the -diagram in Figure 15-4. - -A rectangle labeled 'a' that points to a rectangle containing the integer 5. A rectangle labeled 'b' that points to a rectangle containing the integer 10. The rectangle containing 5 points to the rectangle containing 10, and the rectangle containing 10 points back to the rectangle containing 5, creating a cycle - -Figure 15-4: A reference cycle of lists `a` and `b` -pointing to each other - -If you uncomment the last `println!` and run the program, Rust will try to print -this cycle with `a` pointing to `b` pointing to `a` and so forth until it -overflows the stack. - -Compared to a real-world program, the consequences of creating a reference cycle -in this example aren’t very dire: right after we create the reference cycle, -the program ends. However, if a more complex program allocated lots of memory -in a cycle and held onto it for a long time, the program would use more memory -than it needed and might overwhelm the system, causing it to run out of -available memory. - -Creating reference cycles is not easily done, but it’s not impossible either. -If you have `RefCell` values that contain `Rc` values or similar nested -combinations of types with interior mutability and reference counting, you must -ensure that you don’t create cycles; you can’t rely on Rust to catch them. -Creating a reference cycle would be a logic bug in your program that you should -use automated tests, code reviews, and other software development practices to -minimize. - -Another solution for avoiding reference cycles is reorganizing your data -structures so that some references express ownership and some references don’t. -As a result, you can have cycles made up of some ownership relationships and -some non-ownership relationships, and only the ownership relationships affect -whether or not a value can be dropped. In Listing 15-25, we always want `Cons` -variants to own their list, so reorganizing the data structure isn’t possible. -Let’s look at an example using graphs made up of parent nodes and child nodes -to see when non-ownership relationships are an appropriate way to prevent -reference cycles. +تعداد رفرنس‌های نمونه‌های `Rc` در هر دو متغیر `a` و `b` پس از آن‌که لیست در `a` را طوری تغییر می‌دهیم که به `b` اشاره کند، برابر با ۲ خواهد شد. در پایان تابع `main`، Rust متغیر `b` را حذف می‌کند، که شمارنده‌ی رفرنس نمونه‌ی `Rc` مربوط به `b` را از ۲ به ۱ کاهش می‌دهد. حافظه‌ای که `Rc` در heap نگه می‌دارد در این لحظه آزاد نخواهد شد، چون شمارنده‌ی رفرنس آن هنوز ۱ است، نه صفر. سپس Rust متغیر `a` را حذف می‌کند، که شمارنده‌ی رفرنس نمونه‌ی `Rc` مربوط به `a` را نیز از ۲ به ۱ کاهش می‌دهد. حافظه‌ی این نمونه نیز نمی‌تواند آزاد شود، زیرا نمونه‌ی دیگر از `Rc` هنوز به آن اشاره دارد. در نتیجه، حافظه‌ای که به این لیست اختصاص داده شده است، برای همیشه آزاد نخواهد شد. + +برای تصویرسازی این چرخه‌ی رفرنس، دیاگرام زیر را در شکل 15-4 ایجاد کرده‌ایم: + +مستطیلی با برچسب 'a' که به مستطیلی شامل عدد صحیح 5 اشاره می‌کند. مستطیلی با برچسب 'b' که به مستطیلی شامل عدد صحیح 10 اشاره می‌کند. مستطیل حاوی عدد 5 به مستطیل حاوی عدد 10 اشاره دارد، و مستطیل حاوی عدد 10 دوباره به مستطیل حاوی عدد 5 اشاره دارد، و این یک چرخه ایجاد می‌کند + +شکل 15-4: یک چرخه ارجاعی از لیست‌های `a` و `b` که به یکدیگر اشاره می‌کنند + +اگر آخرین `println!` را از حالت کامنت خارج کرده و برنامه را اجرا کنید، Rust تلاش خواهد کرد این چرخه را چاپ کند؛ چرخه‌ای که در آن `a` به `b` اشاره می‌کند، `b` به `a` و دوباره `a` به `b` و همین‌طور ادامه پیدا می‌کند تا جایی که پشته (stack) پر شده و *stack overflow* رخ می‌دهد. + +در مقایسه با یک برنامه واقعی، عواقب ایجاد چرخه ارجاعی در این مثال چندان وخیم نیست: درست بعد از اینکه چرخه ارجاعی ایجاد می‌شود، برنامه پایان می‌یابد. با این حال، اگر یک برنامه پیچیده‌تر مقدار زیادی حافظه در یک چرخه تخصیص دهد و برای مدت طولانی آن را نگه دارد، برنامه بیشتر از حافظه‌ای که نیاز دارد استفاده خواهد کرد و ممکن است سیستم را از حافظه موجود تخلیه کند. + +ایجاد چرخه‌های ارجاعی کار آسانی نیست، اما غیرممکن هم نیست. اگر مقادیر `RefCell` داشته باشید که مقادیر `Rc` یا ترکیبات مشابهی از انواع با تغییرپذیری داخلی و شمارش ارجاع را در خود جای دهند، باید مطمئن شوید که چرخه‌ای ایجاد نمی‌کنید؛ نمی‌توانید به راست اعتماد کنید که آن‌ها را شناسایی کند. ایجاد چرخه ارجاعی یک اشکال منطقی در برنامه شما خواهد بود که باید با استفاده از تست‌های خودکار، بررسی کد، و دیگر شیوه‌های توسعه نرم‌افزار، آن را به حداقل برسانید. + +یک راه‌حل دیگر برای جلوگیری از چرخه‌های ارجاعی، بازسازی ساختار داده‌هایتان است به‌طوری که برخی ارجاعات بیانگر مالکیت باشند و برخی نباشند. به این ترتیب، می‌توانید چرخه‌هایی داشته باشید که شامل برخی روابط مالکیت و برخی روابط غیرمالکیت هستند، و تنها روابط مالکیت تعیین می‌کنند که آیا یک مقدار می‌تواند حذف شود یا خیر. در فهرست 15-25، ما همیشه می‌خواهیم که متغیرهای `Cons` مالک لیست‌هایشان باشند، بنابراین بازسازی ساختار داده امکان‌پذیر نیست. بیایید به یک مثال با استفاده از گراف‌ها که شامل گره‌های والد و فرزند هستند نگاه کنیم تا ببینیم چه زمانی روابط غیرمالکیت یک راه مناسب برای جلوگیری از چرخه‌های ارجاعی هستند. -### Preventing Reference Cycles Using `Weak` - -So far, we’ve demonstrated that calling `Rc::clone` increases the `strong_count` -of an `Rc` instance, and an `Rc` instance is only cleaned up if its -`strong_count` is 0. You can also create a weak reference to the value within -an `Rc` instance by calling `Rc::downgrade` and passing a reference to the -`Rc`. _Strong references_ are how you can share ownership of an `Rc` -instance. _Weak references_ don’t express an ownership relationship, and their -count doesn’t affect when an `Rc` instance is cleaned up. They won’t cause a -reference cycle because any cycle involving some weak references will be broken -once the strong reference count of values involved is 0. - -When you call `Rc::downgrade`, you get a smart pointer of type `Weak`. -Instead of increasing the `strong_count` in the `Rc` instance by 1, calling -`Rc::downgrade` increases the `weak_count` by 1. The `Rc` type uses -`weak_count` to keep track of how many `Weak` references exist, similar to -`strong_count`. The difference is the `weak_count` doesn’t need to be 0 for the -`Rc` instance to be cleaned up. - -Because the value that `Weak` references might have been dropped, to do -anything with the value that a `Weak` is pointing to you must make sure the -value still exists. Do this by calling the `upgrade` method on a `Weak` -instance, which will return an `Option>`. You’ll get a result of `Some` -if the `Rc` value has not been dropped yet and a result of `None` if the -`Rc` value has been dropped. Because `upgrade` returns an `Option>`, -Rust will ensure that the `Some` case and the `None` case are handled, and -there won’t be an invalid pointer. - -As an example, rather than using a list whose items know only about the next -item, we’ll create a tree whose items know about their children items _and_ -their parent items. - -#### Creating a Tree Data Structure: A `Node` with Child Nodes - -To start, we’ll build a tree with nodes that know about their child nodes. -We’ll create a struct named `Node` that holds its own `i32` value as well as -references to its children `Node` values: +### جلوگیری از چرخه‌های رفرنس با استفاده از `Weak` + +تا این‌جا نشان دادیم که فراخوانی `Rc::clone` شمارنده‌ی `strong_count` یک نمونه‌ی `Rc` را افزایش می‌دهد، و یک نمونه‌ی `Rc` تنها زمانی پاک‌سازی می‌شود که مقدار `strong_count` آن برابر با صفر باشد. همچنین می‌توانید با فراخوانی `Rc::downgrade` و ارسال یک رفرنس به `Rc`، یک *رفرنس ضعیف* (weak reference) به مقدار درون یک نمونه‌ی `Rc` ایجاد کنید. + +*رفرنس‌های قوی* (strong references) روشی برای به‌اشتراک‌گذاری مالکیت یک نمونه‌ی `Rc` هستند. در مقابل، *رفرنس‌های ضعیف* رابطه‌ی مالکیتی ایجاد نمی‌کنند، و شمارش آن‌ها (weak count) هیچ تأثیری در زمان پاک‌سازی یک نمونه‌ی `Rc` ندارد. آن‌ها باعث ایجاد چرخه‌ی رفرنس نمی‌شوند، زیرا هر چرخه‌ای که شامل برخی `weak reference` باشد، زمانی شکسته می‌شود که شمارنده‌ی قوی (`strong_count`) مقادیر درگیر در آن چرخه به صفر برسد. + +وقتی `Rc::downgrade` را فراخوانی می‌کنید، یک اسمارت پوینتر از نوع `Weak` دریافت می‌کنید. به جای افزایش شمارش `strong_count` در نمونه `Rc` به مقدار 1، فراخوانی `Rc::downgrade` شمارش `weak_count` را به مقدار 1 افزایش می‌دهد. نوع `Rc` از `weak_count` برای پیگیری تعداد ارجاعات `Weak` موجود استفاده می‌کند، مشابه `strong_count`. تفاوت این است که شمارش `weak_count` نیازی به 0 بودن برای پاک‌سازی نمونه `Rc` ندارد. + +از آن‌جا که مقداری که یک `Weak` به آن اشاره می‌کند ممکن است پیش از این پاک شده باشد، برای انجام هر کاری با آن مقدار ابتدا باید مطمئن شوید که هنوز وجود دارد. برای این کار، متد `upgrade` را روی یک نمونه‌ی `Weak` فراخوانی می‌کنید؛ این متد یک `Option>` بازمی‌گرداند. اگر مقدار `Rc` هنوز پاک نشده باشد، نتیجه‌ی `Some` دریافت خواهید کرد؛ و اگر مقدار `Rc` قبلاً پاک شده باشد، نتیجه‌ی `None` خواهد بود. +از آن‌جا که `upgrade` یک `Option>` برمی‌گرداند، Rust شما را ملزم می‌کند که هر دو حالت `Some` و `None` را مدیریت کنید، و در نتیجه از بروز اشاره‌گر نامعتبر جلوگیری می‌شود. + +برای مثال، به جای استفاده از یک لیست که آیتم‌های آن فقط درباره آیتم بعدی اطلاع دارند، +ما یک درخت ایجاد خواهیم کرد که آیتم‌های آن درباره آیتم‌های فرزند _و_ والد خود اطلاع دارند. + +#### ایجاد یک ساختار داده‌ی درختی: یک `Node` با گره‌های فرزند (Child Nodes) + +برای شروع، ما یک درخت با گره‌هایی ایجاد خواهیم کرد که درباره گره‌های فرزند خود اطلاع +دارند. ما یک ساختار به نام `Node` ایجاد خواهیم کرد که مقدار `i32` خود را نگه می‌دارد +و همچنین به گره‌های فرزند خود ارجاع می‌دهد: Filename: src/main.rs @@ -155,17 +100,15 @@ references to its children `Node` values: {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-27/src/main.rs:here}} ``` -We want a `Node` to own its children, and we want to share that ownership with -variables so we can access each `Node` in the tree directly. To do this, we -define the `Vec` items to be values of type `Rc`. We also want to -modify which nodes are children of another node, so we have a `RefCell` in -`children` around the `Vec>`. +ما می‌خواهیم که یک `Node` مالک فرزندان خود باشد و همچنین می‌خواهیم که این مالکیت با +متغیرها به اشتراک گذاشته شود تا بتوانیم مستقیماً به هر `Node` در درخت دسترسی داشته +باشیم. برای انجام این کار، آیتم‌های `Vec` را به عنوان مقادیری از نوع `Rc` +تعریف می‌کنیم. همچنین می‌خواهیم تغییر دهیم که کدام گره‌ها فرزندان یک گره دیگر باشند، +بنابراین در `children` یک `RefCell` در اطراف `Vec>` قرار می‌دهیم. -Next, we’ll use our struct definition and create one `Node` instance named -`leaf` with the value `3` and no children, and another instance named `branch` -with the value `5` and `leaf` as one of its children, as shown in Listing 15-27. +در ادامه، از تعریف `struct` خود استفاده می‌کنیم و یک نمونه از `Node` با نام `leaf` ایجاد می‌کنیم که مقدار آن `3` است و هیچ فرزندی ندارد، و یک نمونه‌ی دیگر با نام `branch` می‌سازیم که مقدار آن `5` است و `leaf` را به‌عنوان یکی از فرزندان خود دارد؛ همان‌طور که در لیستینگ 15-27 نشان داده شده است. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-27/src/main.rs:there}} @@ -173,30 +116,20 @@ with the value `5` and `leaf` as one of its children, as shown in Listing 15-27. -We clone the `Rc` in `leaf` and store that in `branch`, meaning the -`Node` in `leaf` now has two owners: `leaf` and `branch`. We can get from -`branch` to `leaf` through `branch.children`, but there’s no way to get from -`leaf` to `branch`. The reason is that `leaf` has no reference to `branch` and -doesn’t know they’re related. We want `leaf` to know that `branch` is its -parent. We’ll do that next. +ما `Rc` را در `leaf` کلون می‌کنیم و آن را در `branch` ذخیره می‌کنیم، به این +معنی که `Node` در `leaf` اکنون دو مالک دارد: `leaf` و `branch`. ما می‌توانیم از +`branch` به `leaf` از طریق `branch.children` برسیم، اما هیچ راهی برای رفتن از +`leaf` به `branch` وجود ندارد. دلیل این است که `leaf` هیچ ارجاعی به `branch` +ندارد و نمی‌داند که آن‌ها مرتبط هستند. ما می‌خواهیم که `leaf` بداند که `branch` +والد آن است. این کار را در مرحله بعد انجام خواهیم داد. -#### Adding a Reference from a Child to Its Parent +#### افزودن یک ارجاع از فرزند به والد -To make the child node aware of its parent, we need to add a `parent` field to -our `Node` struct definition. The trouble is in deciding what the type of -`parent` should be. We know it can’t contain an `Rc`, because that would -create a reference cycle with `leaf.parent` pointing to `branch` and -`branch.children` pointing to `leaf`, which would cause their `strong_count` -values to never be 0. +برای آگاه کردن گره فرزند از والدش، باید یک فیلد `parent` به تعریف ساختار `Node` خود اضافه کنیم. مشکل در تصمیم‌گیری در مورد نوع `parent` است. می‌دانیم که نمی‌تواند شامل یک `Rc` باشد، زیرا این امر باعث ایجاد چرخه ارجاعی می‌شود که در آن `leaf.parent` به `branch` اشاره می‌کند و `branch.children` به `leaf`، که باعث می‌شود مقادیر `strong_count` آن‌ها هرگز به 0 نرسد. -Thinking about the relationships another way, a parent node should own its -children: if a parent node is dropped, its child nodes should be dropped as -well. However, a child should not own its parent: if we drop a child node, the -parent should still exist. This is a case for weak references! +با در نظر گرفتن روابط از دیدگاهی دیگر، یک گره والد باید مالک فرزندان خود باشد: اگر یک گره والد حذف شود، گره‌های فرزند آن نیز باید حذف شوند. اما، یک فرزند نباید مالک والدش باشد: اگر یک گره فرزند حذف شود، والد باید همچنان وجود داشته باشد. این مورد برای استفاده از ارجاعات ضعیف (weak references) مناسب است! -So instead of `Rc`, we’ll make the type of `parent` use `Weak`, -specifically a `RefCell>`. Now our `Node` struct definition looks -like this: +بنابراین، به جای `Rc`، نوع `parent` را از نوع `Weak` انتخاب می‌کنیم، به طور خاص یک `RefCell>`. اکنون تعریف ساختار `Node` ما به این شکل است: Filename: src/main.rs @@ -204,11 +137,10 @@ like this: {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-28/src/main.rs:here}} ``` -A node will be able to refer to its parent node but doesn’t own its parent. -In Listing 15-28, we update `main` to use this new definition so the `leaf` -node will have a way to refer to its parent, `branch`. +یک گره باید بتواند به گره والد خود اشاره کند، اما مالک آن نباشد. +در لیستینگ 15-28، تابع `main` را به‌روزرسانی می‌کنیم تا از این تعریف جدید استفاده کند و گره `leaf` بتواند به گره والد خود، یعنی `branch`، اشاره کند. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-28/src/main.rs:there}} @@ -216,31 +148,22 @@ node will have a way to refer to its parent, `branch`. -Creating the `leaf` node looks similar to Listing 15-27 with the exception of -the `parent` field: `leaf` starts out without a parent, so we create a new, -empty `Weak` reference instance. +ایجاد گره `leaf` مشابه فهرست 15-27 است با این تفاوت که فیلد `parent`: `leaf` ابتدا بدون والد شروع می‌شود، بنابراین یک نمونه جدید و خالی از ارجاع `Weak` ایجاد می‌کنیم. -At this point, when we try to get a reference to the parent of `leaf` by using -the `upgrade` method, we get a `None` value. We see this in the output from the -first `println!` statement: +در این مرحله، وقتی سعی می‌کنیم با استفاده از متد `upgrade` به والد گره `leaf` دسترسی پیدا کنیم، یک مقدار `None` دریافت می‌کنیم. این مورد را در خروجی اولین دستور `println!` مشاهده می‌کنیم: ```text leaf parent = None ``` -When we create the `branch` node, it will also have a new `Weak` -reference in the `parent` field because `branch` doesn’t have a parent node. -We still have `leaf` as one of the children of `branch`. Once we have the -`Node` instance in `branch`, we can modify `leaf` to give it a `Weak` -reference to its parent. We use the `borrow_mut` method on the -`RefCell>` in the `parent` field of `leaf`, and then we use the -`Rc::downgrade` function to create a `Weak` reference to `branch` from -the `Rc` in `branch`. +زمانی که گره `branch` را ایجاد می‌کنیم، این گره در فیلد `parent` خود دارای یک رفرنس جدید از نوع `Weak` خواهد بود، زیرا `branch` هیچ گره والد ندارد. همچنان `leaf` یکی از فرزندان `branch` است. پس از آن‌که نمونه‌ی `Node` را در `branch` ساختیم، می‌توانیم گره `leaf` را طوری تغییر دهیم که یک رفرنس `Weak` به والد خود داشته باشد. +برای این کار، از متد `borrow_mut` روی `RefCell>` موجود در فیلد `parent` در `leaf` استفاده می‌کنیم، و سپس با استفاده از تابع `Rc::downgrade` یک رفرنس `Weak` به `branch` می‌سازیم که از `Rc` موجود در `branch` ساخته شده است. -When we print the parent of `leaf` again, this time we’ll get a `Some` variant -holding `branch`: now `leaf` can access its parent! When we print `leaf`, we -also avoid the cycle that eventually ended in a stack overflow like we had in -Listing 15-26; the `Weak` references are printed as `(Weak)`: +وقتی والد گره `leaf` را دوباره چاپ می‌کنیم، این بار یک متغیر `Some` که `branch` را +نگه می‌دارد دریافت می‌کنیم: اکنون `leaf` می‌تواند به والد خود دسترسی پیدا کند! +هنگامی که `leaf` را چاپ می‌کنیم، همچنین از چرخه‌ای که نهایتاً به سرریز شدن استک +مانند فهرست 15-26 منجر می‌شد اجتناب می‌کنیم؛ ارجاعات `Weak` به‌صورت `(Weak)` +چاپ می‌شوند: ```text leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) }, @@ -248,19 +171,15 @@ children: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) }, children: RefCell { value: [] } }] } }) ``` -The lack of infinite output indicates that this code didn’t create a reference -cycle. We can also tell this by looking at the values we get from calling -`Rc::strong_count` and `Rc::weak_count`. +نبود خروجی بی‌نهایت نشان می‌دهد که این کد چرخه ارجاعی ایجاد نکرده است. همچنین می‌توانیم +این را با مشاهده مقادیری که از فراخوانی `Rc::strong_count` و `Rc::weak_count` +دریافت می‌کنیم، تأیید کنیم. -#### Visualizing Changes to `strong_count` and `weak_count` +#### تجسم تغییرات در `strong_count` و `weak_count` -Let’s look at how the `strong_count` and `weak_count` values of the `Rc` -instances change by creating a new inner scope and moving the creation of -`branch` into that scope. By doing so, we can see what happens when `branch` is -created and then dropped when it goes out of scope. The modifications are shown -in Listing 15-29. +بیایید بررسی کنیم که چگونه مقادیر `strong_count` و `weak_count` در نمونه‌های `Rc` تغییر می‌کنند؛ برای این کار، یک بلاک داخلی جدید ایجاد می‌کنیم و ساخت گره `branch` را به درون این بلاک منتقل می‌کنیم. با این کار می‌توانیم مشاهده کنیم که چه اتفاقی می‌افتد زمانی که `branch` ایجاد می‌شود و سپس پس از خارج شدن از حوزه‌ی دید (scope) حذف می‌گردد. این تغییرات در لیستینگ 15-29 نشان داده شده‌اند. -+ ```rust {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-29/src/main.rs:here}} @@ -268,52 +187,39 @@ in Listing 15-29. -After `leaf` is created, its `Rc` has a strong count of 1 and a weak -count of 0. In the inner scope, we create `branch` and associate it with -`leaf`, at which point when we print the counts, the `Rc` in `branch` -will have a strong count of 1 and a weak count of 1 (for `leaf.parent` pointing -to `branch` with a `Weak`). When we print the counts in `leaf`, we’ll see -it will have a strong count of 2 because `branch` now has a clone of the -`Rc` of `leaf` stored in `branch.children`, but will still have a weak -count of 0. - -When the inner scope ends, `branch` goes out of scope and the strong count of -the `Rc` decreases to 0, so its `Node` is dropped. The weak count of 1 -from `leaf.parent` has no bearing on whether or not `Node` is dropped, so we -don’t get any memory leaks! - -If we try to access the parent of `leaf` after the end of the scope, we’ll get -`None` again. At the end of the program, the `Rc` in `leaf` has a strong -count of 1 and a weak count of 0 because the variable `leaf` is now the only -reference to the `Rc` again. - -All of the logic that manages the counts and value dropping is built into -`Rc` and `Weak` and their implementations of the `Drop` trait. By -specifying that the relationship from a child to its parent should be a -`Weak` reference in the definition of `Node`, you’re able to have parent -nodes point to child nodes and vice versa without creating a reference cycle -and memory leaks. - -## Summary - -This chapter covered how to use smart pointers to make different guarantees and -trade-offs from those Rust makes by default with regular references. The -`Box` type has a known size and points to data allocated on the heap. The -`Rc` type keeps track of the number of references to data on the heap so -that data can have multiple owners. The `RefCell` type with its interior -mutability gives us a type that we can use when we need an immutable type but -need to change an inner value of that type; it also enforces the borrowing -rules at runtime instead of at compile time. - -Also discussed were the `Deref` and `Drop` traits, which enable a lot of the -functionality of smart pointers. We explored reference cycles that can cause -memory leaks and how to prevent them using `Weak`. - -If this chapter has piqued your interest and you want to implement your own -smart pointers, check out [“The Rustonomicon”][nomicon] for more useful -information. - -Next, we’ll talk about concurrency in Rust. You’ll even learn about a few new -smart pointers. +پس از ایجاد `leaf`، مقدار `strong_count` برای `Rc` آن برابر با ۱ و مقدار `weak_count` برابر با ۰ است. در بلاک داخلی، گره‌ی `branch` را ایجاد می‌کنیم و آن را به `leaf` مرتبط می‌سازیم؛ در این مرحله وقتی شمارنده‌ها را چاپ کنیم، `Rc` مربوط به `branch` دارای `strong_count` برابر با ۱ و `weak_count` برابر با ۱ خواهد بود (به‌خاطر اینکه `leaf.parent` به `branch` با یک `Weak` اشاره می‌کند). همچنین وقتی شمارنده‌ها را در `leaf` چاپ کنیم، مشاهده خواهیم کرد که `strong_count` آن برابر با ۲ است، زیرا `branch` اکنون یک کلون از `Rc` مربوط به `leaf` را در `branch.children` نگه می‌دارد، اما مقدار `weak_count` آن همچنان ۰ باقی می‌ماند. + +وقتی دامنه داخلی به پایان می‌رسد، `branch` از دامنه خارج می‌شود و شمارش قوی +`Rc` به 0 کاهش می‌یابد، بنابراین `Node` آن حذف می‌شود. شمارش ضعیف 1 از +`leaf.parent` تأثیری بر اینکه آیا `Node` حذف می‌شود ندارد، بنابراین هیچ نشت +حافظه‌ای نخواهیم داشت! + +اگر بعد از پایان بلاک (scope) تلاش کنیم به والد `leaf` دسترسی پیدا کنیم، دوباره مقدار `None` دریافت خواهیم کرد. در انتهای برنامه، مقدار `strong_count` برای `Rc` در `leaf` برابر با ۱ و مقدار `weak_count` برابر با ۰ خواهد بود، زیرا متغیر `leaf` اکنون تنها رفرنس به آن `Rc` است. + +تمام منطق مدیریت شمارش‌ها و حذف مقدار درون `Rc` و `Weak` و پیاده‌سازی‌های +ویژگی `Drop` آن‌ها تعبیه شده است. با مشخص کردن اینکه رابطه از یک فرزند به والد +آن باید یک ارجاع `Weak` باشد در تعریف `Node`، می‌توانید گره‌های والد را +به گره‌های فرزند و بالعکس ارجاع دهید بدون ایجاد یک چرخه ارجاعی و نشت حافظه. + +## خلاصه + +این فصل نحوه استفاده از اسمارت پوینترها برای ارائه تضمین‌ها و مبادلات متفاوت +از آنچه که راست به طور پیش‌فرض با ارجاع‌های معمولی ارائه می‌دهد را پوشش داد. +نوع `Box` دارای اندازه مشخصی است و به داده‌های تخصیص‌یافته در heap اشاره می‌کند. +نوع `Rc` تعداد ارجاع‌ها به داده‌ها در heap را پیگیری می‌کند تا داده‌ها بتوانند +چندین مالک داشته باشند. نوع `RefCell` با تغییرپذیری داخلی خود به ما نوعی +می‌دهد که می‌توانیم زمانی که به یک نوع غیرقابل‌تغییر نیاز داریم اما باید مقدار +درونی آن نوع را تغییر دهیم، استفاده کنیم؛ همچنین قوانین وام‌دهی را در زمان اجرا +به جای زمان کامپایل اعمال می‌کند. + +همچنین، ویژگی‌های `Deref` و `Drop` که بسیاری از قابلیت‌های اسمارت پوینترها را +ممکن می‌سازند، مورد بحث قرار گرفتند. ما چرخه‌های ارجاعی که می‌توانند باعث +نشت حافظه شوند و نحوه جلوگیری از آن‌ها با استفاده از `Weak` را بررسی کردیم. + +اگر این فصل علاقه شما را برانگیخته و می‌خواهید اسمارت پوینترهای خود را پیاده‌سازی +کنید، به [“The Rustonomicon”][nomicon] برای اطلاعات مفید بیشتر مراجعه کنید. + +در فصل بعدی، درباره همزمانی (concurrency) در راست صحبت خواهیم کرد. حتی با +چند اسمارت پوینتر جدید نیز آشنا خواهید شد. [nomicon]: ../nomicon/index.html diff --git a/src/ch16-00-concurrency.md b/src/ch16-00-concurrency.md index 8b5adcf61a..d3891e3376 100644 --- a/src/ch16-00-concurrency.md +++ b/src/ch16-00-concurrency.md @@ -1,49 +1,20 @@ -# Fearless Concurrency +# همزمانی بدون ترس -Handling concurrent programming safely and efficiently is another of Rust’s -major goals. _Concurrent programming_, in which different parts of a program -execute independently, and _parallel programming_, in which different parts of -a program execute at the same time, are becoming increasingly important as more -computers take advantage of their multiple processors. Historically, -programming in these contexts has been difficult and error prone. Rust hopes to -change that. +رسیدگی ایمن و کارآمد به برنامه‌نویسی هم‌زمان یکی دیگر از اهداف اصلی Rust است. *برنامه‌نویسی هم‌زمان* (concurrent programming)، که در آن بخش‌های مختلفی از یک برنامه به‌طور مستقل اجرا می‌شوند، و *برنامه‌نویسی موازی* (parallel programming)، که در آن بخش‌های مختلفی از برنامه به‌طور هم‌زمان اجرا می‌شوند، با توجه به استفاده‌ی روزافزون کامپیوترها از پردازنده‌های چند‌هسته‌ای، به‌طور فزاینده‌ای اهمیت یافته‌اند. به‌صورت تاریخی، برنامه‌نویسی در این زمینه‌ها دشوار و مستعد خطا بوده است. Rust امیدوار است این وضعیت را تغییر دهد. -Initially, the Rust team thought that ensuring memory safety and preventing -concurrency problems were two separate challenges to be solved with different -methods. Over time, the team discovered that the ownership and type systems are -a powerful set of tools to help manage memory safety _and_ concurrency -problems! By leveraging ownership and type checking, many concurrency errors -are compile-time errors in Rust rather than runtime errors. Therefore, rather -than making you spend lots of time trying to reproduce the exact circumstances -under which a runtime concurrency bug occurs, incorrect code will refuse to -compile and present an error explaining the problem. As a result, you can fix -your code while you’re working on it rather than potentially after it has been -shipped to production. We’ve nicknamed this aspect of Rust _fearless -concurrency_. Fearless concurrency allows you to write code that is free of -subtle bugs and is easy to refactor without introducing new bugs. +در ابتدا، تیم Rust گمان می‌کرد که اطمینان از ایمنی حافظه و جلوگیری از مشکلات هم‌زمانی دو چالش مجزا هستند که باید با روش‌های متفاوتی حل شوند. با گذشت زمان، این تیم دریافت که سیستم مالکیت و سیستم نوع‌ها در Rust مجموعه‌ای قدرتمند از ابزارها هستند که می‌توانند هم برای مدیریت ایمنی حافظه و هم برای حل مشکلات هم‌زمانی مفید باشند! با بهره‌گیری از مالکیت و بررسی نوع‌ها، بسیاری از خطاهای هم‌زمانی در Rust به جای آن‌که خطاهایی در زمان اجرا باشند، در زمان کامپایل شناسایی می‌شوند. بنابراین، به‌جای صرف زمان زیاد برای بازتولید شرایط دقیق بروز یک باگ هم‌زمانی در زمان اجرا، کد نادرست اصلاً کامپایل نخواهد شد و خطایی با توضیح مشکل به شما نمایش داده می‌شود. در نتیجه، شما می‌توانید کد خود را همان موقع که روی آن کار می‌کنید اصلاح کنید، نه احتمالاً پس از آن‌که به مرحله‌ی تولید رسیده است. ما این ویژگی Rust را با لقب *هم‌زمانی بی‌باکانه (fearless concurrency)* توصیف کرده‌ایم. هم‌زمانی بی‌باکانه به شما این امکان را می‌دهد که کدی بدون باگ‌های ظریف بنویسید و آن را بدون ایجاد باگ‌های جدید، به‌راحتی بازسازی (refactor) کنید. -> Note: For simplicity’s sake, we’ll refer to many of the problems as -> _concurrent_ rather than being more precise by saying _concurrent and/or -> parallel_. For this chapter, please mentally substitute _concurrent and/or -> parallel_ whenever we use _concurrent_. In the next chapter, where the -> distinction matters more, we’ll be more specific. +> توجه: برای سادگی، ما بسیاری از مشکلات را *هم‌زمانی* می‌نامیم، به‌جای آن‌که دقیق‌تر بگوییم *هم‌زمانی و/یا موازی بودن*. لطفاً در این فصل، هرجا واژه‌ی *هم‌زمانی* را دیدید، در ذهن خود آن را به *هم‌زمانی و/یا موازی بودن* تعبیر کنید. در فصل بعد، که این تمایز اهمیت بیشتری دارد، به‌طور دقیق‌تر به موضوع خواهیم پرداخت. -Many languages are dogmatic about the solutions they offer for handling -concurrent problems. For example, Erlang has elegant functionality for -message-passing concurrency but has only obscure ways to share state between -threads. Supporting only a subset of possible solutions is a reasonable -strategy for higher-level languages because a higher-level language promises -benefits from giving up some control to gain abstractions. However, lower-level -languages are expected to provide the solution with the best performance in any -given situation and have fewer abstractions over the hardware. Therefore, Rust -offers a variety of tools for modeling problems in whatever way is appropriate -for your situation and requirements. +بسیاری از زبان‌های برنامه‌نویسی در راه‌حل‌هایی که برای حل مشکلات هم‌زمانی ارائه می‌دهند، دچار تعصب‌اند. به‌عنوان مثال، زبان Erlang امکاناتی بسیار زیبا برای هم‌زمانی مبتنی بر ارسال پیام ارائه می‌دهد، اما روش‌های آن برای اشتراک‌گذاری وضعیت بین تردها بسیار پیچیده است. پشتیبانی صرفاً از یک زیرمجموعه از راه‌حل‌های ممکن، راهبردی منطقی برای زبان‌های سطح‌بالاست؛ چراکه این زبان‌ها با واگذاری بخشی از کنترل، در ازای انتزاع‌های بیشتر، مزایایی ارائه می‌دهند. با این حال، از زبان‌های سطح پایین انتظار می‌رود که در هر موقعیت، بهترین عملکرد را ارائه دهند و انتزاع‌های کمتری از سخت‌افزار داشته باشند. بنابراین، Rust مجموعه‌ای متنوع از ابزارها را برای مدل‌سازی مسائل در اختیار شما قرار می‌دهد تا متناسب با وضعیت و نیازمندی‌های شما، راه‌حل مناسب را انتخاب کنید. -Here are the topics we’ll cover in this chapter: +موضوعاتی که در این فصل پوشش خواهیم داد عبارت‌اند از: -- How to create threads to run multiple pieces of code at the same time -- _Message-passing_ concurrency, where channels send messages between threads -- _Shared-state_ concurrency, where multiple threads have access to some piece - of data -- The `Sync` and `Send` traits, which extend Rust’s concurrency guarantees to - user-defined types as well as types provided by the standard library +
+
    +
  • نحوه ایجاد نخ‌ها برای اجرای همزمان چندین بخش از کد
  • +
  • همزمانی پیام‌رسانی، جایی که کانال‌ها پیام‌ها را بین نخ‌ها ارسال می‌کنند
  • +
  • همزمانی حالت اشتراکی، جایی که چندین نخ به بخشی از داده دسترسی دارند
  • +
  • صفات Sync و Send، که تضمین‌های همزمانی Rust را به انواع تعریف‌شده توسط کاربر و همچنین انواع ارائه‌شده توسط کتابخانه استاندارد گسترش می‌دهند
  • +
+
diff --git a/src/ch16-01-threads.md b/src/ch16-01-threads.md index ea765439dd..cd4027d8dd 100644 --- a/src/ch16-01-threads.md +++ b/src/ch16-01-threads.md @@ -1,46 +1,24 @@ -## Using Threads to Run Code Simultaneously - -In most current operating systems, an executed program’s code is run in a -_process_, and the operating system will manage multiple processes at once. -Within a program, you can also have independent parts that run simultaneously. -The features that run these independent parts are called _threads_. For -example, a web server could have multiple threads so that it can respond to -more than one request at the same time. - -Splitting the computation in your program into multiple threads to run multiple -tasks at the same time can improve performance, but it also adds complexity. -Because threads can run simultaneously, there’s no inherent guarantee about the -order in which parts of your code on different threads will run. This can lead -to problems, such as: - -- Race conditions, in which threads are accessing data or resources in an - inconsistent order -- Deadlocks, in which two threads are waiting for each other, preventing both - threads from continuing -- Bugs that only happen in certain situations and are hard to reproduce and fix - reliably - -Rust attempts to mitigate the negative effects of using threads, but -programming in a multithreaded context still takes careful thought and requires -a code structure that is different from that in programs running in a single -thread. - -Programming languages implement threads in a few different ways, and many -operating systems provide an API the programming language can call for creating -new threads. The Rust standard library uses a _1:1_ model of thread -implementation, whereby a program uses one operating system thread per one -language thread. There are crates that implement other models of threading that -make different trade-offs to the 1:1 model. (Rust’s async system, which we will -see in the next chapter, provides another approach to concurrency as well.) - -### Creating a New Thread with `spawn` - -To create a new thread, we call the `thread::spawn` function and pass it a -closure (we talked about closures in Chapter 13) containing the code we want to -run in the new thread. The example in Listing 16-1 prints some text from a main -thread and other text from a new thread. - -+## استفاده از نخ‌ها برای اجرای همزمان کد + +در اغلب سیستم‌عامل‌های امروزی، کدی که در یک برنامه اجرا می‌شود در قالب یک _پروسه (process)_ اجرا می‌شود، و سیستم‌عامل به‌طور هم‌زمان چندین پروسه را مدیریت می‌کند. +در درون یک برنامه، می‌توان بخش‌های مستقلی نیز داشت که به‌صورت هم‌زمان اجرا می‌شوند. ویژگی‌هایی که این بخش‌های مستقل را اجرا می‌کنند، _ترد (thread)_ نام دارند. +برای مثال، یک وب‌سرور می‌تواند چندین ترد داشته باشد تا بتواند هم‌زمان به چندین درخواست پاسخ دهد. + +تقسیم محاسبات در برنامه شما به چندین نخ برای اجرای چندین کار به طور همزمان می‌تواند عملکرد را بهبود بخشد، اما همچنین پیچیدگی را افزایش می‌دهد. از آنجایی که نخ‌ها می‌توانند به طور همزمان اجرا شوند، هیچ تضمینی برای ترتیب اجرای بخش‌های کد در نخ‌های مختلف وجود ندارد. این موضوع می‌تواند به مشکلاتی منجر شود، مانند: + +- شرایط رقابتی (Race conditions)، زمانی که تردها به داده‌ها یا منابع به‌صورت نامنظم و ناسازگار دسترسی پیدا می‌کنند +- بن‌بست‌ها (Deadlocks)، زمانی که دو ترد منتظر یکدیگر هستند و هیچ‌کدام نمی‌توانند به اجرای خود ادامه دهند +- باگ‌هایی که تنها در شرایط خاصی رخ می‌دهند و بازتولید و رفع آن‌ها به‌صورت قابل‌اعتماد دشوار است + +Rust تلاش می‌کند اثرات منفی استفاده از نخ‌ها را کاهش دهد، اما برنامه‌نویسی در یک زمینه چندنخی همچنان نیاز به تفکر دقیق و ساختاری متفاوت از برنامه‌های تک‌نخی دارد. + +زبان‌های برنامه‌نویسی، پیاده‌سازی تردها را به روش‌های مختلفی انجام می‌دهند و بسیاری از سیستم‌عامل‌ها یک API برای ایجاد تردهای جدید در اختیار زبان برنامه‌نویسی قرار می‌دهند. کتابخانه استاندارد Rust از مدل پیاده‌سازی ترد **1:1** استفاده می‌کند؛ به‌عبارت دیگر، هر ترد زبان، متناظر با یک ترد سیستم‌عامل است. کتابخانه‌هایی (crate) نیز وجود دارند که مدل‌های دیگری از تردینگ را پیاده‌سازی می‌کنند و نسبت به مدل 1:1، مصالحه‌ها و ویژگی‌های متفاوتی دارند. (سیستم async در Rust، که در فصل بعدی آن را خواهیم دید، نیز رویکردی دیگر برای هم‌زمانی ارائه می‌دهد.) + +### ایجاد یک نخ جدید با `spawn` + +برای ایجاد یک ترد جدید، از تابع `thread::spawn` استفاده می‌کنیم و یک closure (که در فصل ۱۳ درباره آن صحبت کردیم) را به آن می‌دهیم که حاوی کدی است که می‌خواهیم در ترد جدید اجرا شود. مثال موجود در لیستینگ 16-1، متنی را از ترد اصلی چاپ می‌کند و متنی دیگر را از یک ترد جدید. + + ```rust {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-01/src/main.rs}} @@ -48,10 +26,7 @@ thread and other text from a new thread. -Note that when the main thread of a Rust program completes, all spawned threads -are shut down, whether or not they have finished running. The output from this -program might be a little different every time, but it will look similar to the -following: +توجه داشته باشید که وقتی نخ اصلی یک برنامه Rust تکمیل می‌شود، تمام نخ‌های ایجادشده متوقف می‌شوند، چه آن‌ها اجرای خود را تکمیل کرده باشند یا نه. خروجی این برنامه ممکن است هر بار کمی متفاوت باشد، اما به صورت مشابه زیر خواهد بود: +فراخوانی `join` روی handle نخ جاری را مسدود می‌کند تا زمانی که نخ نمایانده‌شده توسط handle خاتمه یابد. _مسدود کردن_ یک نخ به این معناست که آن نخ از انجام کار یا خروج جلوگیری می‌شود. چون فراخوانی `join` را بعد از حلقه `for` نخ اصلی قرار داده‌ایم، اجرای لیستینگ 16-2 باید خروجی مشابه زیر تولید کند: ```text hi number 1 from the main thread! @@ -130,11 +80,9 @@ hi number 8 from the spawned thread! hi number 9 from the spawned thread! ``` -The two threads continue alternating, but the main thread waits because of the -call to `handle.join()` and does not end until the spawned thread is finished. +دو نخ همچنان به صورت متناوب اجرا می‌شوند، اما نخ اصلی به دلیل فراخوانی `handle.join()` منتظر می‌ماند و تا زمانی که نخ ایجادشده تکمیل نشود پایان نمی‌یابد. -But let’s see what happens when we instead move `handle.join()` before the -`for` loop in `main`, like this: +اما بیایید ببینیم چه اتفاقی می‌افتد اگر `handle.join()` را قبل از حلقه `for` در `main` منتقل کنیم، به این صورت: @@ -144,8 +92,7 @@ But let’s see what happens when we instead move `handle.join()` before the -The main thread will wait for the spawned thread to finish and then run its -`for` loop, so the output won’t be interleaved anymore, as shown here: +نخ اصلی منتظر می‌ماند تا نخ ایجادشده خاتمه یابد و سپس حلقه `for` خود را اجرا می‌کند، بنابراین خروجی دیگر به صورت متناوب نخواهد بود، همان‌طور که در اینجا نشان داده شده است: in Chapter 13, we discussed `move` in the context of closures. Now we’ll -concentrate more on the interaction between `move` and `thread::spawn`. +ما اغلب از کلمه کلیدی `move` همراه با closuresهایی که به `thread::spawn` داده می‌شوند استفاده می‌کنیم، زیرا در این صورت closure مالکیت مقادیری که از محیط استفاده می‌کند را به خود می‌گیرد، و به این ترتیب مالکیت آن مقادیر از یک ترد به ترد دیگر منتقل می‌شود. در بخش [«گرفتن رفرنس یا انتقال مالکیت»][capture] در فصل 13، `move` را در زمینه‌ی closures بررسی کردیم. اکنون تمرکز بیشتری بر تعامل بین `move` و `thread::spawn` خواهیم داشت. -Notice in Listing 16-1 that the closure we pass to `thread::spawn` takes no -arguments: we’re not using any data from the main thread in the spawned -thread’s code. To use data from the main thread in the spawned thread, the -spawned thread’s closure must capture the values it needs. Listing 16-3 shows -an attempt to create a vector in the main thread and use it in the spawned -thread. However, this won’t work yet, as you’ll see in a moment. +در فهرست 16-1 توجه کنید که closureیی که به `thread::spawn` می‌دهیم هیچ آرگومانی نمی‌گیرد: ما در کد ترد ایجاد شده از هیچ داده‌ای از ترد اصلی استفاده نمی‌کنیم. برای استفاده از داده‌های ترد اصلی در ترد جدید، closure در ترد جدید باید مقادیری را که نیاز دارد capture کند. فهرست 16-3 تلاشی را برای ایجاد یک vector در ترد اصلی و استفاده از آن در ترد ایجاد شده نشان می‌دهد. با این حال، همان‌طور که در ادامه خواهید دید، این کد هنوز کار نخواهد کرد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-03/src/main.rs}} @@ -194,24 +130,17 @@ thread. However, this won’t work yet, as you’ll see in a moment. -The closure uses `v`, so it will capture `v` and make it part of the closure’s -environment. Because `thread::spawn` runs this closure in a new thread, we -should be able to access `v` inside that new thread. But when we compile this -example, we get the following error: +این closure از `v` استفاده می‌کند، بنابراین `v` را می‌گیرد و آن را بخشی از محیط closure می‌کند. از آنجا که `thread::spawn` این closure را در یک نخ جدید اجرا می‌کند، باید بتوانیم به `v` در داخل آن نخ جدید دسترسی داشته باشیم. اما وقتی این مثال را کامپایل می‌کنیم، خطای زیر را دریافت می‌کنیم: ```console {{#include ../listings/ch16-fearless-concurrency/listing-16-03/output.txt}} ``` -Rust _infers_ how to capture `v`, and because `println!` only needs a reference -to `v`, the closure tries to borrow `v`. However, there’s a problem: Rust can’t -tell how long the spawned thread will run, so it doesn’t know whether the -reference to `v` will always be valid. +زبان Rust به صورت خودکار *تشخیص* می‌دهد که چگونه باید مقدار `v` را capture کند، و از آن‌جا که `println!` تنها به یک رفرنس به `v` نیاز دارد، closure تلاش می‌کند تا `v` را قرض بگیرد (borrow کند). اما مشکلی وجود دارد: Rust نمی‌تواند تشخیص دهد که ترد ایجادشده چه مدت اجرا خواهد شد، بنابراین نمی‌داند که آیا رفرنس به `v` همیشه معتبر خواهد ماند یا نه. -Listing 16-4 provides a scenario that’s more likely to have a reference to `v` -that won’t be valid. +فهرست 16-4 سناریویی را نشان می‌دهد که احتمال نامعتبر بودن رفرنس به `v` در آن بیشتر است. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-04/src/main.rs}} @@ -219,15 +148,9 @@ that won’t be valid. -If Rust allowed us to run this code, there’s a possibility that the spawned -thread would be immediately put in the background without running at all. The -spawned thread has a reference to `v` inside, but the main thread immediately -drops `v`, using the `drop` function we discussed in Chapter 15. Then, when the -spawned thread starts to execute, `v` is no longer valid, so a reference to it -is also invalid. Oh no! +اگر Rust اجازه اجرای این کد را به ما می‌داد، این احتمال وجود داشت که ترد ایجادشده بلافاصله به پس‌زمینه منتقل شود بدون آن‌که اجرا شود. این ترد ایجادشده، یک رفرنس به `v` در درون خود دارد، اما ترد اصلی بلافاصله `v` را drop می‌کند، با استفاده از تابع `drop` که در فصل ۱۵ درباره‌اش صحبت کردیم. سپس، زمانی که ترد ایجادشده شروع به اجرا کند، دیگر `v` وجود ندارد، بنابراین رفرنسی که به آن اشاره دارد نیز نامعتبر خواهد بود. اوه نه! -To fix the compiler error in Listing 16-3, we can use the error message’s -advice: +برای رفع خطای کامپایل در لیستینگ 16-3، می‌توانیم از مشاوره پیام خطا استفاده کنیم: +وقتی کد موجود در لیستینگ 16-8 را اجرا کنیم، مقدار چاپ‌شده از نخ اصلی را خواهیم دید: ```text Got: hi ``` -Perfect! +عالی! -### Channels and Ownership Transference +### کانال‌ها و انتقال مالکیت -The ownership rules play a vital role in message sending because they help you -write safe, concurrent code. Preventing errors in concurrent programming is the -advantage of thinking about ownership throughout your Rust programs. Let’s do -an experiment to show how channels and ownership work together to prevent -problems: we’ll try to use a `val` value in the spawned thread _after_ we’ve -sent it down the channel. Try compiling the code in Listing 16-9 to see why -this code isn’t allowed. +قوانین مالکیت نقش حیاتی‌ای در ارسال پیام ایفا می‌کنند، چرا که به شما کمک می‌کنند تا کدی ایمن و هم‌زمان (concurrent) بنویسید. جلوگیری از بروز خطا در برنامه‌نویسی هم‌زمان یکی از مزایای تفکر بر مبنای مالکیت در سراسر برنامه‌های Rust است. بیایید یک آزمایش انجام دهیم تا ببینیم چگونه کانال‌ها و مالکیت با هم همکاری می‌کنند تا از بروز مشکل جلوگیری شود: در این آزمایش، سعی می‌کنیم از متغیر `val` در نخ ایجاد‌شده *بعد از* این‌که آن را از طریق کانال ارسال کرده‌ایم، استفاده کنیم. سعی کنید کدی که در فهرست 16-9 آمده را کامپایل کنید تا ببینید چرا این کد مجاز نیست. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-09/src/main.rs}} @@ -145,26 +75,17 @@ this code isn’t allowed. -Here, we try to print `val` after we’ve sent it down the channel via `tx.send`. -Allowing this would be a bad idea: once the value has been sent to another -thread, that thread could modify or drop it before we try to use the value -again. Potentially, the other thread’s modifications could cause errors or -unexpected results due to inconsistent or nonexistent data. However, Rust gives -us an error if we try to compile the code in Listing 16-9: +در اینجا، ما سعی می‌کنیم `val` را پس از ارسال آن از طریق `tx.send` چاپ کنیم. اجازه دادن به این کار ایده بدی خواهد بود: هنگامی که مقدار به نخ دیگری ارسال شده است، آن نخ می‌تواند قبل از اینکه سعی کنیم دوباره از مقدار استفاده کنیم، آن را تغییر دهد یا حذف کند. به طور بالقوه، تغییرات نخ دیگر می‌تواند باعث خطاها یا نتایج غیرمنتظره به دلیل داده‌های ناسازگار یا غیرموجود شود. با این حال، Rust اگر سعی کنیم کد موجود در لیستینگ 16-9 را کامپایل کنیم، به ما خطا می‌دهد: ```console {{#include ../listings/ch16-fearless-concurrency/listing-16-09/output.txt}} ``` -Our concurrency mistake has caused a compile-time error. The `send` function -takes ownership of its parameter, and when the value is moved the receiver -takes ownership of it. This stops us from accidentally using the value again -after sending it; the ownership system checks that everything is okay. +اشتباه ما در هم‌زمانی منجر به بروز یک خطای زمان کامپایل شده است. تابع `send` مالکیت پارامتر خود را می‌گیرد، و زمانی که مقدار منتقل می‌شود، دریافت‌کننده مالکیت آن را در اختیار می‌گیرد. این موضوع باعث می‌شود که به‌صورت تصادفی پس از ارسال، دوباره از آن مقدار استفاده نکنیم؛ سیستم مالکیت بررسی می‌کند که همه چیز در وضعیت درستی قرار دارد. -### Sending Multiple Values and Seeing the Receiver Waiting +### ارسال مقادیر متعدد و مشاهده انتظار گیرنده -The code in Listing 16-8 compiled and ran, but it didn’t clearly show us that -two separate threads were talking to each other over the channel. +کدی که در لیستینگ 16-8 آمده بود کامپایل و اجرا شد، اما به‌صورت واضح به ما نشان نداد که دو نخ مجزا از طریق یک channel با یکدیگر در حال ارتباط هستند. In Listing 16-10 we’ve made some modifications that will prove the code in Listing 16-8 is running concurrently: the spawned thread will now send multiple @@ -178,21 +99,14 @@ messages and pause for a second between each message. -This time, the spawned thread has a vector of strings that we want to send to -the main thread. We iterate over them, sending each individually, and pause -between each by calling the `thread::sleep` function with a `Duration` value of -one second. +این بار، نخی که ایجاد شده (spawned thread) یک وکتور از رشته‌ها (`vector of strings`) دارد که می‌خواهیم آن‌ها را به نخ اصلی ارسال کنیم. روی این رشته‌ها پیمایش می‌کنیم، هر کدام را به‌صورت جداگانه ارسال می‌کنیم، و بین ارسال هر کدام، با فراخوانی تابع `thread::sleep` و دادن یک مقدار `Duration` برابر با یک ثانیه، مکث می‌کنیم. -In the main thread, we’re not calling the `recv` function explicitly anymore: -instead, we’re treating `rx` as an iterator. For each value received, we’re -printing it. When the channel is closed, iteration will end. +در نخ اصلی، دیگر تابع `recv` را به طور صریح فراخوانی نمی‌کنیم: در عوض، با `rx` به عنوان یک تکرارگر رفتار می‌کنیم. برای هر مقداری که دریافت می‌شود، آن را چاپ می‌کنیم. هنگامی که کانال بسته می‌شود، تکرار متوقف خواهد شد. -When running the code in Listing 16-10, you should see the following output -with a one-second pause in between each line: +هنگام اجرای کد موجود در لیست 16-10، باید خروجی زیر را مشاهده کنید +با یک ثانیه توقف بین هر خط: - + ```text Got: hi @@ -201,18 +115,16 @@ Got: the Got: thread ``` -Because we don’t have any code that pauses or delays in the `for` loop in the -main thread, we can tell that the main thread is waiting to receive values from -the spawned thread. +از آنجا که هیچ کدی در حلقه `for` نخ اصلی نداریم که مکث یا تأخیری ایجاد کند، می‌توانیم بگوییم که نخ اصلی منتظر دریافت مقادیر از نخ ایجادشده است. -### Creating Multiple Producers by Cloning the Transmitter +### ایجاد تولیدکننده‌های متعدد با کلون کردن فرستنده -Earlier we mentioned that `mpsc` was an acronym for _multiple producer, -single consumer_. Let’s put `mpsc` to use and expand the code in Listing 16-10 -to create multiple threads that all send values to the same receiver. We can do -so by cloning the transmitter, as shown in Listing 16-11. +پیش‌تر اشاره کردیم که `mpsc` مخفف *چند تولیدکننده، یک مصرف‌کننده* است. +بیایید از `mpsc` استفاده کنیم و کد موجود در لیست 16-10 را گسترش دهیم تا چندین ترد ایجاد کنیم +که همگی مقادیر را به یک دریافت‌کننده ارسال می‌کنند. +برای این کار می‌توانیم فرستنده را clone کنیم، همان‌طور که در لیست 16-11 نشان داده شده است. -+ ```rust,noplayground {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-11/src/main.rs:here}} @@ -220,16 +132,9 @@ so by cloning the transmitter, as shown in Listing 16-11. -This time, before we create the first spawned thread, we call `clone` on the -transmitter. This will give us a new transmitter we can pass to the first -spawned thread. We pass the original transmitter to a second spawned thread. -This gives us two threads, each sending different messages to the one receiver. - -When you run the code, your output should look something like this: +این بار، قبل از اینکه نخ ایجادشده اول را ایجاد کنیم، روی فرستنده `clone` فراخوانی می‌کنیم. این کار به ما یک فرستنده جدید می‌دهد که می‌توانیم به نخ ایجادشده اول ارسال کنیم. فرستنده اصلی را به نخ ایجادشده دوم ارسال می‌کنیم. این کار به ما دو نخ می‌دهد که هر کدام پیام‌های مختلفی را به یک گیرنده ارسال می‌کنند. - +وقتی کد را اجرا می‌کنید، خروجی شما باید چیزی شبیه به این باشد: ```text Got: hi @@ -242,10 +147,6 @@ Got: thread Got: you ``` -You might see the values in another order, depending on your system. This is -what makes concurrency interesting as well as difficult. If you experiment with -`thread::sleep`, giving it various values in the different threads, each run -will be more nondeterministic and create different output each time. +ممکن است مقادیر را به ترتیب دیگری ببینید، بسته به سیستم شما. این همان چیزی است که همزمانی را هم جالب و هم دشوار می‌کند. اگر با `thread::sleep` آزمایش کنید و مقادیر مختلفی را در نخ‌های مختلف به آن بدهید، هر اجرا غیرقطعی‌تر خواهد شد و هر بار خروجی متفاوتی ایجاد می‌کند. -Now that we’ve looked at how channels work, let’s look at a different method of -concurrency. +اکنون که دیدیم کانال‌ها چگونه کار می‌کنند، بیایید به یک روش دیگر همزمانی نگاهی بیندازیم. diff --git a/src/ch16-03-shared-state.md b/src/ch16-03-shared-state.md index bd1a34da34..abbbde0a42 100644 --- a/src/ch16-03-shared-state.md +++ b/src/ch16-03-shared-state.md @@ -1,58 +1,39 @@ -## Shared-State Concurrency - -Message passing is a fine way to handle concurrency, but it’s not the only way. -Another method would be for multiple threads to access the same shared data. -Consider this part of the slogan from the Go language documentation again: “Do -not communicate by sharing memory.” - -What would communicating by sharing memory look like? In addition, why would -message-passing enthusiasts caution not to use memory sharing? - -In a way, channels in any programming language are similar to single ownership -because once you transfer a value down a channel, you should no longer use that -value. Shared-memory concurrency is like multiple ownership: multiple threads -can access the same memory location at the same time. As you saw in Chapter 15, -where smart pointers made multiple ownership possible, multiple ownership can -add complexity because these different owners need managing. Rust’s type system -and ownership rules greatly assist in getting this management correct. For an -example, let’s look at mutexes, one of the more common concurrency primitives -for shared memory. - -### Using Mutexes to Allow Access to Data from One Thread at a Time - -_Mutex_ is an abbreviation for _mutual exclusion_, as in a mutex allows only -one thread to access some data at any given time. To access the data in a -mutex, a thread must first signal that it wants access by asking to acquire the -mutex’s lock. The _lock_ is a data structure that is part of the mutex that -keeps track of who currently has exclusive access to the data. Therefore, the -mutex is described as _guarding_ the data it holds via the locking system. - -Mutexes have a reputation for being difficult to use because you have to -remember two rules: - -1. You must attempt to acquire the lock before using the data. -2. When you’re done with the data that the mutex guards, you must unlock the - data so other threads can acquire the lock. - -For a real-world metaphor for a mutex, imagine a panel discussion at a -conference with only one microphone. Before a panelist can speak, they have to -ask or signal that they want to use the microphone. When they get the -microphone, they can talk for as long as they want to and then hand the -microphone to the next panelist who requests to speak. If a panelist forgets to -hand the microphone off when they’re finished with it, no one else is able to -speak. If management of the shared microphone goes wrong, the panel won’t work -as planned! - -Management of mutexes can be incredibly tricky to get right, which is why so -many people are enthusiastic about channels. However, thanks to Rust’s type -system and ownership rules, you can’t get locking and unlocking wrong. - -#### The API of `Mutex` - -As an example of how to use a mutex, let’s start by using a mutex in a -single-threaded context, as shown in Listing 16-12. - -+## همزمانی با حالت مشترک (Shared-State Concurrency) + +ارسال پیام (Message passing) روش مناسبی برای مدیریت هم‌زمانی (concurrency) است، اما تنها روش موجود نیست. روش دیگری نیز وجود دارد که در آن چندین ترد (thread) به داده‌ی مشترک یکسانی دسترسی دارند. دوباره این بخش از شعار مستندات زبان Go را در نظر بگیرید: «با به‌اشتراک‌گذاری حافظه ارتباط برقرار نکنید.» + +ارتباط با به اشتراک‌گذاری حافظه چگونه خواهد بود؟ علاوه بر این، چرا علاقه‌مندان به +ارسال پیام هشدار می‌دهند که از اشتراک حافظه استفاده نکنید؟ + +به‌نوعی، کانال‌ها (channels) در هر زبان برنامه‌نویسی مشابه مالکیت تکی (single ownership) هستند، چرا که وقتی یک مقدار را از طریق کانال انتقال می‌دهید، دیگر نباید از آن مقدار استفاده کنید. هم‌زمانی با حافظه‌ی مشترک (shared-memory concurrency) شبیه به مالکیت چندگانه است: چندین ترد می‌توانند به‌طور هم‌زمان به یک محل حافظه دسترسی داشته باشند. همان‌طور که در فصل ۱۵ دیدید، جایی که smart pointerها امکان مالکیت چندگانه را فراهم کردند، مالکیت چندگانه می‌تواند پیچیدگی‌هایی را به همراه داشته باشد، چرا که این مالکان مختلف نیاز به مدیریت دارند. سیستم نوع‌دهی و قواعد مالکیت در Rust کمک شایانی به مدیریت درست این وضعیت می‌کنند. به عنوان یک مثال، بیایید به mutexها نگاه کنیم، که یکی از ابتدایی‌ترین سازوکارهای هم‌زمانی برای حافظه‌ی مشترک هستند. + +### استفاده از Mutex‌ها برای اجازه دسترسی به داده‌ها توسط یک نخ در هر زمان + +واژه‌ی *Mutex* مخفف *mutual exclusion* به‌معنای «ممانعت متقابل» است؛ به این معنا که یک mutex فقط به یک ترد اجازه می‌دهد تا در هر لحظه به داده‌ای دسترسی داشته باشد. برای دسترسی به داده درون یک mutex، یک ترد ابتدا باید اعلام کند که قصد دسترسی دارد، با درخواست قفل (lock) آن mutex. *Lock* یک ساختار داده‌ای است که بخشی از mutex به‌شمار می‌رود و مسئول پیگیری این است که در حال حاضر چه کسی به‌صورت انحصاری به داده دسترسی دارد. بنابراین، mutex به‌عنوان ابزاری توصیف می‌شود که از داده‌ای که در خود نگه می‌دارد از طریق سیستم قفل‌گذاری *محافظت* می‌کند. + +Mutex‌ها به دلیل این که باید دو قانون را به خاطر بسپارید، به سخت بودن شهرت دارند: + +1. پیش از استفاده از داده، باید تلاش کنید تا قفل (lock) آن را به‌دست آورید. +2. زمانی که کارتان با داده‌ای که mutex از آن محافظت می‌کند تمام شد، باید قفل را آزاد (unlock) کنید تا سایر تردها بتوانند قفل را به‌دست آورند. + +برای یک تمثیل دنیای واقعی برای mutex، یک بحث پانل در یک کنفرانس را تصور کنید +که فقط یک میکروفون وجود دارد. قبل از اینکه یک عضو پانل بتواند صحبت کند، باید +درخواست دهد یا سیگنال دهد که می‌خواهد از میکروفون استفاده کند. وقتی میکروفون را +می‌گیرد، می‌تواند هر چقدر که بخواهد صحبت کند و سپس میکروفون را به عضو بعدی +که درخواست صحبت کرده است بدهد. اگر یک عضو پانل فراموش کند که میکروفون را پس +دهد، هیچ کس دیگری نمی‌تواند صحبت کند. اگر مدیریت میکروفون مشترک اشتباه انجام +شود، پانل مطابق برنامه پیش نخواهد رفت! + +مدیریت mutex‌ها می‌تواند بسیار دشوار باشد، به همین دلیل است که بسیاری از افراد +به کانال‌ها علاقه‌مند هستند. اما به لطف سیستم نوعی و قوانین مالکیت راست، شما +نمی‌توانید در قفل کردن و باز کردن قفل اشتباه کنید. + +#### API `Mutex` + +به‌عنوان مثالی از نحوه استفاده از mutex، بیایید با استفاده از یک mutex در یک +زمینه تک‌ریسمانی شروع کنیم، همانطور که در فهرست 16-12 نشان داده شده است: + + ```rust {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-12/src/main.rs}} @@ -60,42 +41,21 @@ single-threaded context, as shown in Listing 16-12. -As with many types, we create a `Mutex` using the associated function `new`. -To access the data inside the mutex, we use the `lock` method to acquire the -lock. This call will block the current thread so it can’t do any work until -it’s our turn to have the lock. +همان‌طور که با بسیاری از نوع‌ها مشاهده می‌شود، یک `Mutex` را با استفاده از تابع وابسته `new` ایجاد می‌کنیم. برای دسترسی به داده داخل Mutex، از متد `lock` استفاده می‌کنیم تا قفل را به دست آوریم. این فراخوانی Thread فعلی را متوقف می‌کند، بنابراین نمی‌تواند کاری انجام دهد تا زمانی که نوبت ما برای گرفتن قفل برسد. -The call to `lock` would fail if another thread holding the lock panicked. In -that case, no one would ever be able to get the lock, so we’ve chosen to -`unwrap` and have this thread panic if we’re in that situation. +فراخوانی `lock` در صورتی که یک Thread دیگر که قفل را نگه داشته دچار وحشت (_panic_) شود، شکست می‌خورد. در چنین حالتی، هیچ‌کس دیگر نمی‌تواند قفل را به دست آورد، بنابراین انتخاب کرده‌ایم که از `unwrap` استفاده کنیم و اگر در چنین وضعیتی قرار گرفتیم، این Thread نیز دچار وحشت شود. -After we’ve acquired the lock, we can treat the return value, named `num` in -this case, as a mutable reference to the data inside. The type system ensures -that we acquire a lock before using the value in `m`. The type of `m` is -`Mutex`, not `i32`, so we _must_ call `lock` to be able to use the `i32` -value. We can’t forget; the type system won’t let us access the inner `i32` -otherwise. +بعد از گرفتن قفل، می‌توانیم مقدار بازگردانده‌شده را، که در اینجا به نام `num` است، به عنوان یک مرجع قابل تغییر به داده داخل در نظر بگیریم. سیستم نوع تضمین می‌کند که قبل از استفاده از مقدار داخل `m` قفل را به دست آوریم. نوع `m` برابر با `Mutex` است، نه `i32`، بنابراین _باید_ برای استفاده از مقدار `i32`، متد `lock` را فراخوانی کنیم. نمی‌توانیم فراموش کنیم؛ سیستم نوع اجازه دسترسی به مقدار داخلی `i32` را به ما نمی‌دهد. -The call to `lock` returns a type called `MutexGuard`, wrapped in a -`LockResult` that we handled with the call to `unwrap`. The `MutexGuard` type -implements `Deref` to point at our inner data; the type also has a `Drop` -implementation that releases the lock automatically when a `MutexGuard` goes -out of scope, which happens at the end of the inner scope. As a result, we -don’t risk forgetting to release the lock and blocking the mutex from being -used by other threads because the lock release happens automatically. +همان‌طور که احتمالاً حدس می‌زنید، `Mutex` یک اشاره‌گر هوشمند است. دقیق‌تر، فراخوانی `lock` یک اشاره‌گر هوشمند به نام `MutexGuard` را بازمی‌گرداند، که در یک `LockResult` بسته‌بندی شده است و آن را با فراخوانی `unwrap` مدیریت کردیم. اشاره‌گر هوشمند `MutexGuard` ویژگی `Deref` را پیاده‌سازی می‌کند تا به داده داخلی ما اشاره کند. همچنین، این اشاره‌گر هوشمند یک پیاده‌سازی از `Drop` دارد که به‌طور خودکار قفل را زمانی که یک `MutexGuard` از محدوده خارج می‌شود، آزاد می‌کند، که این اتفاق در انتهای محدوده داخلی رخ می‌دهد. در نتیجه، خطر فراموش کردن آزاد کردن قفل و جلوگیری از استفاده دیگر Threadها از Mutex وجود ندارد، زیرا آزادسازی قفل به صورت خودکار انجام می‌شود. -After dropping the lock, we can print the mutex value and see that we were able -to change the inner `i32` to `6`. +پس از آزاد کردن قفل، می‌توانیم مقدار Mutex را چاپ کنیم و ببینیم که توانستیم مقدار داخلی `i32` را به ۶ تغییر دهیم. -#### Sharing a `Mutex` Between Multiple Threads +#### اشتراک‌گذاری یک `Mutex` بین چندین Thread -Now let’s try to share a value between multiple threads using `Mutex`. We’ll -spin up 10 threads and have them each increment a counter value by 1, so the -counter goes from 0 to 10. The example in Listing 16-13 will have a compiler -error, and we’ll use that error to learn more about using `Mutex` and how -Rust helps us use it correctly. +حالا، بیایید تلاش کنیم یک مقدار را بین چندین Thread با استفاده از `Mutex` به اشتراک بگذاریم. ما ۱۰ Thread ایجاد خواهیم کرد و هرکدام مقدار شمارنده را ۱ واحد افزایش می‌دهند، بنابراین شمارنده از ۰ به ۱۰ می‌رسد. مثال بعدی در لیست ۱۶-۱۳ دارای خطای کامپایل خواهد بود، و از آن خطا برای یادگیری بیشتر در مورد استفاده از `Mutex` و اینکه چگونه Rust به ما کمک می‌کند از آن به درستی استفاده کنیم، استفاده خواهیم کرد. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-13/src/main.rs}} @@ -103,38 +63,24 @@ Rust helps us use it correctly. -We create a `counter` variable to hold an `i32` inside a `Mutex`, as we did -in Listing 16-12. Next, we create 10 threads by iterating over a range of -numbers. We use `thread::spawn` and give all the threads the same closure: one -that moves the counter into the thread, acquires a lock on the `Mutex` by -calling the `lock` method, and then adds 1 to the value in the mutex. When a -thread finishes running its closure, `num` will go out of scope and release the -lock so another thread can acquire it. +ما یک متغیر `counter` ایجاد می‌کنیم تا یک مقدار `i32` را در یک `Mutex` نگه دارد، همان‌طور که در لیست ۱۶-۱۲ انجام دادیم. سپس، با تکرار روی یک بازه عددی، ۱۰ Thread ایجاد می‌کنیم. از `thread::spawn` استفاده می‌کنیم و به تمام Threadها یک Closure یکسان می‌دهیم: یک Closure که متغیر `counter` را به Thread منتقل می‌کند، قفل `Mutex` را با فراخوانی متد `lock` به دست می‌آورد، و سپس ۱ واحد به مقدار داخل Mutex اضافه می‌کند. وقتی یک Thread اجرای Closure خود را تمام می‌کند، `num` از محدوده خارج شده و قفل را آزاد می‌کند تا Thread دیگری بتواند آن را به دست آورد. -In the main thread, we collect all the join handles. Then, as we did in Listing -16-2, we call `join` on each handle to make sure all the threads finish. At -that point, the main thread will acquire the lock and print the result of this -program. +در Thread اصلی، تمام handleهای join را جمع‌آوری می‌کنیم. سپس، همان‌طور که در لیست ۱۶-۲ انجام دادیم، متد `join` را روی هر handle فراخوانی می‌کنیم تا مطمئن شویم تمام Threadها تمام شده‌اند. در آن نقطه، Thread اصلی قفل را به دست می‌آورد و نتیجه این برنامه را چاپ می‌کند. -We hinted that this example wouldn’t compile. Now let’s find out why! + +ما اشاره کردیم که این مثال کامپایل نخواهد شد. حالا بیایید ببینیم چرا! ```console {{#include ../listings/ch16-fearless-concurrency/listing-16-13/output.txt}} ``` -The error message states that the `counter` value was moved in the previous -iteration of the loop. Rust is telling us that we can’t move the ownership of -lock `counter` into multiple threads. Let’s fix the compiler error with the -multiple-ownership method we discussed in Chapter 15. +پیام خطا نشان می‌دهد که مقدار `counter` در تکرار قبلی حلقه منتقل شده است. Rust به ما می‌گوید که نمی‌توانیم مالکیت `counter` را به چندین Thread منتقل کنیم. بیایید این خطای کامپایلر را با استفاده از روش مالکیت چندگانه که در فصل ۱۵ بحث کردیم، برطرف کنیم. -#### Multiple Ownership with Multiple Threads +#### مالکیت چندگانه با چندین Thread -In Chapter 15, we gave a value to multiple owners by using the smart pointer -`Rc` to create a reference counted value. Let’s do the same here and see -what happens. We’ll wrap the `Mutex` in `Rc` in Listing 16-14 and clone -the `Rc` before moving ownership to the thread. +در فصل ۱۵، ما با استفاده از اشاره‌گر هوشمند `Rc` برای ایجاد یک مقدار شمارش‌شده توسط مرجع (_reference-counted value_) به یک مقدار چندین مالک دادیم. بیایید همین کار را اینجا انجام دهیم و ببینیم چه اتفاقی می‌افتد. ما `Mutex` را در `Rc` بسته‌بندی می‌کنیم (همان‌طور که در لیست ۱۶-۱۴ نشان داده شده است) و قبل از انتقال مالکیت به Thread، `Rc` را کلون می‌کنیم. -+ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-14/src/main.rs}} @@ -142,51 +88,36 @@ the `Rc` before moving ownership to the thread. -Once again, we compile and get… different errors! The compiler is teaching us a -lot. +دوباره کامپایل می‌کنیم و... خطاهای متفاوتی دریافت می‌کنیم! کامپایلر چیزهای زیادی به ما یاد می‌دهد. + ```console {{#include ../listings/ch16-fearless-concurrency/listing-16-14/output.txt}} ``` -Wow, that error message is very wordy! Here’s the important part to focus on: -`` `Rc>` cannot be sent between threads safely ``. The compiler is -also telling us the reason why: `` the trait `Send` is not implemented for -`Rc>` ``. We’ll talk about `Send` in the next section: it’s one of -the traits that ensures the types we use with threads are meant for use in -concurrent situations. - -Unfortunately, `Rc` is not safe to share across threads. When `Rc` -manages the reference count, it adds to the count for each call to `clone` and -subtracts from the count when each clone is dropped. But it doesn’t use any -concurrency primitives to make sure that changes to the count can’t be -interrupted by another thread. This could lead to wrong counts—subtle bugs that -could in turn lead to memory leaks or a value being dropped before we’re done -with it. What we need is a type that is exactly like `Rc`, but that makes -changes to the reference count in a thread-safe way. - -#### Atomic Reference Counting with `Arc` - -Fortunately, `Arc` _is_ a type like `Rc` that is safe to use in -concurrent situations. The _a_ stands for _atomic_, meaning it’s an _atomically -reference-counted_ type. Atomics are an additional kind of concurrency -primitive that we won’t cover in detail here: see the standard library -documentation for [`std::sync::atomic`][atomic] for more -details. At this point, you just need to know that atomics work like primitive -types but are safe to share across threads. - -You might then wonder why all primitive types aren’t atomic and why standard -library types aren’t implemented to use `Arc` by default. The reason is that -thread safety comes with a performance penalty that you only want to pay when -you really need to. If you’re just performing operations on values within a -single thread, your code can run faster if it doesn’t have to enforce the -guarantees atomics provide. - -Let’s return to our example: `Arc` and `Rc` have the same API, so we fix -our program by changing the `use` line, the call to `new`, and the call to -`clone`. The code in Listing 16-15 will finally compile and run. - -+وای، این پیام خطا واقعاً پرحرف است! اما بخش مهمی که باید روی آن تمرکز کنیم این است: +```Rc>` cannot be sent between threads safely``` +کامپایلر همچنین دلیل آن را نیز به ما می‌گوید: +```the trait `Send` is not implemented for `Rc>```` +در بخش بعدی درباره‌ی `Send` صحبت خواهیم کرد: این یکی از `trait`هایی است که اطمینان حاصل می‌کند نوع‌هایی که با تردها استفاده می‌شوند، برای استفاده در موقعیت‌های هم‌زمان طراحی شده‌اند. + +متأسفانه، استفاده از `Rc` برای اشتراک‌گذاری داده‌ها بین تردها ایمن نیست. +زمانی که `Rc` شمارنده‌ی رفرنس را مدیریت می‌کند، با هر بار فراخوانی `clone` به شمارنده اضافه می‌شود و با از بین رفتن هر کلون، از شمارنده کم می‌شود. +اما این عملیات از هیچ سازوکار هم‌زمانی‌ای استفاده نمی‌کند تا مطمئن شود که تغییرات روی شمارنده در میان اجرای ترد دیگری قطع نشوند. +این موضوع می‌تواند منجر به شمارنده‌های اشتباه شود—باگ‌هایی ظریف که در ادامه ممکن است منجر به نشت حافظه یا از بین رفتن مقداری شوند در حالی که هنوز به آن نیاز داریم. +چیزی که ما نیاز داریم، نوعی است که دقیقاً مانند `Rc` عمل کند، اما تغییرات شمارنده‌ی رفرنس را به‌شکلی امن برای ترد انجام دهد. + + +#### شمارش ارجاع اتمی با `Arc` + +خوشبختانه، `Arc` یک نوع مشابه `Rc` است که برای استفاده در شرایط همزمان ایمن است. حرف _a_ در `Arc` مخفف _atomic_ است، به این معنا که یک نوع _شمارش مرجع اتمی_ است. اتمیک‌ها نوع دیگری از عناصر ابتدایی همزمانی هستند که در اینجا به‌طور مفصل به آن‌ها نمی‌پردازیم؛ برای جزئیات بیشتر به مستندات کتابخانه استاندارد در مورد [`std::sync::atomic`][atomic] مراجعه کنید. در این مرحله، فقط باید بدانید که اتمیک‌ها مانند نوع‌های ابتدایی کار می‌کنند اما برای اشتراک‌گذاری بین Threadها ایمن هستند. + +شاید از خود بپرسید چرا تمام نوع‌های ابتدایی اتمی نیستند و چرا نوع‌های کتابخانه استاندارد به‌طور پیش‌فرض از `Arc` استفاده نمی‌کنند. دلیل این است که ایمنی Thread با یک هزینه عملکردی همراه است که فقط زمانی که واقعاً نیاز باشد، می‌خواهید آن را پرداخت کنید. اگر فقط روی مقادیر در یک Thread واحد عملیات انجام می‌دهید، کد شما می‌تواند سریع‌تر اجرا شود اگر مجبور به اعمال تضمین‌های اتمیک نباشد. + +بیایید به مثال خود برگردیم: `Arc` و `Rc` API یکسانی دارند، بنابراین برنامه خود را با تغییر خط `use`، فراخوانی `new`، و فراخوانی `clone` اصلاح می‌کنیم. کد موجود در لیست ۱۶-۱۵ در نهایت کامپایل و اجرا می‌شود: + ++ ```rust {{#rustdoc_include ../listings/ch16-fearless-concurrency/listing-16-15/src/main.rs}} @@ -194,50 +125,29 @@ our program by changing the `use` line, the call to `new`, and the call to -This code will print the following: + +این کد خروجی زیر را چاپ خواهد کرد: + ```text Result: 10 ``` -We did it! We counted from 0 to 10, which may not seem very impressive, but it -did teach us a lot about `Mutex` and thread safety. You could also use this -program’s structure to do more complicated operations than just incrementing a -counter. Using this strategy, you can divide a calculation into independent -parts, split those parts across threads, and then use a `Mutex` to have each -thread update the final result with its part. - -Note that if you are doing simple numerical operations, there are types simpler -than `Mutex` types provided by the [`std::sync::atomic` module of the -standard library][atomic]. These types provide safe, concurrent, -atomic access to primitive types. We chose to use `Mutex` with a primitive -type for this example so we could concentrate on how `Mutex` works. - -### Similarities Between `RefCell`/`Rc` and `Mutex`/`Arc` - -You might have noticed that `counter` is immutable but we could get a mutable -reference to the value inside it; this means `Mutex` provides interior -mutability, as the `Cell` family does. In the same way we used `RefCell` in -Chapter 15 to allow us to mutate contents inside an `Rc`, we use `Mutex` -to mutate contents inside an `Arc`. - -Another detail to note is that Rust can’t protect you from all kinds of logic -errors when you use `Mutex`. Recall from Chapter 15 that using `Rc` came -with the risk of creating reference cycles, where two `Rc` values refer to -each other, causing memory leaks. Similarly, `Mutex` comes with the risk of -creating _deadlocks_. These occur when an operation needs to lock two resources -and two threads have each acquired one of the locks, causing them to wait for -each other forever. If you’re interested in deadlocks, try creating a Rust -program that has a deadlock; then research deadlock mitigation strategies for -mutexes in any language and have a go at implementing them in Rust. The -standard library API documentation for `Mutex` and `MutexGuard` offers -useful information. - -We’ll round out this chapter by talking about the `Send` and `Sync` traits and -how we can use them with custom types. - -[atomic]: ../std/sync/atomic/index.html +ما موفق شدیم! شمارنده را از ۰ به ۱۰ افزایش دادیم که ممکن است خیلی چشمگیر به نظر نرسد، اما چیزهای زیادی درباره `Mutex` و ایمنی Thread یاد گرفتیم. همچنین می‌توانید از ساختار این برنامه برای انجام عملیات پیچیده‌تری به‌جز افزایش یک شمارنده استفاده کنید. با استفاده از این استراتژی، می‌توانید یک محاسبه را به بخش‌های مستقل تقسیم کنید، این بخش‌ها را بین Threadها تقسیم کنید، و سپس از یک `Mutex` استفاده کنید تا هر Thread نتیجه نهایی را با بخش مربوط به خودش به‌روزرسانی کند. + +توجه داشته باشید که اگر در حال انجام عملیات عددی ساده هستید، نوع‌های ساده‌تری نسبت به `Mutex` در ماژول [`std::sync::atomic` از کتابخانه استاندارد][atomic] ارائه شده‌اند. این نوع‌ها دسترسی اتمی، ایمن و همزمان به نوع‌های ابتدایی فراهم می‌کنند. ما برای این مثال از `Mutex` با یک نوع ابتدایی استفاده کردیم تا بتوانیم بر نحوه کار `Mutex` تمرکز کنیم. + +### شباهت‌های بین `RefCell`/`Rc` و `Mutex`/`Arc` + +ممکن است متوجه شده باشید که `counter` تغییرناپذیر است، اما توانستیم یک مرجع قابل تغییر به مقدار داخل آن بگیریم؛ این بدان معناست که `Mutex` قابلیت تغییر داخلی (_interior mutability_) را فراهم می‌کند، همان‌طور که خانواده `Cell` این کار را می‌کنند. به همان شکلی که در فصل ۱۵ از `RefCell` برای اجازه تغییر محتوا درون یک `Rc` استفاده کردیم، از `Mutex` برای تغییر محتوا درون یک `Arc` استفاده می‌کنیم. + +نکته دیگری که باید توجه کنید این است که Rust نمی‌تواند شما را از تمام انواع خطاهای منطقی هنگام استفاده از `Mutex` محافظت کند. به یاد بیاورید که در فصل ۱۵ استفاده از `Rc` با خطر ایجاد چرخه‌های مرجع همراه بود، جایی که دو مقدار `Rc` به یکدیگر ارجاع می‌دادند و باعث نشت حافظه می‌شدند. به‌طور مشابه، `Mutex` با خطر ایجاد _بن‌بست_ (_deadlock_) همراه است. این وضعیت زمانی رخ می‌دهد که یک عملیات نیاز به قفل کردن دو منبع دارد و دو Thread هر کدام یکی از قفل‌ها را به دست آورده‌اند و باعث می‌شوند که برای همیشه منتظر یکدیگر بمانند. اگر به بن‌بست علاقه دارید، سعی کنید یک برنامه Rust ایجاد کنید که دچار بن‌بست شود؛ سپس استراتژی‌های کاهش بن‌بست برای Mutexها در هر زبانی را تحقیق کنید و آن‌ها را در Rust پیاده‌سازی کنید. مستندات API کتابخانه استاندارد برای `Mutex` و `MutexGuard` اطلاعات مفیدی ارائه می‌دهد. + +ما این فصل را با صحبت درباره ویژگی‌های `Send` و `Sync` و نحوه استفاده از آن‌ها با نوع‌های سفارشی تکمیل خواهیم کرد. + + +[atomic]: https://doc.rust-lang.org/std/sync/atomic/index.html diff --git a/src/ch16-04-extensible-concurrency-sync-and-send.md b/src/ch16-04-extensible-concurrency-sync-and-send.md index ee6bae88de..f0dc0c551b 100644 --- a/src/ch16-04-extensible-concurrency-sync-and-send.md +++ b/src/ch16-04-extensible-concurrency-sync-and-send.md @@ -1,93 +1,55 @@ -## Extensible Concurrency with the `Send` and `Sync` Traits +## هم‌روندی توسعه‌پذیر با `trait`‌های `Send` و `Sync` -Interestingly, almost every concurrency feature we’ve talked about so far in -this chapter has been part of the standard library, not the language. Your -options for handling concurrency are not limited to the language or the -standard library; you can write your own concurrency features or use those -written by others. - -However, among the key concurrency concepts that are embedded in the language -rather than the standard library are the `std::marker` traits `Send` and `Sync`. - -### Allowing Transference of Ownership Between Threads with `Send` - -The `Send` marker trait indicates that ownership of values of the type -implementing `Send` can be transferred between threads. Almost every Rust type -implements `Send`, but there are some exceptions, including `Rc`: this -cannot implement `Send` because if you cloned an `Rc` value and tried to -transfer ownership of the clone to another thread, both threads might update -the reference count at the same time. For this reason, `Rc` is implemented -for use in single-threaded situations where you don’t want to pay the -thread-safe performance penalty. - -Therefore, Rust’s type system and trait bounds ensure that you can never -accidentally send an `Rc` value across threads unsafely. When we tried to do -this in Listing 16-14, we got the error `` the trait `Send` is not implemented -for `Rc>` ``. When we switched to `Arc`, which does implement -`Send`, the code compiled. - -Any type composed entirely of `Send` types is automatically marked as `Send` as -well. Almost all primitive types are `Send`, aside from raw pointers, which -we’ll discuss in Chapter 20. - -### Allowing Access from Multiple Threads with `Sync` - -The `Sync` marker trait indicates that it is safe for the type implementing -`Sync` to be referenced from multiple threads. In other words, any type `T` -implements `Sync` if `&T` (an immutable reference to `T`) implements `Send`, -meaning the reference can be sent safely to another thread. Similar to `Send`, -primitive types all implement `Sync`, and types composed entirely of types that -implement `Sync` also implement `Sync`. - -The smart pointer `Rc` also doesn’t implement `Sync` for the same reasons -that it doesn’t implement `Send`. The `RefCell` type (which we talked about -in Chapter 15) and the family of related `Cell` types don’t implement -`Sync`. The implementation of borrow checking that `RefCell` does at runtime -is not thread-safe. The smart pointer `Mutex` implements `Sync` and can be -used to share access with multiple threads, as you saw in [“Sharing a -`Mutex` Between Multiple -Threads”][sharing-a-mutext-between-multiple-threads]. - -### Implementing `Send` and `Sync` Manually Is Unsafe - -Because types composed entirely of other types that implement the `Send` and -`Sync` traits also automatically implement `Send` and `Sync`, we don’t have to -implement those traits manually. As marker traits, they don’t even have any -methods to implement. They’re just useful for enforcing invariants related to -concurrency. - -Manually implementing these traits involves implementing unsafe Rust code. -We’ll talk about using unsafe Rust code in Chapter 20; for now, the important -information is that building new concurrent types not made up of `Send` and -`Sync` parts requires careful thought to uphold the safety guarantees. [“The -Rustonomicon”][nomicon] has more information about these guarantees and how to -uphold them. - -## Summary - -This isn’t the last you’ll see of concurrency in this book: the next chapter -focuses on async programming, and the project in Chapter 21 will use the -concepts in this chapter in a more realistic situation than the smaller -examples discussed here. - -As mentioned earlier, because very little of how Rust handles concurrency is -part of the language, many concurrency solutions are implemented as crates. -These evolve more quickly than the standard library, so be sure to search -online for the current, state-of-the-art crates to use in multithreaded -situations. - -The Rust standard library provides channels for message passing and smart -pointer types, such as `Mutex` and `Arc`, that are safe to use in -concurrent contexts. The type system and the borrow checker ensure that the -code using these solutions won’t end up with data races or invalid references. -Once you get your code to compile, you can rest assured that it will happily -run on multiple threads without the kinds of hard-to-track-down bugs common in -other languages. Concurrent programming is no longer a concept to be afraid of: -go forth and make your programs concurrent, fearlessly! +جالب است که تقریباً تمام ویژگی‌های هم‌روندی که تا این‌جای فصل درباره‌شان صحبت کردیم، بخشی از کتابخانه‌ی استاندارد بوده‌اند، نه زبان. گزینه‌های شما برای مدیریت هم‌روندی محدود به زبان یا کتابخانه‌ی استاندارد نیست؛ می‌توانید ویژگی‌های هم‌روندی خود را بنویسید یا از آن‌هایی استفاده کنید که دیگران نوشته‌اند. + +با این حال، در میان مفاهیم کلیدی هم‌روندی که در خود زبان (و نه در کتابخانه‌ی استاندارد) گنجانده شده‌اند، `trait`‌های `std::marker` یعنی `Send` و `Sync` قرار دارند. + +### اجازه انتقال مالکیت بین نخ‌ها با `Send` + +`trait` نشانه‌گذاری‌شده‌ی `Send` مشخص می‌کند که مالکیت مقادیر نوعی که این `trait` را پیاده‌سازی کرده، می‌تواند بین نخ‌ها منتقل شود. تقریباً همه‌ی نوع‌های Rust، `Send` را پیاده‌سازی می‌کنند، اما برخی استثناها نیز وجود دارند؛ از جمله `Rc`: این نوع نمی‌تواند `Send` را پیاده‌سازی کند، چرا که اگر یک مقدار `Rc` را clone کنید و بخواهید مالکیت آن را به نخ دیگری منتقل کنید، ممکن است هر دو نخ هم‌زمان شمارنده‌ی رفرنس را به‌روزرسانی کنند. به همین دلیل، `Rc` برای استفاده در موقعیت‌های تک‌نخی طراحی شده است، جایی که نمی‌خواهید هزینه‌ی عملکردی مرتبط با ایمنی نخ را بپردازید. + +بنابراین، سیستم نوع Rust و محدودیت‌های `trait` تضمین می‌کنند که هرگز به‌طور ناخواسته نتوانید یک مقدار `Rc` را به‌شکل ناایمن بین نخ‌ها منتقل کنید. زمانی که سعی کردیم این کار را در لیستینگ 16-14 انجام دهیم، خطایی دریافت کردیم با این مضمون که ``trait `Send` برای `Rc>` پیاده‌سازی نشده است``. اما زمانی که به `Arc` تغییر دادیم، که `Send` را پیاده‌سازی می‌کند، کد با موفقیت کامپایل شد. + +هر نوعی که به‌طور کامل از نوع‌های `Send` تشکیل شده باشد به‌طور خودکار به عنوان +`Send` علامت‌گذاری می‌شود. تقریباً تمام نوع‌های اولیه `Send` هستند، به جز +اشاره‌گر (Pointer)های خام، که در فصل 20 درباره آن‌ها صحبت خواهیم کرد. + +### اجازه دسترسی از چندین نخ با `Sync` + +`trait` نشانه‌گذاری‌شده‌ی `Sync` مشخص می‌کند که ارجاع دادن به نوعی که این `trait` را پیاده‌سازی کرده از چندین نخ به‌صورت هم‌زمان بی‌خطر است. به‌عبارت دیگر، هر نوعی `T` زمانی `Sync` را پیاده‌سازی می‌کند که `&T` (یک رفرنس غیرقابل تغییر به `T`) `Send` را پیاده‌سازی کرده باشد، یعنی این رفرنس می‌تواند با اطمینان به نخ دیگری ارسال شود. مشابه `Send`، تمام نوع‌های اولیه (`primitive types`) `Sync` را پیاده‌سازی می‌کنند، و نوع‌هایی که به‌طور کامل از نوع‌هایی تشکیل شده‌اند که خود `Sync` هستند، نیز به‌طور خودکار `Sync` را پیاده‌سازی می‌کنند. + +اشاره‌گر هوشمند `Rc` نیز برای همان دلایلی که `Send` را پیاده‌سازی نمی‌کند، `Sync` را نیز پیاده‌سازی نمی‌کند. نوع `RefCell` (که در فصل ۱۵ درباره‌ی آن صحبت کردیم) و خانواده‌ی نوع‌های مرتبط با `Cell` نیز `Sync` را پیاده‌سازی نمی‌کنند. پیاده‌سازی بررسی وام‌گیری (`borrow checking`) که `RefCell` در زمان اجرا انجام می‌دهد، برای نخ‌های مختلف ایمن نیست. اشاره‌گر هوشمند `Mutex` `Sync` را پیاده‌سازی می‌کند و می‌تواند برای اشتراک‌گذاری دسترسی بین چندین نخ استفاده شود، همان‌طور که در \[«اشتراک‌گذاری یک `Mutex` بین چند نخ»]\[sharing-a-mutext-between-multiple-threads] مشاهده کردید. + +### پیاده‌سازی دستی `Send` و `Sync` ناایمن است + +از آن‌جا که نوع‌هایی که به‌طور کامل از نوع‌های دیگری تشکیل شده‌اند که خودشان `Send` و `Sync` را پیاده‌سازی کرده‌اند، به‌صورت خودکار این دو `trait` را پیاده‌سازی می‌کنند، نیازی به پیاده‌سازی دستی آن‌ها نداریم. به‌عنوان `marker trait`‌ها، این `trait`‌ها حتی هیچ متدی برای پیاده‌سازی ندارند. آن‌ها فقط برای اعمال کردن محدودیت‌هایی مرتبط با هم‌زمانی مفید هستند. + +پیاده‌سازی دستی این ویژگی‌ها شامل پیاده‌سازی کد ناایمن در راست می‌شود. ما در فصل +20 درباره استفاده از کد ناایمن در راست صحبت خواهیم کرد؛ فعلاً، اطلاعات مهم این +است که ساخت نوع‌های همزمان جدید که از قسمت‌های `Send` و `Sync` تشکیل نشده‌اند +نیاز به دقت زیادی دارد تا اصول ایمنی رعایت شوند. [“The Rustonomicon”][nomicon] +اطلاعات بیشتری درباره این اصول و نحوه رعایت آن‌ها ارائه می‌دهد. + +## خلاصه + +این آخرین باری نیست که در این کتاب با هم‌زمانی (concurrency) روبه‌رو می‌شوید: فصل بعدی بر برنامه‌نویسی async تمرکز دارد و پروژه‌ی فصل ۲۱ مفاهیم این فصل را در یک موقعیت واقعی‌تر نسبت به مثال‌های کوچکی که در این‌جا بررسی شد به‌کار خواهد گرفت. + +همانطور که قبلاً اشاره شد، به دلیل اینکه بخش بسیار کمی از نحوه مدیریت همزمانی در راست +بخشی از زبان است، بسیاری از راه‌حل‌های همزمانی به‌عنوان crate پیاده‌سازی شده‌اند. +این‌ها سریع‌تر از کتابخانه استاندارد تکامل می‌یابند، بنابراین حتماً به صورت آنلاین جستجو +کنید تا crate‌های به‌روز و پیشرفته‌ای که برای موقعیت‌های چندریسمانی مناسب هستند را پیدا کنید. + +کتابخانه استاندارد راست کانال‌هایی برای ارسال پیام و انواع اسمارت پوینتر، مانند `Mutex` +و `Arc`، فراهم می‌کند که استفاده از آن‌ها در زمینه‌های همزمان ایمن است. سیستم نوعی +و کنترل‌کننده وام‌دهی تضمین می‌کنند که کدی که از این راه‌حل‌ها استفاده می‌کند با رقابت‌های +داده یا ارجاع‌های نامعتبر مواجه نمی‌شود. هنگامی که کد شما کامپایل شود، می‌توانید مطمئن +باشید که بدون آن دسته از اشکال‌های سخت‌ردیابی که در زبان‌های دیگر معمول است، به خوبی +روی چندین نخ اجرا خواهد شد. برنامه‌نویسی همزمان دیگر مفهومی برای ترسیدن نیست: +پیش بروید و برنامه‌های خود را بی‌باکانه همزمان کنید! [sharing-a-mutext-between-multiple-threads]: ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads [nomicon]: ../nomicon/index.html diff --git a/src/ch17-00-async-await.md b/src/ch17-00-async-await.md index 2598b10e43..98f17653d3 100644 --- a/src/ch17-00-async-await.md +++ b/src/ch17-00-async-await.md @@ -1,163 +1,76 @@ -# Fundamentals of Asynchronous Programming: Async, Await, Futures, and Streams - -Many operations we ask the computer to do can take a while to finish. It would -be nice if we could do something else while we are waiting for those -long-running processes to complete. Modern computers offer two techniques for -working on more than one operation at a time: parallelism and concurrency. Once -we start writing programs that involve parallel or concurrent operations, -though, we quickly encounter new challenges inherent to _asynchronous -programming_, where operations may not finish sequentially in the order they -were started. This chapter builds on Chapter 16’s use of threads for parallelism -and concurrency by introducing an alternative approach to asynchronous -programming: Rust’s Futures, Streams, the `async` and `await` syntax that -supports them, and the tools for managing and coordinating between asynchronous -operations. - -Let’s consider an example. Say you’re exporting a video you’ve created of a -family celebration, an operation that could take anywhere from minutes to hours. -The video export will use as much CPU and GPU power as it can. If you had only -one CPU core and your operating system didn’t pause that export until it -completed—that is, if it executed the export _synchronously_—you couldn’t do -anything else on your computer while that task was running. That would be a -pretty frustrating experience. Fortunately, your computer’s operating system -can, and does, invisibly interrupt the export often enough to let you get other -work done simultaneously. - -Now say you’re downloading a video shared by someone else, which can also take a -while but does not take up as much CPU time. In this case, the CPU has to wait -for data to arrive from the network. While you can start reading the data once -it starts to arrive, it might take some time for all of it to show up. Even once -the data is all present, if the video is quite large, it could take at least a -second or two to load it all. That might not sound like much, but it’s a very -long time for a modern processor, which can perform billions of operations every -second. Again, your operating system will invisibly interrupt your program to -allow the CPU to perform other work while waiting for the network call to -finish. - -The video export is an example of a _CPU-bound_ or _compute-bound_ operation. -It’s limited by the computer’s potential data processing speed within the CPU or -GPU, and how much of that speed it can dedicate to the operation. The video -download is an example of an _IO-bound_ operation, because it’s limited by the -speed of the computer’s _input and output_; it can only go as fast as the data -can be sent across the network. - -In both of these examples, the operating system’s invisible interrupts provide a -form of concurrency. That concurrency happens only at the level of the entire -program, though: the operating system interrupts one program to let other -programs get work done. In many cases, because we understand our programs at a -much more granular level than the operating system does, we can spot -opportunities for concurrency that the operating system can’t see. - -For example, if we’re building a tool to manage file downloads, we should be -able to write our program so that starting one download won’t lock up the UI, -and users should be able to start multiple downloads at the same time. Many -operating system APIs for interacting with the network are _blocking_, though; -that is, they block the program’s progress until the data they’re processing is -completely ready. - -> Note: This is how _most_ function calls work, if you think about it. However, -> the term _blocking_ is usually reserved for function calls that interact with -> files, the network, or other resources on the computer, because those are the -> cases where an individual program would benefit from the operation being -> _non_-blocking. - -We could avoid blocking our main thread by spawning a dedicated thread to -download each file. However, the overhead of those threads would eventually -become a problem. It would be preferable if the call didn’t block in the first -place. It would also be better if we could write in the same direct style we use -in blocking code, similar to this: +# اصول برنامه‌نویسی ناهمزمان: Async، Await، Futures، و Streams + +بسیاری از عملیات‌هایی که از کامپیوتر می‌خواهیم انجام دهد ممکن است مدتی طول بکشد تا کامل شوند. خوب می‌شد اگر می‌توانستیم در حالی که منتظر این فرآیندهای طولانی هستیم، کار دیگری انجام دهیم. کامپیوترهای مدرن دو تکنیک برای انجام هم‌زمان بیش از یک عملیات ارائه می‌دهند: _موازی‌سازی_ و _همزمانی_. اما وقتی شروع به نوشتن برنامه‌هایی می‌کنیم که شامل عملیات موازی یا همزمان هستند، به سرعت با چالش‌های جدیدی مواجه می‌شویم که در ذات _برنامه‌نویسی ناهمزمان_ هستند، جایی که عملیات‌ها ممکن است به ترتیب شروع‌شده تکمیل نشوند. این فصل بر اساس استفاده از Threadها برای موازی‌سازی و همزمانی که در فصل ۱۶ دیدیم، یک رویکرد جایگزین برای برنامه‌نویسی ناهمزمان معرفی می‌کند: Futures، Streams، سینتکس `async` و `await` در Rust، و ابزارهایی برای مدیریت و هماهنگی بین عملیات ناهمزمان. + +بیایید یک مثال را بررسی کنیم. فرض کنید در حال خروجی گرفتن از یک ویدئو هستید که از یک جشن خانوادگی ساخته‌اید؛ این عملیات ممکن است از چند دقیقه تا چند ساعت طول بکشد. خروجی ویدئو تا جایی که ممکن است از قدرت CPU و GPU استفاده خواهد کرد. اگر فقط یک هسته CPU داشتید و سیستم‌عامل شما آن خروجی را تا پایان تکمیل متوقف نمی‌کرد—یعنی اگر آن را به صورت _همزمان_ اجرا می‌کرد—در حالی که آن کار در حال اجرا بود نمی‌توانستید هیچ کار دیگری روی کامپیوتر خود انجام دهید. این تجربه بسیار ناامیدکننده‌ای می‌شد. خوشبختانه، سیستم‌عامل کامپیوتر شما می‌تواند و معمولاً هم می‌کند، به طور نامرئی خروجی را به اندازه کافی متوقف می‌کند تا بتوانید همزمان کارهای دیگری انجام دهید. + +حالا فرض کنید یک ویدئو که توسط شخص دیگری به اشتراک گذاشته شده است را دانلود می‌کنید، که این نیز ممکن است مدتی طول بکشد اما به اندازه خروجی گرفتن از CPU زمان نمی‌برد. در این حالت، CPU باید منتظر بماند تا داده از شبکه برسد. در حالی که می‌توانید داده را از زمانی که شروع به رسیدن می‌کند بخوانید، ممکن است مدتی طول بکشد تا همه آن برسد. حتی وقتی داده به طور کامل موجود باشد، اگر ویدئو خیلی بزرگ باشد، ممکن است حداقل یک یا دو ثانیه طول بکشد تا همه آن بارگذاری شود. شاید به نظر نرسد زمان زیادی باشد، اما برای یک پردازنده مدرن که می‌تواند میلیاردها عملیات را در هر ثانیه انجام دهد، این زمان بسیار طولانی است. باز هم، سیستم‌عامل برنامه شما را به طور نامرئی متوقف می‌کند تا CPU بتواند در حالی که منتظر تماس شبکه است، کارهای دیگری انجام دهد. + +خروجی ویدئو یک مثال از یک عملیات _وابسته به CPU_ یا _وابسته به محاسبه_ (_CPU-bound_) است. این عملیات محدود به سرعت پردازش داده کامپیوتر در CPU یا GPU و میزان توانایی آن برای اختصاص این سرعت به عملیات است. دانلود ویدئو یک مثال از یک عملیات _وابسته به ورودی و خروجی_ (_IO-bound_) است، زیرا محدود به سرعت _ورودی و خروجی_ کامپیوتر است؛ این عملیات فقط به سرعتی که داده می‌تواند از طریق شبکه ارسال شود، وابسته است. + +در هر دو این مثال‌ها، وقفه‌های نامرئی سیستم‌عامل نوعی همزمانی فراهم می‌کنند. با این حال، این همزمانی فقط در سطح کل برنامه اتفاق می‌افتد: سیستم‌عامل یک برنامه را متوقف می‌کند تا برنامه‌های دیگر بتوانند کار انجام دهند. در بسیاری از موارد، از آنجا که ما برنامه‌های خود را در سطح بسیار جزئی‌تری نسبت به سیستم‌عامل درک می‌کنیم، می‌توانیم فرصت‌هایی برای همزمانی پیدا کنیم که سیستم‌عامل نمی‌تواند ببیند. + +به عنوان مثال، اگر در حال ساخت یک ابزار برای مدیریت دانلود فایل‌ها هستید، باید بتوانید برنامه خود را طوری بنویسید که شروع یک دانلود، رابط کاربری را قفل نکند، و کاربران بتوانند به طور همزمان چندین دانلود را آغاز کنند. بسیاری از APIهای سیستم‌عامل برای تعامل با شبکه _مسدودکننده_ (_blocking_) هستند؛ یعنی پیشرفت برنامه را تا زمانی که داده‌ای که پردازش می‌کنند کاملاً آماده باشد، متوقف می‌کنند. + +> نکته: این همان چیزی است که _بیشتر_ فراخوانی‌های توابع انجام می‌دهند، اگر در مورد آن فکر کنید. با این حال، اصطلاح _blocking_ معمولاً برای فراخوانی توابعی که با فایل‌ها، شبکه یا منابع دیگر روی کامپیوتر تعامل دارند استفاده می‌شود، زیرا این مواردی هستند که یک برنامه فردی می‌تواند از غیرمسدودکننده (_non-blocking_) بودن عملیات بهره‌مند شود. + +ما می‌توانیم با ایجاد یک Thread اختصاصی برای دانلود هر فایل، از مسدود شدن Thread اصلی جلوگیری کنیم. با این حال، سربار آن Threadها در نهایت به مشکل تبدیل خواهد شد. بهتر است که فراخوانی از ابتدا مسدودکننده نباشد. همچنین بهتر است که بتوانیم به همان سبک مستقیم کدی که در کد مسدودکننده استفاده می‌کنیم، بنویسیم، شبیه به این: ```rust,ignore,does_not_compile let data = fetch_data_from(url).await; println!("{data}"); ``` -That is exactly what Rust’s _async_ (short for _asynchronous_) abstraction gives -us. In this chapter, you’ll learn all about async as we cover the following -topics: +این دقیقاً همان چیزی است که انتزاع _async_ (مخفف _asynchronous_) در Rust به ما می‌دهد. در این فصل، همه چیز درباره async را یاد خواهید گرفت و موضوعات زیر را پوشش خواهیم داد: -- How to use Rust’s `async` and `await` syntax -- How to use the async model to solve some of the same challenges we looked at - in Chapter 16 -- How multithreading and async provide complementary solutions, that you can - combine in many cases +- نحوه استفاده از سینتکس `async` و `await` در Rust +- نحوه استفاده از مدل async برای حل برخی از چالش‌هایی که در فصل ۱۶ بررسی کردیم +- چگونگی ارائه راه‌حل‌های مکمل توسط multithreading و async، که در بسیاری از موارد می‌توانید آن‌ها را با هم ترکیب کنید -Before we see how async works in practice, though, we need to take a short -detour to discuss the differences between parallelism and concurrency. +با این حال، قبل از اینکه ببینیم async در عمل چگونه کار می‌کند، باید یک توقف کوتاه برای بحث درباره تفاوت‌های بین موازی‌سازی و همزمانی داشته باشیم. -### Parallelism and Concurrency +### تفاوت بین موازی‌سازی و همزمانی -We’ve treated parallelism and concurrency as mostly interchangeable so far. Now -we need to distinguish between them more precisely, because the differences will -show up as we start working. +ما تاکنون همزمانی (_concurrency_) و موازی‌سازی (_parallelism_) را تقریباً به جای هم در نظر گرفته‌ایم. اکنون باید آن‌ها را به طور دقیق‌تر از هم متمایز کنیم، زیرا تفاوت‌هایشان در هنگام کار مشخص خواهد شد. -Consider the different ways a team could split up work on a software project. -You could assign a single member multiple tasks, assign each member one task, or -use a mix of the two approaches. +به روش‌های مختلفی که یک تیم می‌تواند کار بر روی یک پروژه نرم‌افزاری را تقسیم کند فکر کنید. می‌توانید چندین وظیفه را به یک عضو اختصاص دهید، به هر عضو یک وظیفه اختصاص دهید، یا ترکیبی از این دو روش را استفاده کنید. -When an individual works on several different tasks before any of them is -complete, this is _concurrency_. Maybe you have two different projects checked -out on your computer, and when you get bored or stuck on one project, you switch -to the other. You’re just one person, so you can’t make progress on both tasks -at the exact same time, but you can multi-task, making progress on one at a time -by switching between them (see Figure 17-1). +وقتی یک فرد روی چندین وظیفه مختلف قبل از اتمام هر یک از آن‌ها کار می‌کند، این _همزمانی_ است. شاید شما دو پروژه مختلف را روی کامپیوتر خود باز کرده‌اید و وقتی از یکی خسته یا در آن گیر کردید، به دیگری تغییر می‌دهید. شما فقط یک نفر هستید، بنابراین نمی‌توانید به طور همزمان روی هر دو وظیفه پیشرفت کنید، اما می‌توانید چندوظیفه‌ای (_multi-tasking_) کنید و با جابه‌جا شدن بین آن‌ها، یکی یکی پیشرفت کنید (نگاه کنید به شکل ۱۷-۱).
A diagram with boxes labeled Task A and Task B, with diamonds in them representing subtasks. There are arrows pointing from A1 to B1, B1 to A2, A2 to B2, B2 to A3, A3 to A4, and A4 to B3. The arrows between the subtasks cross the boxes between Task A and Task B. -
Figure 17-1: A concurrent workflow, switching between Task A and Task B
+
شکل ۱۷-۱: یک جریان کاری همزمان، که بین وظیفه A و وظیفه B جابه‌جا می‌شود.
-When the team splits up a group of tasks by having each member take one task and -work on it alone, this is _parallelism_. Each person on the team can make -progress at the exact same time (see Figure 17-2). +وقتی تیم گروهی از وظایف را به این صورت تقسیم می‌کند که هر عضو یک وظیفه را بر عهده می‌گیرد و به تنهایی روی آن کار می‌کند، این _موازی‌سازی_ است. هر فرد در تیم می‌تواند دقیقاً به طور همزمان پیشرفت کند (نگاه کنید به شکل ۱۷-۲).
-A diagram with boxes labeled Task A and Task B, with diamonds in them representing subtasks. There are arrows pointing from A1 to A2, A2 to A3, A3 to A4, B1 to B2, and B2 to B3. No arrows cross between the boxes for Task A and Task B. +یک نمودار با جعبه‌هایی که با برچسب‌های وظیفه A و وظیفه B نام‌گذاری شده‌اند، و لوزی‌هایی درون آن‌ها که نمایانگر زیروظایف هستند. فلش‌هایی از A1 به A2، A2 به A3، A3 به A4، B1 به B2، و B2 به B3 اشاره می‌کنند. هیچ فلشی بین جعبه‌های وظیفه A و وظیفه B عبور نمی‌کند. -
Figure 17-2: A parallel workflow, where work happens on Task A and Task B independently
+
شکل ۱۷-۲: یک جریان کاری موازی، که در آن کار روی وظیفه A و وظیفه B به طور مستقل انجام می‌شود.
-In both of these workflows, you might have to coordinate between different -tasks. Maybe you _thought_ the task assigned to one person was totally -independent from everyone else’s work, but it actually requires another person -on the team to finish their task first. Some of the work could be done in -parallel, but some of it was actually _serial_: it could only happen in a -series, one task after the other, as in Figure 17-3. +در هر دو این جریان‌های کاری، ممکن است نیاز به هماهنگی بین وظایف مختلف داشته باشید. شاید _فکر_ می‌کردید وظیفه‌ای که به یک نفر اختصاص داده شده کاملاً مستقل از کار سایر اعضای تیم است، اما در واقع نیاز دارد که یک نفر دیگر در تیم ابتدا وظیفه خود را به پایان برساند. بخشی از کار می‌تواند به صورت موازی انجام شود، اما بخشی از آن در واقع _سریالی_ است: فقط می‌تواند به صورت متوالی انجام شود، یک وظیفه پس از دیگری، همان‌طور که در شکل ۱۷-۳ نشان داده شده است.
-A diagram with boxes labeled Task A and Task B, with diamonds in them representing subtasks. There are arrows pointing from A1 to A2, A2 to a pair of thick vertical lines like a “pause” symbol, from that symbol to A3, B1 to B2, B2 to B3, which is below that symbol, B3 to A3, and B3 to B4. +یک نمودار با جعبه‌هایی که با برچسب وظیفه A و وظیفه B نام‌گذاری شده‌اند، و لوزی‌هایی درون آن‌ها که نمایانگر زیروظایف هستند. فلش‌هایی از A1 به A2، A2 به یک جفت خطوط عمودی ضخیم مانند نماد 'توقف'، از آن نماد به A3، B1 به B2، B2 به B3 (که در زیر آن نماد قرار دارد)، B3 به A3، و B3 به B4 اشاره می‌کنند. -
Figure 17-3: A partially parallel workflow, where work happens on Task A and Task B independently until Task A3 is blocked on the results of Task B3.
+
شکل ۱۷-۳: یک جریان کاری نیمه موازی، که در آن کار روی وظیفه A و وظیفه B به طور مستقل انجام می‌شود تا زمانی که A3 به نتایج B3 وابسته باشد.
-Likewise, you might realize that one of your own tasks depends on another of -your tasks. Now your concurrent work has also become serial. - -Parallelism and concurrency can intersect with each other, too. If you learn -that a colleague is stuck until you finish one of your tasks, you’ll probably -focus all your efforts on that task to “unblock” your colleague. You and your -coworker are no longer able to work in parallel, and you’re also no longer able -to work concurrently on your own tasks. - -The same basic dynamics come into play with software and hardware. On a machine -with a single CPU core, the CPU can perform only one operation at a time, but it -can still work concurrently. Using tools such as threads, processes, and async, -the computer can pause one activity and switch to others before eventually -cycling back to that first activity again. On a machine with multiple CPU cores, -it can also do work in parallel. One core can be performing one task while -another core performs a completely unrelated one, and those operations actually -happen at the same time. - -When working with async in Rust, we’re always dealing with concurrency. -Depending on the hardware, the operating system, and the async runtime we are -using (more on async runtimes shortly), that concurrency may also use parallelism -under the hood. - -Now, let’s dive into how async programming in Rust actually works. +به همین ترتیب، ممکن است متوجه شوید که یکی از وظایف شما به وظیفه دیگری از کارهای شما بستگی دارد. اکنون کار همزمان شما نیز سریالی شده است. + +موازی‌سازی و همزمانی می‌توانند با یکدیگر تقاطع داشته باشند. اگر متوجه شوید که یک همکار تا زمانی که یکی از وظایف شما به پایان نرسیده گیر کرده است، احتمالاً تمام تلاش خود را روی آن وظیفه متمرکز می‌کنید تا "همکارتان را از بن‌بست خارج کنید." شما و همکارتان دیگر نمی‌توانید به صورت موازی کار کنید، و همچنین دیگر نمی‌توانید به صورت همزمان روی وظایف خودتان کار کنید. + +همان دینامیک‌های اساسی در نرم‌افزار و سخت‌افزار نیز وجود دارند. روی ماشینی با یک هسته CPU، CPU فقط می‌تواند یک عملیات را در هر لحظه انجام دهد، اما همچنان می‌تواند به صورت همزمان کار کند. با استفاده از ابزارهایی مانند Threads، فرآیندها (_processes_) و async، کامپیوتر می‌تواند یک فعالیت را متوقف کند و به فعالیت‌های دیگر تغییر دهد، و در نهایت دوباره به فعالیت اول بازگردد. روی ماشینی با چندین هسته CPU، می‌تواند کارها را به صورت موازی نیز انجام دهد. یک هسته می‌تواند یک وظیفه را اجرا کند در حالی که هسته دیگری وظیفه‌ای کاملاً نامرتبط را اجرا می‌کند، و این عملیات‌ها واقعاً در یک زمان اتفاق می‌افتند. + +هنگام کار با async در Rust، همیشه با همزمانی سر و کار داریم. بسته به سخت‌افزار، سیستم‌عامل، و Runtime async که استفاده می‌کنیم (که در ادامه درباره Runtimeهای async بیشتر صحبت خواهیم کرد)، این همزمانی ممکن است در پس‌زمینه از موازی‌سازی نیز استفاده کند. + +حالا بیایید به این بپردازیم که برنامه‌نویسی async در Rust در عمل چگونه کار می‌کند. diff --git a/src/ch17-01-futures-and-syntax.md b/src/ch17-01-futures-and-syntax.md index 5263ce1779..4dcd2954fd 100644 --- a/src/ch17-01-futures-and-syntax.md +++ b/src/ch17-01-futures-and-syntax.md @@ -1,66 +1,24 @@ -## Futures and the Async Syntax - -The key elements of asynchronous programming in Rust are _futures_ and Rust’s -`async` and `await` keywords. - -A _future_ is a value that may not be ready now but will become ready at some -point in the future. (This same concept shows up in many languages, sometimes -under other names such as _task_ or _promise_.) Rust provides a `Future` trait -as a building block so that different async operations can be implemented with -different data structures but with a common interface. In Rust, futures are -types that implement the `Future` trait. Each future holds its own information -about the progress that has been made and what "ready" means. - -You can apply the `async` keyword to blocks and functions to specify that they -can be interrupted and resumed. Within an async block or async function, you can -use the `await` keyword to _await a future_ (that is, wait for it to become -ready). Any point where you await a future within an async block or function is -a potential spot for that async block or function to pause and resume. The -process of checking with a future to see if its value is available yet is called -_polling_. - -Some other languages, such as C# and JavaScript, also use `async` and `await` -keywords for async programming. If you’re familiar with those languages, you may -notice some significant differences in how Rust does things, including how it -handles the syntax. That’s for good reason, as we’ll see! - -When writing async Rust, we use the `async` and `await` keywords most of the -time. Rust compiles them into equivalent code using the `Future` trait, much as -it compiles `for` loops into equivalent code using the `Iterator` trait. Because -Rust provides the `Future` trait, though, you can also implement it for your own -data types when you need to. Many of the functions we’ll see throughout this -chapter return types with their own implementations of `Future`. We’ll return to -the definition of the trait at the end of the chapter and dig into more of how -it works, but this is enough detail to keep us moving forward. - -This may all feel a bit abstract, so let’s write our first async program: a -little web scraper. We’ll pass in two URLs from the command line, fetch both of -them concurrently, and return the result of whichever one finishes first. This -example will have a fair bit of new syntax, but don’t worry—we’ll explain -everything you need to know as we go. - -## Our First Async Program - -To keep the focus of this chapter on learning async rather than juggling parts -of the ecosystem, we’ve created the `trpl` crate (`trpl` is short for “The Rust -Programming Language”). It re-exports all the types, traits, and functions -you’ll need, primarily from the [`futures`][futures-crate] and -[`tokio`][tokio] crates. The `futures` crate is an official home -for Rust experimentation for async code, and it’s actually where the `Future` -trait was originally designed. Tokio is the most widely used async runtime in -Rust today, especially for web applications. There are other great runtimes out -there, and they may be more suitable for your purposes. We use the `tokio` crate -under the hood for `trpl` because it’s well tested and widely used. - -In some cases, `trpl` also renames or wraps the original APIs to keep you -focused on the details relevant to this chapter. If you want to understand what -the crate does, we encourage you to check out [its source -code][crate-source]. You’ll be able to see what crate each -re-export comes from, and we’ve left extensive comments explaining what the -crate does. - -Create a new binary project named `hello-async` and add the `trpl` crate as a -dependency: +## Futures و سینتکس Async + +عناصر کلیدی برنامه‌نویسی ناهمزمان در Rust شامل _futures_ و کلمات کلیدی `async` و `await` هستند. + +یک _future_ مقداری است که ممکن است اکنون آماده نباشد، اما در آینده در نقطه‌ای آماده خواهد شد. (این مفهوم در بسیاری از زبان‌ها وجود دارد، گاهی با نام‌های دیگر مانند _task_ یا _promise_.) Rust یک ویژگی `Future` به عنوان یک بلوک سازنده فراهم می‌کند تا عملیات‌های async مختلف با ساختارهای داده متفاوت اما با یک رابط مشترک پیاده‌سازی شوند. در Rust، futures نوع‌هایی هستند که ویژگی `Future` را پیاده‌سازی می‌کنند. هر future اطلاعات خود را در مورد پیشرفت و اینکه "آماده" به چه معناست نگه می‌دارد. + +می‌توانید کلمه کلیدی `async` را به بلوک‌ها و توابع اعمال کنید تا مشخص کنید که می‌توانند متوقف شده و از سر گرفته شوند. درون یک بلوک async یا تابع async، می‌توانید از کلمه کلیدی `await` برای _انتظار یک future_ (یعنی منتظر ماندن تا آماده شود) استفاده کنید. هر نقطه‌ای که در آن یک future را در یک بلوک یا تابع async انتظار می‌کشید، یک نقطه بالقوه برای متوقف و از سر گرفتن آن بلوک یا تابع async است. فرآیند بررسی یک future برای اینکه ببیند مقدار آن هنوز آماده است یا خیر، _polling_ نامیده می‌شود. + +برخی زبان‌های دیگر، مانند C# و JavaScript، نیز از کلمات کلیدی `async` و `await` برای برنامه‌نویسی ناهمزمان استفاده می‌کنند. اگر با این زبان‌ها آشنا هستید، ممکن است تفاوت‌های قابل توجهی در نحوه عملکرد Rust، از جمله نحوه مدیریت سینتکس آن، مشاهده کنید. این تفاوت‌ها دلایل خوبی دارند، همان‌طور که خواهیم دید! + +هنگام نوشتن کد async در Rust، بیشتر اوقات از کلمات کلیدی `async` و `await` استفاده می‌کنیم. Rust آن‌ها را به کدی معادل با استفاده از ویژگی `Future` کامپایل می‌کند، همان‌طور که حلقه‌های `for` را به کدی معادل با استفاده از ویژگی `Iterator` کامپایل می‌کند. با این حال، از آنجا که Rust ویژگی `Future` را ارائه می‌دهد، می‌توانید آن را برای نوع‌های داده خودتان نیز پیاده‌سازی کنید. بسیاری از توابعی که در طول این فصل مشاهده خواهیم کرد نوع‌هایی را بازمی‌گردانند که پیاده‌سازی‌های خود از `Future` را دارند. در انتهای فصل به تعریف این ویژگی بازمی‌گردیم و بیشتر در مورد نحوه عملکرد آن بحث می‌کنیم، اما این توضیحات برای ادامه کافی است. + +ممکن است این توضیحات کمی انتزاعی به نظر برسند، بنابراین بیایید اولین برنامه async خود را بنویسیم: یک web scraper کوچک. ما دو URL را از خط فرمان دریافت می‌کنیم، هر دو را به صورت همزمان دریافت می‌کنیم و نتیجه اولین URL که به پایان می‌رسد را بازمی‌گردانیم. این مثال دارای سینتکس جدیدی خواهد بود، اما نگران نباشید—همه چیزهایی که باید بدانید را در طول مسیر توضیح خواهیم داد. + +## اولین برنامه Async ما + +برای تمرکز این فصل روی یادگیری async به جای مدیریت بخش‌های اکوسیستم، یک crate به نام `trpl` ایجاد کرده‌ایم (`trpl` مخفف "The Rust Programming Language" است). این crate همه نوع‌ها، ویژگی‌ها، و توابع مورد نیاز شما را بازصادر می‌کند، عمدتاً از crateهای [`futures`][futures-crate] و [`tokio`][tokio]. crate `futures` خانه رسمی برای آزمایش کد async در Rust است و در واقع جایی است که ویژگی `Future` در ابتدا طراحی شد. `tokio` امروز رایج‌ترین Runtime async در Rust است، به ویژه برای برنامه‌های وب. Runtimeهای عالی دیگری نیز وجود دارند که ممکن است برای اهداف شما مناسب‌تر باشند. ما از crate `tokio` در زیرساخت `trpl` استفاده می‌کنیم زیرا به خوبی تست شده و به طور گسترده استفاده می‌شود. + +در برخی موارد، `trpl` همچنین APIهای اصلی را تغییر نام داده یا آن‌ها را پوشش می‌دهد تا شما را بر روی جزئیات مرتبط با این فصل متمرکز نگه دارد. اگر می‌خواهید بفهمید این crate چه می‌کند، ما شما را تشویق می‌کنیم که [سورس کد آن][crate-source] را بررسی کنید. می‌توانید ببینید که هر بازصادر از کدام crate می‌آید، و توضیحات گسترده‌ای در مورد آنچه که crate انجام می‌دهد گذاشته‌ایم. + +یک پروژه باینری جدید به نام `hello-async` ایجاد کنید و crate `trpl` را به عنوان وابستگی اضافه کنید: ```console $ cargo new hello-async @@ -68,17 +26,14 @@ $ cd hello-async $ cargo add trpl ``` -Now we can use the various pieces provided by `trpl` to write our first async -program. We’ll build a little command line tool that fetches two web pages, -pulls the `` element from each, and prints out the title of whichever -page finishes that whole process first. +اکنون می‌توانیم از بخش‌های مختلف ارائه‌شده توسط `trpl` استفاده کنیم تا اولین برنامه async خود را بنویسیم. ما یک ابزار کوچک خط فرمان ایجاد خواهیم کرد که دو صفحه وب را دریافت می‌کند، عنصر `<title>` را از هرکدام استخراج می‌کند و عنوان صفحه‌ای که سریع‌تر کل این فرآیند را تکمیل می‌کند، چاپ می‌کند. + +### تعریف تابع `page_title` -### Defining the page_title Function +بیایید با نوشتن یک تابع که یک URL صفحه را به عنوان پارامتر می‌گیرد، یک درخواست به آن ارسال می‌کند و متن عنصر `<title>` را بازمی‌گرداند شروع کنیم (نگاه کنید به لیست ۱۷-۱). -Let’s start by writing a function that takes one page URL as a parameter, makes -a request to it, and returns the text of the title element (see Listing 17-1). +<Listing number="17-1" file-name="src/main.rs" caption="تعریف یک تابع async برای دریافت عنصر `<title>` از یک صفحه HTML"> -<Listing number="17-1" file-name="src/main.rs" caption="Defining an async function to get the title element from an HTML page"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-01/src/main.rs:all}} @@ -86,55 +41,17 @@ a request to it, and returns the text of the title element (see Listing 17-1). </Listing> -First, we define a function named `page_title` and mark it with the `async` -keyword. Then we use the `trpl::get` function to fetch whatever URL is passed in -and add the `await` keyword to await the response. To get the text of the -response, we call its `text` method, and once again await it with the `await` -keyword. Both of these steps are asynchronous. For the `get` function, we have -to wait for the server to send back the first part of its response, which will -include HTTP headers, cookies, and so on, and can be delivered separately from -the response body. Especially if the body is very large, it can take some time -for it all to arrive. Because we have to wait for the _entirety_ of the response -to arrive, the `text` method is also async. - -We have to explicitly await both of these futures, because futures in Rust are -_lazy_: they don’t do anything until you ask them to with the `await` keyword. -(In fact, Rust will show a compiler warning if you don’t use a future.) This -might remind you of Chapter 13’s discussion of iterators in the section -[Processing a Series of Items With Iterators][iterators-lazy]<!-- ignore -->. -Iterators do nothing unless you call their `next` method—whether directly or by -using `for` loops or methods such as `map` that use `next` under the hood. -Likewise, futures do nothing unless you explicitly ask them to. This laziness -allows Rust to avoid running async code until it’s actually needed. - -> Note: This is different from the behavior we saw in the previous chapter when -> using `thread::spawn` in [Creating a New Thread with -> spawn][thread-spawn]<!--ignore-->, where the closure we passed to another -> thread started running immediately. It’s also different from how many other -> languages approach async. But it’s important for Rust to be able to provide -> its performance guarantees, just as it is with iterators. - -Once we have `response_text`, we can parse it into an instance of the `Html` -type using `Html::parse`. Instead of a raw string, we now have a data type we -can use to work with the HTML as a richer data structure. In particular, we can -use the `select_first` method to find the first instance of a given CSS -selector. By passing the string `"title"`, we’ll get the first `<title>` element -in the document, if there is one. Because there may not be any matching element, -`select_first` returns an `Option<ElementRef>`. Finally, we use the -`Option::map` method, which lets us work with the item in the `Option` if it’s -present, and do nothing if it isn’t. (We could also use a `match` expression -here, but `map` is more idiomatic.) In the body of the function we supply to -`map`, we call `inner_html` on the `title` to get its content, which is -a `String`. When all is said and done, we have an `Option<String>`. - -Notice that Rust’s `await` keyword goes _after_ the expression you’re awaiting, -not before it. That is, it’s a _postfix_ keyword. This may differ from what -you’re used to if you’ve used `async` in other languages, but in Rust it makes -chains of methods much nicer to work with. As a result, we can change the body -of `page_title` to chain the `trpl::get` and `text` function calls together -with `await` between them, as shown in Listing 17-2. - -<Listing number="17-2" file-name="src/main.rs" caption="Chaining with the `await` keyword"> +ابتدا یک تابع به نام `page_title` تعریف می‌کنیم و آن را با کلمه کلیدی `async` علامت‌گذاری می‌کنیم. سپس از تابع `trpl::get` برای دریافت هر URL که به آن ارسال می‌شود استفاده می‌کنیم و کلمه کلیدی `await` را اضافه می‌کنیم تا منتظر پاسخ بمانیم. برای دریافت متن پاسخ، متد `text` را فراخوانی می‌کنیم و دوباره با کلمه کلیدی `await` منتظر آن می‌مانیم. هر دو این مراحل ناهمزمان هستند. برای تابع `get`، باید منتظر باشیم تا سرور اولین قسمت از پاسخ خود را ارسال کند که شامل هدرهای HTTP، کوکی‌ها و غیره است و می‌تواند جدا از بدنه پاسخ ارسال شود. به ویژه اگر بدنه بسیار بزرگ باشد، ممکن است مدتی طول بکشد تا همه آن برسد. از آنجا که باید منتظر _تمامیت_ پاسخ بمانیم، متد `text` نیز async است. + +باید به‌صراحت منتظر هر دو future باشیم، زیرا futures در Rust _تنبل_ هستند: تا زمانی که از آن‌ها با کلمه کلیدی `await` درخواست نشود، هیچ کاری انجام نمی‌دهند. (در واقع، Rust یک هشدار کامپایلر نمایش می‌دهد اگر از یک future استفاده نکنید.) این ممکن است شما را به یاد بحث فصل ۱۳ درباره iteratorها در بخش [پردازش یک سری از آیتم‌ها با iteratorها][iterators-lazy]<!-- ignore --> بیندازد. iteratorها هیچ کاری انجام نمی‌دهند مگر اینکه متد `next` آن‌ها را فراخوانی کنید—چه به صورت مستقیم یا با استفاده از حلقه‌های `for` یا متدهایی مانند `map` که در پشت صحنه از `next` استفاده می‌کنند. به همین ترتیب، futures هیچ کاری انجام نمی‌دهند مگر اینکه به‌صراحت از آن‌ها درخواست شود. این ویژگی تنبلی به Rust اجازه می‌دهد تا کد async را تا زمانی که واقعاً مورد نیاز است، اجرا نکند. + +> توجه: این رفتار با چیزی که در فصل قبل هنگام استفاده از `thread::spawn` در [ایجاد یک نخ جدید با spawn][thread-spawn]<!--ignore--> دیدیم متفاوت است، جایی که closure‌ای که به نخ دیگر منتقل کردیم بلافاصله شروع به اجرا کرد. همچنین این رفتار با رویکرد بسیاری از زبان‌های دیگر در مورد async نیز تفاوت دارد. اما این موضوع برای Rust اهمیت دارد تا بتواند تضمین‌های عملکردی خود را همانند کاری که با پیمایشگرها انجام می‌دهد، حفظ کند. + +زمانی که `response_text` را دریافت کردیم، می‌توانیم آن را با استفاده از `Html::parse` به نمونه‌ای از نوع `Html` تبدیل کنیم. به جای یک رشته‌ی خام، اکنون یک نوع داده داریم که می‌توانیم از آن برای کار با HTML به‌عنوان یک ساختار داده‌ی غنی‌تر استفاده کنیم. به‌ویژه می‌توانیم از متد `select_first` برای یافتن اولین نمونه از یک سلکتور CSS مشخص استفاده کنیم. با ارسال رشته‌ی `"title"`، اولین عنصر `<title>` موجود در سند را دریافت خواهیم کرد، اگر عنصری وجود داشته باشد. از آن‌جایی که ممکن است هیچ عنصر مطابقت‌یافته‌ای وجود نداشته باشد، `select_first` یک `Option<ElementRef>` بازمی‌گرداند. در نهایت، از متد `Option::map` استفاده می‌کنیم که به ما اجازه می‌دهد اگر مقداری در `Option` وجود داشت با آن کار کنیم، و اگر وجود نداشت، هیچ کاری انجام ندهیم. (می‌توانستیم از یک عبارت `match` نیز استفاده کنیم، اما استفاده از `map` در این‌جا ایدیاتیک‌تر است.) در بدنه‌ی تابعی که به `map` می‌دهیم، متد `inner_html` را روی `title` فراخوانی می‌کنیم تا محتوای آن را به‌صورت یک `String` دریافت کنیم. در پایان، نتیجه‌ی ما یک `Option<String>` خواهد بود. + +توجه داشته باشید که کلمه‌ی کلیدی `await` در Rust **پس از** عبارتی که منتظر آن هستید می‌آید، نه قبل از آن. به عبارت دیگر، این یک کلمه‌ی کلیدی **پسوندی** است. این ممکن است با چیزی که در زبان‌های دیگر هنگام استفاده از `async` تجربه کرده‌اید متفاوت باشد، اما در Rust این موضوع باعث می‌شود زنجیره‌های توابع خواناتر و قابل‌مدیریت‌تر شوند. بنابراین، می‌توانیم بدنه‌ی تابع `page_title` را طوری تغییر دهیم که توابع `trpl::get` و `text` را با استفاده از `await` بین آن‌ها به‌صورت زنجیره‌ای صدا بزنیم، همان‌طور که در لیست 17-2 نشان داده شده است. + +<Listing number="17-2" file-name="src/main.rs" caption="زنجیره کردن با کلمه کلیدی `await`"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-02/src/main.rs:chaining}} @@ -142,20 +59,12 @@ with `await` between them, as shown in Listing 17-2. </Listing> -With that, we have successfully written our first async function! Before we add -some code in `main` to call it, let’s talk a little more about what we’ve -written and what it means. +با این توضیحات، ما اولین تابع async خود را با موفقیت نوشتیم! پیش از اضافه کردن کدی در `main` برای فراخوانی آن، بیایید کمی بیشتر درباره آنچه نوشته‌ایم و معنای آن صحبت کنیم. -When Rust sees a block marked with the `async` keyword, it compiles it into a -unique, anonymous data type that implements the `Future` trait. When Rust sees a -function marked with `async`, it compiles it into a non-async function whose -body is an async block. An async function’s return type is the type of the -anonymous data type the compiler creates for that async block. +هنگامی که Rust یک بلوک که با کلمه کلیدی `async` علامت‌گذاری شده است را می‌بیند، آن را به یک نوع داده منحصربه‌فرد و ناشناس که ویژگی `Future` را پیاده‌سازی می‌کند، کامپایل می‌کند. هنگامی که Rust یک تابع که با `async` علامت‌گذاری شده است را می‌بیند، آن را به یک تابع غیر-async که بدنه آن یک بلوک async است، کامپایل می‌کند. نوع بازگشتی یک تابع async نوع داده ناشناسی است که کامپایلر برای آن بلوک async ایجاد می‌کند. + +بنابراین، نوشتن `async fn` معادل نوشتن تابعی است که یک _future_ از نوع بازگشتی برمی‌گرداند. برای کامپایلر، یک تعریف تابع مانند `async fn page_title` در لیست ۱۷-۱ معادل یک تابع غیر-async به شکل زیر است: -Thus, writing `async fn` is equivalent to writing a function that returns a -_future_ of the return type. To the compiler, a function definition such as the -`async fn page_title` in Listing 17-1 is equivalent to a non-async function -defined like this: ```rust # extern crate trpl; // required for mdbook test @@ -172,33 +81,19 @@ fn page_title(url: &str) -> impl Future<Output = Option<String>> { } ``` -Let’s walk through each part of the transformed version: - -- It uses the `impl Trait` syntax we discussed back in Chapter 10 in the - [“Traits as Parameters”][impl-trait]<!-- ignore --> section. -- The returned trait is a `Future` with an associated type of `Output`. Notice - that the `Output` type is `Option<String>`, which is the same as the original - return type from the `async fn` version of `page_title`. -- All of the code called in the body of the original function is wrapped in an - `async move` block. Remember that blocks are expressions. This whole block is - the expression returned from the function. -- This async block produces a value with the type `Option<String>`, as just - described. That value matches the `Output` type in the return type. This - is just like other blocks you have seen. -- The new function body is an `async move` block because of how it uses the - `url` parameter. (We’ll talk much more about `async` versus `async move` later - in the chapter.) - -Now we can call `page_title` in `main`. - -## Determining a Single Page’s Title - -To start, we’ll just get the title for a single page. In Listing 17-3, we follow -the same pattern we used in Chapter 12 to get command line arguments in the -[Accepting Command Line Arguments][cli-args]<!-- ignore --> section. Then we -pass the first URL `page_title` and await the result. Because the value -produced by the future is an `Option<String>`, we use a `match` expression to -print different messages to account for whether the page had a `<title>`. +بیایید هر بخش از نسخه تبدیل‌شده را بررسی کنیم: + +* این تابع از سینتکس `impl Trait` استفاده می‌کند که در فصل ۱۰ در بخش \[«Traits به‌عنوان پارامتر»]\[impl-trait]<!-- ignore --> بررسی کردیم. +* trait بازگشتی یک `Future` است با نوع مرتبطی به نام `Output`. دقت کنید که نوع `Output` مقدار `Option<String>` است، که همان نوع بازگشتی نسخه‌ی اصلی `async fn` تابع `page_title` می‌باشد. +* تمام کدی که در بدنه‌ی تابع اصلی فراخوانی می‌شد، اکنون درون یک بلاک `async move` قرار گرفته است. به یاد داشته باشید که بلاک‌ها در Rust یک عبارت محسوب می‌شوند. این بلاک به‌طور کامل همان عبارتی است که از تابع بازگردانده می‌شود. +* این بلاک async یک مقدار با نوع `Option<String>` تولید می‌کند، همان‌طور که توصیف شد. این مقدار با نوع `Output` در نوع بازگشتی مطابقت دارد. این موضوع مشابه بلاک‌های دیگری است که تاکنون دیده‌اید. +* بدنه‌ی تابع جدید یک بلاک `async move` است، به دلیل نحوه‌ی استفاده از پارامتر `url` درون بلاک. (در ادامه‌ی این فصل، به‌طور مفصل‌تر درباره‌ی تفاوت `async` و `async move` صحبت خواهیم کرد.) + +حالا می‌توانیم `page_title` را در `main` فراخوانی کنیم. + +## تعیین عنوان یک صفحه + +برای شروع، فقط عنوان یک صفحه را دریافت می‌کنیم. در لیست ۱۷-۳، همان الگویی که در فصل ۱۲ برای دریافت آرگومان‌های خط فرمان در بخش [پذیرفتن آرگومان‌های خط فرمان][cli-args]<!-- ignore --> استفاده کردیم را دنبال می‌کنیم. سپس URL اول را به `page_title` ارسال کرده و نتیجه را انتظار می‌کشیم. چون مقداری که توسط future تولید می‌شود یک `Option<String>` است، از یک عبارت `match` برای چاپ پیام‌های مختلف استفاده می‌کنیم تا مشخص شود آیا صفحه یک `<title>` داشته است یا خیر. <Listing number="17-3" file-name="src/main.rs" caption="Calling the `page_title` function from `main` with a user-supplied argument"> @@ -208,9 +103,8 @@ print different messages to account for whether the page had a `<title>`. </Listing> -Unfortunately, this code doesn’t compile. The only place we can use the `await` -keyword is in async functions or blocks, and Rust won’t let us mark the -special `main` function as `async`. +متأسفانه، این کد کامپایل نمی‌شود. تنها جایی که می‌توانیم از کلمه کلیدی `await` استفاده کنیم، در توابع یا بلوک‌های async است، و Rust اجازه نمی‌دهد تابع ویژه `main` را به‌عنوان `async` علامت‌گذاری کنیم. + <!-- manual-regeneration cd listings/ch17-async-await/listing-17-03 @@ -226,35 +120,15 @@ error[E0752]: `main` function is not allowed to be `async` | ^^^^^^^^^^^^^^^ `main` function is not allowed to be `async` ``` -The reason `main` can’t be marked `async` is that async code needs a _runtime_: -a Rust crate that manages the details of executing asynchronous code. A -program’s `main` function can _initialize_ a runtime, but it’s not a runtime -_itself_. (We’ll see more about why this is the case in a bit.) Every Rust -program that executes async code has at least one place where it sets up a -runtime and executes the futures. - -Most languages that support async bundle a runtime, but Rust does not. Instead, -there are many different async runtimes available, each of which makes different -tradeoffs suitable to the use case it targets. For example, a high-throughput -web server with many CPU cores and a large amount of RAM has very different -needs than a microcontroller with a single core, a small amount of RAM, and no -heap allocation ability. The crates that provide those runtimes also often -supply async versions of common functionality such as file or network I/O. - -Here, and throughout the rest of this chapter, we’ll use the `run` function from -the `trpl` crate, which takes a future as an argument and runs it to completion. -Behind the scenes, calling `run` sets up a runtime that’s used to run the future -passed in. Once the future completes, `run` returns whatever value the future -produced. - -We could pass the future returned by `page_title` directly to `run`, and once it -completed, we could match on the resulting `Option<String>`, as -we tried to do in Listing 17-3. However, for most of the examples in the chapter -(and most async code in the real world), we’ll be doing more than just one -async function call, so instead we’ll pass an `async` block and explicitly -await the result of the `page_title` call, as in Listing 17-4. - -<Listing number="17-4" caption="Awaiting an async block with `trpl::run`" file-name="src/main.rs"> +دلیل اینکه نمی‌توان `main` را به‌عنوان `async` علامت‌گذاری کرد این است که کد async به یک _runtime_ نیاز دارد: یک crate در Rust که جزئیات اجرای کد ناهمزمان را مدیریت می‌کند. تابع `main` یک برنامه می‌تواند یک runtime را _مقداردهی اولیه_ کند، اما خودش یک runtime نیست. (در ادامه، بیشتر خواهیم دید که چرا این‌گونه است.) هر برنامه Rust که کد async اجرا می‌کند، حداقل یک مکان دارد که در آن یک runtime راه‌اندازی کرده و futures را اجرا می‌کند. + +بیشتر زبان‌هایی که از async پشتیبانی می‌کنند، یک runtime همراه دارند، اما Rust این کار را نمی‌کند. در عوض، بسیاری از runtimeهای async مختلف موجود هستند که هرکدام موازنه‌های متفاوتی برای موارد استفاده خاص خود ارائه می‌دهند. برای مثال، یک وب سرور با توان عملیاتی بالا که دارای هسته‌های CPU متعدد و مقدار زیادی RAM است، نیازهای بسیار متفاوتی نسبت به یک میکروکنترلر با یک هسته، مقدار کمی RAM و بدون قابلیت تخصیص heap دارد. crateهایی که این runtimeها را فراهم می‌کنند اغلب نسخه‌های async از قابلیت‌های عمومی مانند I/O فایل یا شبکه را نیز ارائه می‌دهند. + +اینجا و در بقیه این فصل، از تابع `run` از crate `trpl` استفاده خواهیم کرد، که یک future را به‌عنوان آرگومان می‌گیرد و آن را تا پایان اجرا می‌کند. در پشت صحنه، فراخوانی `run` یک runtime راه‌اندازی می‌کند که برای اجرای future ارسال‌شده استفاده می‌شود. وقتی future کامل شد، `run` هر مقداری که future تولید کرده باشد، بازمی‌گرداند. + +می‌توانستیم future بازگردانده‌شده توسط `page_title` را مستقیماً به `run` ارسال کنیم، و وقتی کامل شد، می‌توانستیم بر اساس `Option<String>` نتیجه، یک `match` انجام دهیم، همان‌طور که در لیست ۱۷-۳ تلاش کردیم. با این حال، برای بیشتر مثال‌های این فصل (و بیشتر کد async در دنیای واقعی)، بیش از یک فراخوانی تابع async انجام خواهیم داد، بنابراین به‌جای آن یک بلوک `async` ارسال می‌کنیم و صراحتاً نتیجه فراخوانی `page_title` را انتظار می‌کشیم، همان‌طور که در لیست ۱۷-۴ نشان داده شده است. + +<Listing number="17-4" caption="منتظر ماندن یک بلوک async با `trpl::run`" file-name="src/main.rs"> <!-- should_panic,noplayground because mdbook test does not pass args --> @@ -264,7 +138,7 @@ await the result of the `page_title` call, as in Listing 17-4. </Listing> -When we run this code, we get the behavior we expected initially: +وقتی این کد را اجرا می‌کنیم، رفتاری را که ممکن است ابتدا انتظار داشتیم دریافت می‌کنیم: <!-- manual-regeneration cd listings/ch17-async-await/listing-17-04 @@ -281,53 +155,28 @@ The title for https://www.rust-lang.org was Rust Programming Language ``` -Phew—we finally have some working async code! But before we add the code to race -the two sites against each other, let’s briefly turn our attention back to how -futures work. +پوووف—بالاخره مقداری کد async کارا داریم! اما قبل از اینکه کدی اضافه کنیم که دو سایت را در مقابل یکدیگر رقابت دهد، بیایید به‌طور مختصر دوباره به نحوه کار futures توجه کنیم. -Each _await point_—that is, every place where the code uses the `await` -keyword—represents a place where control is handed back to the runtime. To -make that work, Rust needs to keep track of the state involved in the async -block so that the runtime can kick off some other work and then come back when -it’s ready to try advancing the first one again. This is an invisible state machine, -as if you’d written an enum like this to save the current state at each await -point: +هر _نقطه انتظار_—یعنی هر جایی که کد از کلمه کلیدی `await` استفاده می‌کند—نمایانگر جایی است که کنترل به runtime بازمی‌گردد. برای اینکه این کار انجام شود، Rust نیاز دارد وضعیت مربوط به بلوک async را پیگیری کند تا runtime بتواند کار دیگری را آغاز کند و سپس وقتی آماده شد دوباره برای پیشرفت بلوک اول بازگردد. این یک ماشین حالت نامرئی است، گویی که شما یک enum مانند این نوشته‌اید تا وضعیت فعلی را در هر نقطه انتظار ذخیره کند: ```rust {{#rustdoc_include ../listings/ch17-async-await/no-listing-state-machine/src/lib.rs:enum}} ``` -Writing the code to transition between each state by hand would be tedious and -error-prone, however, especially when you need to add more functionality and -more states to the code later. Fortunately, the Rust compiler creates and -manages the state machine data structures for async code automatically. The -normal borrowing and ownership rules around data structures all still apply, and -happily, the compiler also handles checking those for us and provides useful -error messages. We’ll work through a few of those later in the chapter. +نوشتن کدی که به صورت دستی بین هر حالت انتقال یابد خسته‌کننده و مستعد خطا خواهد بود، به‌ویژه زمانی که بخواهید عملکرد بیشتری اضافه کرده و حالات بیشتری به کد اضافه کنید. خوشبختانه، کامپایلر Rust به طور خودکار ساختارهای داده مربوط به ماشین حالت را برای کد async ایجاد و مدیریت می‌کند. قوانین عادی مالکیت و قرض‌گیری در مورد ساختارهای داده همچنان اعمال می‌شوند، و خوشبختانه، کامپایلر بررسی این موارد را نیز برای ما انجام می‌دهد و پیام‌های خطای مفیدی ارائه می‌دهد. در ادامه فصل چند مورد از این پیام‌ها را بررسی خواهیم کرد. + +در نهایت، چیزی باید این ماشین حالت را اجرا کند، و آن چیز یک runtime است. (به همین دلیل ممکن است در بررسی runtimeها به ارجاعاتی به _executors_ برخورد کنید: یک executor بخشی از runtime است که مسئول اجرای کد async است.) -Ultimately, something has to execute this state machine, and that something is a -runtime. (This is why you may come across references to _executors_ -when looking into runtimes: an executor is the part of a runtime responsible for -executing the async code.) +اکنون می‌توانید دلیل این‌که چرا کامپایلر اجازه نداد تابع `main` را در لیستینگ 17-3 به‌صورت `async` تعریف کنیم، بهتر درک کنید. اگر `main` یک تابع async بود، باید یک جزء دیگر مسئول مدیریت ماشین حالت برای `future`ای می‌بود که `main` بازمی‌گرداند؛ اما `main` نقطه‌ی شروع برنامه است! بنابراین، به‌جای آن در تابع `main`، تابع `trpl::run` را فراخوانی کردیم تا یک runtime راه‌اندازی کند و `future` بازگردانده‌شده از بلاک async را تا زمان اتمام اجرا کند. -Now you can see why the compiler stopped us from making `main` itself an async -function back in Listing 17-3. If `main` were an async function, something else -would need to manage the state machine for whatever future `main` returned, but -`main` is the starting point for the program! Instead, we called the `trpl::run` -function in `main` to set up a runtime and run the future returned by the -`async` block until it is done. +> نکته: برخی runtimeها ماکروهایی فراهم می‌کنند که به شما اجازه می‌دهند یک تابع `main` به‌صورت async بنویسید. این ماکروها عبارت `async fn main() { ... }` را بازنویسی می‌کنند به یک تابع `fn main` معمولی که همان کاری را انجام می‌دهد که ما در لیستینگ 17-4 به‌صورت دستی انجام دادیم: فراخوانی تابعی که یک future را تا تکمیل اجرا می‌کند، مانند کاری که `trpl::run` انجام می‌دهد. -> Note: Some runtimes provide macros so you _can_ write an async `main` -> function. Those macros rewrite `async fn main() { ... }` to be a normal `fn -> main`, which does the same thing we did by hand in Listing 17-4: call a -> function that runs a future to completion the way `trpl::run` does. +حالا بیایید این بخش‌ها را کنار هم قرار دهیم و ببینیم چگونه می‌توان کدی همزمان نوشت. -Now let’s put these pieces together and see how we can write concurrent code. +### رقابت بین دو URL -### Racing Our Two URLs Against Each Other +در لیست ۱۷-۵، ما `page_title` را با دو URL مختلف که از خط فرمان ارسال شده‌اند، فراخوانی کرده و آن‌ها را با یکدیگر رقابت می‌دهیم. -In Listing 17-5, we call `page_title` with two different URLs passed in from the -command line and race them. <Listing number="17-5" caption="" file-name="src/main.rs"> @@ -339,23 +188,11 @@ command line and race them. </Listing> -We begin by calling `page_title` for each of the user-supplied URLs. We save the -resulting futures as `title_fut_1` and `title_fut_2`. Remember, these don’t do -anything yet, because futures are lazy and we haven’t yet awaited them. Then we -pass the futures to `trpl::race`, which returns a value to indicate which of the -futures passed to it finishes first. +ما با فراخوانی `page_title` برای هر یک از URLهایی که توسط کاربر ارسال شده‌اند، شروع می‌کنیم. Futureهای حاصل را به نام‌های `title_fut_1` و `title_fut_2` ذخیره می‌کنیم. به یاد داشته باشید، این‌ها هنوز کاری انجام نمی‌دهند، زیرا futures تنبل هستند و هنوز منتظر آن‌ها نمانده‌ایم. سپس این futures را به `trpl::race` ارسال می‌کنیم، که مقداری بازمی‌گرداند تا نشان دهد کدام یک از futures ارسال‌شده به آن ابتدا کامل شده است. -> Note: Under the hood, `race` is built on a more general function, `select`, -> which you will encounter more often in real-world Rust code. A `select` -> function can do a lot of things that the `trpl::race` function can’t, but it -> also has some additional complexity that we can skip over for now. +> نکته: در پشت صحنه، `race` بر اساس یک تابع عمومی‌تر به نام `select` ساخته شده است، که اغلب در کدهای واقعی Rust با آن مواجه خواهید شد. یک تابع `select` می‌تواند کارهایی انجام دهد که تابع `trpl::race` نمی‌تواند، اما همچنین دارای پیچیدگی‌های اضافی است که فعلاً می‌توانیم از آن صرف‌نظر کنیم. -Either future can legitimately “win,” so it doesn’t make sense to return a -`Result`. Instead, `race` returns a type we haven’t seen before, -`trpl::Either`. The `Either` type is somewhat similar to a `Result` in that it -has two cases. Unlike `Result`, though, there is no notion of success or -failure baked into `Either`. Instead, it uses `Left` and `Right` to indicate -“one or the other”: +هرکدام از futures می‌توانند به طور قانونی "برنده" شوند، بنابراین بازگرداندن یک `Result` منطقی نیست. در عوض، `race` نوعی را بازمی‌گرداند که قبلاً ندیده‌ایم: `trpl::Either`. نوع `Either` تا حدودی شبیه به `Result` است به این معنا که دو حالت دارد. اما برخلاف `Result`، هیچ مفهومی از موفقیت یا شکست در `Either` وجود ندارد. در عوض، از `Left` و `Right` برای نشان دادن "یکی یا دیگری" استفاده می‌کند: ```rust enum Either<A, B> { @@ -364,23 +201,12 @@ enum Either<A, B> { } ``` -The `race` function returns `Left` with the output from the first future -argument it finishes first, or `Right` with the output of the second future -argument if that one finishes first. This matches the order the arguments appear -in when calling the function: the first argument is to the left of the second -argument. +تابع `race` در صورتی که اولین future‌ ارائه‌شده زودتر به پایان برسد، مقدار `Left` را همراه با خروجی آن بازمی‌گرداند، و اگر دومین future زودتر به پایان برسد، مقدار `Right` را همراه با خروجی آن بازمی‌گرداند. این رفتار با ترتیبی که آرگومان‌ها هنگام فراخوانی تابع ظاهر می‌شوند مطابقت دارد: آرگومان اول در سمت چپ آرگومان دوم قرار دارد. + +همچنین تابع `page_title` را به‌روزرسانی می‌کنیم تا همان URL ارسال‌شده را بازگرداند. به این ترتیب، اگر صفحه‌ای که ابتدا بازمی‌گردد، دارای یک `<title>` نباشد که بتوانیم آن را استخراج کنیم، همچنان می‌توانیم یک پیام معنادار چاپ کنیم. با در دسترس بودن این اطلاعات، خروجی `println!` خود را به‌روزرسانی می‌کنیم تا مشخص کند کدام URL اول کامل شده است و `<title>` صفحه وب در آن URL چیست (اگر وجود داشته باشد). -We also update `page_title` to return the same URL passed in. That way, if -the page that returns first does not have a `<title>` we can resolve, we can -still print a meaningful message. With that information available, we wrap up by -updating our `println!` output to indicate both which URL finished first and -what, if any, the `<title>` is for the web page at that URL. +شما اکنون یک web scraper کوچک و کارا ساخته‌اید! چند URL انتخاب کنید و ابزار خط فرمان را اجرا کنید. ممکن است متوجه شوید که برخی سایت‌ها به طور مداوم سریع‌تر از بقیه هستند، در حالی که در موارد دیگر، سایت سریع‌تر از اجرای به اجرای دیگر متفاوت است. مهم‌تر از همه، شما اصول کار با futures را آموخته‌اید، بنابراین حالا می‌توانیم عمیق‌تر به آنچه می‌توان با async انجام داد، بپردازیم. -You have built a small working web scraper now! Pick a couple URLs and run the -command line tool. You may discover that some sites are consistently faster than -others, while in other cases the faster site varies from run to run. More -importantly, you’ve learned the basics of working with futures, so now we can -dig deeper into what we can do with async. [impl-trait]: ch10-02-traits.html#traits-as-parameters [iterators-lazy]: ch13-02-iterators.html @@ -389,6 +215,6 @@ dig deeper into what we can do with async. <!-- TODO: map source link version to version of Rust? --> -[crate-source]: https://github.com/rust-lang/book/tree/main/packages/trpl +[crate-source]: https://github.com/persian-rust/book/tree/main/packages/trpl [futures-crate]: https://crates.io/crates/futures [tokio]: https://tokio.rs diff --git a/src/ch17-02-concurrency-with-async.md b/src/ch17-02-concurrency-with-async.md index 36343f15a3..51f8693e50 100644 --- a/src/ch17-02-concurrency-with-async.md +++ b/src/ch17-02-concurrency-with-async.md @@ -4,31 +4,18 @@ <a id="concurrency-with-async"></a> -In this section, we’ll apply async to some of the same concurrency challenges -we tackled with threads in chapter 16. Because we already talked about a lot of -the key ideas there, in this section we’ll focus on what’s different between -threads and futures. -In many cases, the APIs for working with concurrency using async are very -similar to those for using threads. In other cases, they end up being quite -different. Even when the APIs _look_ similar between threads and async, they -often have different behavior—and they nearly always have different performance -characteristics. +در این بخش، async را به برخی از همان چالش‌های همزمانی که با نخ‌ها در فصل 16 انجام دادیم اعمال می‌کنیم. از آنجا که قبلاً درباره بسیاری از ایده‌های کلیدی در آنجا صحبت کرده‌ایم، در این بخش تمرکز بر تفاوت‌های بین نخ‌ها و آینده‌ها (futures) خواهیم داشت. -<!-- Old headings. Do not remove or links may break. --> +در بسیاری از موارد، APIها برای کار با همزمانی (_concurrency_) با استفاده از async بسیار شبیه به APIهایی هستند که برای استفاده از Threadها استفاده می‌شوند. در موارد دیگر، این APIها کاملاً متفاوت هستند. حتی زمانی که APIها بین Threadها و async _شبیه_ به نظر می‌رسند، اغلب رفتار متفاوتی دارند—و تقریباً همیشه ویژگی‌های عملکردی متفاوتی دارند. <a id="counting"></a> -### Creating a New Task with `spawn_task` +### ایجاد یک Task جدید با `spawn_task` -The first operation we tackled in [Creating a New Thread with -Spawn][thread-spawn]<!-- ignore --> was counting up on two separate threads. -Let’s do the same using async. The `trpl` crate supplies a `spawn_task` function -that looks very similar to the `thread::spawn` API, and a `sleep` function -that is an async version of the `thread::sleep` API. We can use these together -to implement the counting example, as shown in Listing 17-6. +اولین عملیاتی که در [ایجاد یک Thread جدید با Spawn][thread-spawn]<!-- ignore --> انجام دادیم، شمارش افزایشی در دو Thread جداگانه بود. بیایید همان کار را با استفاده از async انجام دهیم. crate `trpl` یک تابع `spawn_task` فراهم می‌کند که بسیار شبیه به API `thread::spawn` است، و یک تابع `sleep` که نسخه async از API `thread::sleep` است. می‌توانیم از این دو با هم استفاده کنیم تا مثال شمارش را پیاده‌سازی کنیم، همان‌طور که در لیست ۱۷-۶ نشان داده شده است. -<Listing number="17-6" caption="Creating a new task to print one thing while the main task prints something else" file-name="src/main.rs"> +<Listing number="17-6" caption="ایجاد یک Task جدید برای چاپ یک چیز در حالی که Task اصلی چیز دیگری را چاپ می‌کند" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-06/src/main.rs:all}} @@ -36,21 +23,13 @@ to implement the counting example, as shown in Listing 17-6. </Listing> -As our starting point, we set up our `main` function with `trpl::run` so that -our top-level function can be async. +به‌عنوان نقطه شروع، تابع `main` خود را با استفاده از `trpl::run` تنظیم می‌کنیم تا تابع سطح بالای ما بتواند async باشد. -> Note: From this point forward in the chapter, every example will include this -> exact same wrapping code with `trpl::run` in `main`, so we’ll often skip it -> just as we do with `main`. Don’t forget to include it in your code! +> نکته: از این نقطه به بعد در فصل، هر مثال این کد بسته‌بندی یکسان را با `trpl::run` در `main` شامل خواهد شد، بنابراین اغلب آن را مانند `main` نادیده می‌گیریم. فراموش نکنید که آن را در کد خود بگنجانید! -Then we write two loops within that block, each containing a `trpl::sleep` call, -which waits for half a second (500 milliseconds) before sending the next -message. We put one loop in the body of a `trpl::spawn_task` and the other in a -top-level `for` loop. We also add an `await` after the `sleep` calls. +سپس دو حلقه درون آن بلوک می‌نویسیم که هر کدام شامل یک فراخوانی به trpl::sleep هستند، که قبل از ارسال پیام بعدی به مدت نیم ثانیه (۵۰۰ میلی‌ثانیه) منتظر می‌مانند. یکی از حلقه‌ها را در بدنه یک trpl::spawn_task قرار می‌دهیم و دیگری را در یک حلقه for در سطح بالا. همچنین پس از فراخوانی‌های sleep یک await اضافه می‌کنیم. -This code behaves similarly to the thread-based implementation—including the -fact that you may see the messages appear in a different order in your own -terminal when you run it: +این کد رفتاری مشابه با پیاده‌سازی مبتنی بر Thread دارد—از جمله اینکه ممکن است پیام‌ها را در ترتیبی متفاوت در ترمینال خود هنگام اجرا مشاهده کنید:s <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the threads running differently rather than @@ -68,16 +47,9 @@ hi number 4 from the second task! hi number 5 from the first task! ``` -This version stops as soon as the `for` loop in the body of the main async block -finishes, because the task spawned by `spawn_task` is shut down when the `main` -function ends. If you want it to run all the way to the task’s completion, you -will need to use a join handle to wait for the first task to complete. With -threads, we used the `join` method to “block” until the thread was done running. -In Listing 17-7, we can use `await` to do the same thing, because the task -handle itself is a future. Its `Output` type is a `Result`, so we also unwrap it -after awaiting it. +این نسخه به محض اینکه حلقه `for` در بدنه بلوک async اصلی به پایان می‌رسد، متوقف می‌شود، زیرا taskی که توسط `spawn_task` ایجاد شده است با پایان یافتن تابع `main` متوقف می‌شود. اگر بخواهید تا اتمام کامل task اجرا شود، باید از یک handle join استفاده کنید تا منتظر بمانید اولین task به پایان برسد. با Threadها، از متد `join` برای "مسدود کردن" تا زمانی که Thread اجرا می‌شد، استفاده می‌کردیم. در لیست ۱۷-۷، می‌توانیم از `await` برای انجام همین کار استفاده کنیم، زیرا handle task خودش یک future است. نوع `Output` آن یک `Result` است، بنابراین پس از منتظر ماندن آن را unwrap می‌کنیم. -<Listing number="17-7" caption="Using `await` with a join handle to run a task to completion" file-name="src/main.rs"> +<Listing number="17-7" caption="استفاده از `await` با یک handle الحاقی برای اجرای تسک تا تکمیل" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-07/src/main.rs:handle}} @@ -85,7 +57,7 @@ after awaiting it. </Listing> -This updated version runs until _both_ loops finish. +نسخه به‌روزرسانی‌شده تا زمانی که _هر دو_ حلقه تمام شوند اجرا می‌شود. <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the threads running differently rather than @@ -107,27 +79,13 @@ hi number 8 from the first task! hi number 9 from the first task! ``` -So far, it looks like async and threads give us the same basic outcomes, just -with different syntax: using `await` instead of calling `join` on the join -handle, and awaiting the `sleep` calls. +تا اینجا، به نظر می‌رسد async و نخ‌ها نتایج اصلی یکسانی به ما می‌دهند، فقط با سینتکس متفاوت: استفاده از `await` به جای فراخوانی `join` روی handle الحاقی و انتظار برای فراخوانی‌های `sleep`. -The bigger difference is that we didn’t need to spawn another operating system -thread to do this. In fact, we don’t even need to spawn a task here. Because -async blocks compile to anonymous futures, we can put each loop in an async -block and have the runtime run them both to completion using the `trpl::join` -function. +تفاوت بزرگ‌تر این است که نیازی به ایجاد یک نخ سیستم‌عامل جداگانه برای این کار نداشتیم. در واقع، حتی نیازی به ایجاد یک تسک هم در اینجا نداریم. زیرا بلوک‌های async به آینده‌های ناشناس کامپایل می‌شوند، می‌توانیم هر حلقه را در یک بلوک async قرار دهیم و اجازه دهیم runtime هر دو را با استفاده از تابع `trpl::join` تا تکمیل اجرا کند. -In the section [Waiting for All Threads to Finishing Using `join` -Handles][join-handles]<!-- ignore -->, we showed how to use the `join` method on -the `JoinHandle` type returned when you call `std::thread::spawn`. The -`trpl::join` function is similar, but for futures. When you give it two futures, -it produces a single new future whose output is a tuple containing the output of -each future you passed in once they _both_ complete. Thus, in Listing 17-8, we -use `trpl::join` to wait for both `fut1` and `fut2` to finish. We do _not_ await -`fut1` and `fut2` but instead the new future produced by `trpl::join`. We ignore -the output, because it’s just a tuple containing two unit values. +در بخش [انتظار برای اتمام تمام Threadها با استفاده از Handles `join`][join-handles]<!-- ignore -->، نشان دادیم که چگونه می‌توان از متد `join` در نوع `JoinHandle` که هنگام فراخوانی `std::thread::spawn` بازگردانده می‌شود، استفاده کرد. تابع `trpl::join` مشابه است، اما برای futures طراحی شده است. وقتی دو future به آن می‌دهید، یک future جدید ایجاد می‌کند که خروجی آن یک tuple شامل خروجی هر یک از futureهایی است که به آن ارسال کرده‌اید، به شرطی که _هر دو_ کامل شوند. بنابراین، در لیست ۱۷-۸، از `trpl::join` استفاده می‌کنیم تا منتظر بمانیم `fut1` و `fut2` به پایان برسند. ما _نه_ برای `fut1` و `fut2`، بلکه برای future جدیدی که توسط `trpl::join` تولید می‌شود، منتظر می‌مانیم. خروجی را نادیده می‌گیریم، زیرا فقط یک tuple شامل دو مقدار unit است. -<Listing number="17-8" caption="Using `trpl::join` to await two anonymous futures" file-name="src/main.rs"> +<Listing number="17-8" caption="استفاده از `trpl::join` برای منتظر ماندن دو آینده ناشناس" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-08/src/main.rs:join}} @@ -135,7 +93,7 @@ the output, because it’s just a tuple containing two unit values. </Listing> -When we run this, we see both futures run to completion: +وقتی این کد را اجرا می‌کنیم، می‌بینیم هر دو futures تا تکمیل اجرا می‌شوند: <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the threads running differently rather than @@ -157,43 +115,23 @@ hi number 8 from the first task! hi number 9 from the first task! ``` -Now, you’ll see the exact same order every time, which is very different from -what we saw with threads. That is because the `trpl::join` function is _fair_, -meaning it checks each future equally often, alternating between them, and never -lets one race ahead if the other is ready. With threads, the operating system -decides which thread to check and how long to let it run. With async Rust, the -runtime decides which task to check. (In practice, the details get complicated -because an async runtime might use operating system threads under the hood as -part of how it manages concurrency, so guaranteeing fairness can be more work -for a runtime—but it’s still possible!) Runtimes don’t have to guarantee -fairness for any given operation, and they often offer different APIs to let you -choose whether or not you want fairness. - -Try some of these variations on awaiting the futures and see what they do: +اکنون، هر بار دقیقاً همان ترتیب را مشاهده خواهید کرد، که بسیار متفاوت از چیزی است که با Threadها دیدیم. دلیل این امر این است که تابع `trpl::join` _منصفانه_ است، به این معنی که هر future را به یک اندازه بررسی می‌کند، بین آن‌ها تناوب می‌گذارد و هرگز اجازه نمی‌دهد یکی از آن‌ها جلو بیفتد اگر دیگری آماده باشد. با Threadها، سیستم‌عامل تصمیم می‌گیرد که کدام Thread بررسی شود و چه مدت به آن اجازه اجرا بدهد. با Rust async، runtime تصمیم می‌گیرد که کدام task بررسی شود. (در عمل، جزئیات پیچیده می‌شوند زیرا یک runtime async ممکن است از Threadهای سیستم‌عامل در پشت صحنه به‌عنوان بخشی از نحوه مدیریت همزمانی استفاده کند، بنابراین تضمین منصفانه بودن می‌تواند برای runtime بیشتر کار ببرد—اما همچنان ممکن است!) runtimeها نیازی به تضمین منصفانه بودن برای هر عملیات خاصی ندارند، و اغلب APIهای مختلفی ارائه می‌دهند که به شما اجازه می‌دهند انتخاب کنید آیا می‌خواهید منصفانه بودن را اعمال کنید یا خیر. -- Remove the async block from around either or both of the loops. -- Await each async block immediately after defining it. -- Wrap only the first loop in an async block, and await the resulting future - after the body of second loop. +برخی از این تغییرات در انتظار برای futures را امتحان کنید و ببینید چه می‌کنند: -For an extra challenge, see if you can figure out what the output will be in -each case _before_ running the code! +- بلوک async را از اطراف یکی یا هر دو حلقه حذف کنید. +- هر بلوک async را بلافاصله پس از تعریف آن منتظر بمانید. +- فقط حلقه اول را در یک بلوک async قرار دهید و آینده حاصل را پس از بدنه حلقه دوم منتظر بمانید. -<!-- Old headings. Do not remove or links may break. --> +برای یک چالش اضافی، ببینید آیا می‌توانید پیش از اجرای کد پیش‌بینی کنید که خروجی چه خواهد بود! <a id="message-passing"></a> -### Counting Up on Two Tasks Using Message Passing +### شمارش افزایشی در دو Task با استفاده از ارسال پیام -Sharing data between futures will also be familiar: we’ll use message passing -again, but this time with async versions of the types and functions. We’ll take -a slightly different path than we did in [Using Message Passing to Transfer Data -Between Threads][message-passing-threads]<!-- ignore --> to illustrate some of -the key differences between thread-based and futures-based concurrency. In -Listing 17-9, we’ll begin with just a single async block—_not_ spawning a -separate task as we spawned a separate thread. +اشتراک داده‌ها بین futures نیز آشنا خواهد بود: دوباره از ارسال پیام استفاده خواهیم کرد، اما این بار با نسخه‌های async از انواع و توابع. ما مسیری کمی متفاوت از [استفاده از ارسال پیام برای انتقال داده‌ها بین Threadها][message-passing-threads]<!-- ignore --> خواهیم پیمود تا برخی از تفاوت‌های کلیدی بین همزمانی مبتنی بر Thread و همزمانی مبتنی بر futures را نشان دهیم. در لیست ۱۷-۹، فقط با یک بلوک async شروع می‌کنیم—و _نه_ ایجاد یک task جداگانه، همان‌طور که یک Thread جداگانه ایجاد کردیم. -<Listing number="17-9" caption="Creating an async channel and assigning the two halves to `tx` and `rx`" file-name="src/main.rs"> +<Listing number="17-9" caption="ایجاد یک کانال async و اختصاص دو نیمه به `tx` و `rx`" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-09/src/main.rs:channel}} @@ -201,40 +139,19 @@ separate task as we spawned a separate thread. </Listing> -Here, we use `trpl::channel`, an async version of the multiple-producer, -single-consumer channel API we used with threads back in Chapter 16. The async -version of the API is only a little different from the thread-based version: it -uses a mutable rather than an immutable receiver `rx`, and its `recv` method -produces a future we need to await rather than producing the value directly. Now -we can send messages from the sender to the receiver. Notice that we don’t have -to spawn a separate thread or even a task; we merely need to await the `rx.recv` -call. - -The synchronous `Receiver::recv` method in `std::mpsc::channel` blocks until -it receives a message. The `trpl::Receiver::recv` method does not, because it -is async. Instead of blocking, it hands control back to the runtime until either -a message is received or the send side of the channel closes. By contrast, we -don’t await the `send` call, because it doesn’t block. It doesn’t need to, -because the channel we’re sending it into is unbounded. - -> Note: Because all of this async code runs in an async block in a `trpl::run` -> call, everything within it can avoid blocking. However, the code _outside_ it -> will block on the `run` function returning. That’s the whole point of the -> `trpl::run` function: it lets you _choose_ where to block on some set of async -> code, and thus where to transition between sync and async code. In most async -> runtimes, `run` is actually named `block_on` for exactly this reason. - -Notice two things about this example. First, the message will arrive right away. -Second, although we use a future here, there’s no concurrency yet. Everything -in the listing happens in sequence, just as it would if there were no futures -involved. - -Let’s address the first part by sending a series of messages and sleeping in -between them, as shown in Listing 17-10. +اینجا، از `trpl::channel` استفاده می‌کنیم، نسخه async از API کانال چندتولیدی، یک‌مصرفی که در فصل 16 با نخ‌ها استفاده کردیم. نسخه async از API فقط کمی با نسخه مبتنی بر نخ متفاوت است: به جای استفاده از یک گیرنده غیرقابل‌تغییر (immutable)، از یک گیرنده قابل‌تغییر (mutable) `rx` استفاده می‌کند، و متد `recv` آن یک آینده تولید می‌کند که باید منتظر آن بمانیم، به جای تولید مقدار به‌طور مستقیم. اکنون می‌توانیم پیام‌ها را از فرستنده به گیرنده ارسال کنیم. توجه کنید که نیازی به ایجاد یک نخ جداگانه یا حتی یک تسک نداریم؛ فقط باید فراخوانی `rx.recv` را منتظر بمانیم. + +متد همگام `Receiver::recv` در `std::mpsc::channel` تا زمانی که پیامی دریافت شود مسدود می‌شود. متد `trpl::Receiver::recv` این کار را نمی‌کند، زیرا async است. به جای مسدود شدن، کنترل را به runtime بازمی‌گرداند تا زمانی که یا پیامی دریافت شود یا سمت ارسال کانال بسته شود. در مقابل، ما فراخوانی `send` را منتظر نمی‌مانیم، زیرا مسدود نمی‌شود. نیازی به این کار ندارد، زیرا کانالی که پیام را به آن ارسال می‌کنیم بدون حد است. + +> نکته: از آنجا که تمام این کد async در یک بلوک async درون یک فراخوانی `trpl::run` اجرا می‌شود، همه چیز درون آن می‌تواند از مسدود شدن اجتناب کند. با این حال، کد _خارج_ از آن روی بازگشت تابع `run` مسدود می‌شود. این همان هدف اصلی تابع `trpl::run` است: به شما اجازه می‌دهد _انتخاب_ کنید که کجا روی مجموعه‌ای از کد async مسدود شوید و بنابراین کجا بین کدهای sync و async انتقال دهید. در بیشتر runtimeهای async، `run` در واقع به همین دلیل `block_on` نامیده می‌شود. + +دو نکته در مورد این مثال توجه کنید. اول، پیام بلافاصله خواهد رسید. دوم، اگرچه ما اینجا از یک future استفاده می‌کنیم، هنوز هم هیچ همزمانی (_concurrency_) وجود ندارد. همه چیز در این لیست به ترتیب انجام می‌شود، درست مانند اینکه هیچ futureای در کار نباشد. + +بیایید به قسمت اول بپردازیم، با ارسال یک سری پیام و خوابیدن بین آن‌ها، همان‌طور که در لیست ۱۷-۱۰ نشان داده شده است. <!-- We cannot test this one because it never stops! --> -<Listing number="17-10" caption="Sending and receiving multiple messages over the async channel and sleeping with an `await` between each message" file-name="src/main.rs"> +<Listing number="17-10" caption="ارسال و دریافت چندین پیام از طریق کانال async و استفاده از `await` بین هر پیام" file-name="src/main.rs"> ```rust,ignore {{#rustdoc_include ../listings/ch17-async-await/listing-17-10/src/main.rs:many-messages}} @@ -242,62 +159,25 @@ between them, as shown in Listing 17-10. </Listing> -In addition to sending the messages, we need to receive them. In this case, -because we know how many messages are coming in, we could do that manually by -calling `rx.recv().await` four times. In the real world, though, we’ll generally -be waiting on some _unknown_ number of messages, so we need to keep waiting -until we determine that there are no more messages. - -In Listing 16-10, we used a `for` loop to process all the items received from a -synchronous channel. Rust doesn’t yet have a way to write a `for` loop over an -_asynchronous_ series of items, however, so we need to use a loop we haven’t -seen before: the `while let` conditional loop. This is the loop version of the -`if let` construct we saw back in the section [Concise Control Flow with `if -let` and `let else`][if-let]<!-- ignore -->. The loop will continue executing as -long as the pattern it specifies continues to match the value. - -The `rx.recv` call produces a future, which we await. The runtime will pause the -future until it is ready. Once a message arrives, the future will resolve to -`Some(message)` as many times as a message arrives. When the channel closes, -regardless of whether _any_ messages have arrived, the future will instead -resolve to `None` to indicate that there are no more values and thus we should -stop polling—that is, stop awaiting. - -The `while let` loop pulls all of this together. If the result of calling -`rx.recv().await` is `Some(message)`, we get access to the message and we can -use it in the loop body, just as we could with `if let`. If the result is -`None`, the loop ends. Every time the loop completes, it hits the await point -again, so the runtime pauses it again until another message arrives. - -The code now successfully sends and receives all of the messages. Unfortunately, -there are still a couple of problems. For one thing, the messages do not arrive -at half-second intervals. They arrive all at once, 2 seconds (2,000 -milliseconds) after we start the program. For another, this program never exits! -Instead, it waits forever for new messages. You will need to shut it down using -<span class="keystroke">ctrl-c</span>. - -Let’s start by examining why the messages come in all at once after the full -delay, rather than coming in with delays between each one. Within a given async -block, the order in which `await` keywords appear in the code is also the order -in which they’re executed when the program runs. - -There’s only one async block in Listing 17-10, so everything in it runs -linearly. There’s still no concurrency. All the `tx.send` calls happen, -interspersed with all of the `trpl::sleep` calls and their associated await -points. Only then does the `while let` loop get to go through any of the `await` -points on the `recv` calls. - -To get the behavior we want, where the sleep delay happens between each message, -we need to put the `tx` and `rx` operations in their own async blocks, as shown -in Listing 17-11. Then the runtime can execute each of them separately using -`trpl::join`, just as in the counting example. Once again, we await the result -of calling `trpl::join`, not the individual futures. If we awaited the -individual futures in sequence, we would just end up back in a sequential -flow—exactly what we’re trying _not_ to do. +علاوه بر ارسال پیام‌ها، باید آن‌ها را دریافت کنیم. در این مورد، چون می‌دانیم چند پیام قرار است دریافت شوند، می‌توانستیم این کار را به‌صورت دستی با چهار بار فراخوانی `rx.recv().await` انجام دهیم. اما در دنیای واقعی، معمولاً در انتظار یک تعداد _نامعلوم_ از پیام‌ها خواهیم بود، بنابراین نیاز داریم تا زمانی که مشخص کنیم پیام دیگری وجود ندارد، به انتظار ادامه دهیم. + +در لیست ۱۶-۱۰، از یک حلقه `for` برای پردازش تمام آیتم‌های دریافت‌شده از یک کانال همزمان استفاده کردیم. با این حال، Rust هنوز راهی برای نوشتن یک حلقه `for` روی یک سری آیتم _ناهمزمان_ ندارد، بنابراین باید از حلقه‌ای استفاده کنیم که قبلاً ندیده‌ایم: حلقه شرطی `while let`. این حلقه نسخه حلقه‌ای از ساختار `if let` است که در بخش [کنترل جریان مختصر با `if let` و `let else`][if-let]<!-- ignore --> دیدیم. این حلقه تا زمانی که الگوی مشخص‌شده آن همچنان با مقدار مطابقت داشته باشد، به اجرا ادامه می‌دهد. + +فراخوانی `rx.recv` یک future تولید می‌کند که منتظر آن می‌مانیم. runtime تا زمانی که future آماده شود، آن را متوقف می‌کند. وقتی پیامی برسد، future به `Some(message)` حل می‌شود، به ازای هر باری که پیام برسد. وقتی کانال بسته شود، صرف‌نظر از اینکه _آیا_ پیام‌هایی رسیده‌اند یا خیر، future به `None` حل می‌شود تا نشان دهد دیگر مقادیری وجود ندارد و بنابراین باید polling را متوقف کنیم—یعنی منتظر ماندن را متوقف کنیم. + +حلقه `while let` همه این‌ها را کنار هم قرار می‌دهد. اگر نتیجه فراخوانی `rx.recv().await` برابر با `Some(message)` باشد، به پیام دسترسی پیدا می‌کنیم و می‌توانیم از آن در بدنه حلقه استفاده کنیم، همانطور که با `if let` می‌توانستیم. اگر نتیجه `None` باشد، حلقه متوقف می‌شود. هر بار که حلقه کامل می‌شود، به نقطه انتظار بازمی‌گردد، بنابراین runtime دوباره آن را متوقف می‌کند تا زمانی که پیام دیگری برسد. + +کد اکنون با موفقیت تمام پیام‌ها را ارسال و دریافت می‌کند. با این حال، هنوز چند مشکل باقی مانده است. اول این‌که، پیام‌ها با فاصله‌های نیم‌ثانیه‌ای دریافت نمی‌شوند؛ بلکه همگی به‌طور هم‌زمان و پس از گذشت ۲ ثانیه (۲۰۰۰ میلی‌ثانیه) از شروع برنامه می‌رسند. مشکل دیگر این است که این برنامه هیچ‌گاه خاتمه نمی‌یابد! بلکه برای همیشه منتظر دریافت پیام‌های جدید باقی می‌ماند. برای متوقف‌کردن آن باید از <span class="keystroke">ctrl-c</span> استفاده کنید. + +بیایید با بررسی دلیل اینکه چرا پیام‌ها پس از تأخیر کامل به‌یک‌باره می‌آیند، شروع کنیم، به‌جای اینکه با تأخیر بین هرکدام ظاهر شوند. در یک بلوک async خاص، ترتیب ظاهر شدن کلمات کلیدی `await` در کد، همان ترتیبی است که هنگام اجرای برنامه اجرا می‌شوند. + +در فهرست 17-10 فقط یک بلوک async وجود دارد، بنابراین همه چیز در آن به‌صورت خطی اجرا می‌شود. هنوز هم هیچ همزمانی وجود ندارد. تمام فراخوانی‌های `tx.send` انجام می‌شوند، در میان تمام فراخوانی‌های `trpl::sleep` و نقاط انتظار مرتبط با آن‌ها. فقط پس از آن، حلقه `while let` به نقاط انتظار روی فراخوانی‌های `recv` می‌رسد. + +برای به دست آوردن رفتار مورد نظر، که در آن تأخیر خواب بین هر پیام رخ می‌دهد، باید عملیات‌های `tx` و `rx` را در بلوک‌های async جداگانه قرار دهیم، همان‌طور که در لیست ۱۷-۱۱ نشان داده شده است. سپس runtime می‌تواند هر یک از آن‌ها را جداگانه با استفاده از `trpl::join` اجرا کند، دقیقاً مانند مثال شمارش. بار دیگر، منتظر نتیجه فراخوانی `trpl::join` می‌مانیم، نه futures فردی. اگر به صورت ترتیبی برای futures فردی منتظر می‌ماندیم، دوباره به جریان ترتیبی بازمی‌گشتیم—دقیقاً چیزی که تلاش می‌کنیم _انجام ندهیم_. <!-- We cannot test this one because it never stops! --> -<Listing number="17-11" caption="Separating `send` and `recv` into their own `async` blocks and awaiting the futures for those blocks" file-name="src/main.rs"> +<Listing number="17-11" caption="جدا کردن `send` و `recv` در بلوک‌های `async` جداگانه و منتظر ماندن برای آینده‌های این بلوک‌ها" file-name="src/main.rs"> ```rust,ignore {{#rustdoc_include ../listings/ch17-async-await/listing-17-11/src/main.rs:futures}} @@ -305,47 +185,26 @@ flow—exactly what we’re trying _not_ to do. </Listing> -With the updated code in Listing 17-11, the messages get printed at -500-millisecond intervals, rather than all in a rush after 2 seconds. - -The program still never exits, though, because of the way the `while let` loop -interacts with `trpl::join`: - -- The future returned from `trpl::join` completes only once _both_ futures - passed to it have completed. -- The `tx` future completes once it finishes sleeping after sending the last - message in `vals`. -- The `rx` future won’t complete until the `while let` loop ends. -- The `while let` loop won’t end until awaiting `rx.recv` produces `None`. -- Awaiting `rx.recv` will return `None` only once the other end of the channel - is closed. -- The channel will close only if we call `rx.close` or when the sender side, - `tx`, is dropped. -- We don’t call `rx.close` anywhere, and `tx` won’t be dropped until the - outermost async block passed to `trpl::run` ends. -- The block can’t end because it is blocked on `trpl::join` completing, which - takes us back to the top of this list. - -We could manually close `rx` by calling `rx.close` somewhere, but that doesn’t -make much sense. Stopping after handling some arbitrary number of messages would -make the program shut down, but we could miss messages. We need some other way -to make sure that `tx` gets dropped _before_ the end of the function. - -Right now, the async block where we send the messages only borrows `tx` because -sending a message doesn’t require ownership, but if we could move `tx` into that -async block, it would be dropped once that block ends. In the Chapter 13 section -[Capturing References or Moving Ownership][capture-or-move]<!-- ignore -->, you -learned how to use the `move` keyword with closures, and, as discussed in the -Chapter 16 section [Using `move` Closures with Threads][move-threads]<!-- ignore --->, we often need to move data into closures when working with threads. The -same basic dynamics apply to async blocks, so the `move` keyword works with -async blocks just as it does with closures. - -In Listing 17-12, we change the block used to send messages from `async` to -`async move`. When we run _this_ version of the code, it shuts down gracefully -after the last message is sent and received. - -<Listing number="17-12" caption="A revision of the code from Listing 17-11 that correctly shuts down when complete" file-name="src/main.rs"> +با کد به‌روزرسانی‌شده در لیست ۱۷-۱۱، پیام‌ها با فواصل ۵۰۰ میلی‌ثانیه چاپ می‌شوند، به‌جای اینکه همه با عجله پس از ۲ ثانیه ظاهر شوند. + +برنامه هنوز هم هرگز خارج نمی‌شود، به دلیل نحوه تعامل حلقه `while let` با `trpl::join`: + +- Future بازگردانده‌شده از `trpl::join` تنها زمانی تکمیل می‌شود که _هر دو_ future ارسال‌شده به آن تکمیل شده باشند. +- Future مربوط به `tx` زمانی تکمیل می‌شود که پس از ارسال آخرین پیام در `vals` خوابیدن آن به پایان برسد. +- Future مربوط به `rx` تا زمانی که حلقه `while let` به پایان نرسد تکمیل نخواهد شد. +- حلقه `while let` تا زمانی که منتظر `rx.recv` باشد و مقدار `None` تولید شود، پایان نمی‌یابد. +- منتظر شدن برای `rx.recv` تنها زمانی مقدار `None` بازمی‌گرداند که طرف دیگر کانال بسته شود. +- کانال تنها در صورتی بسته می‌شود که `rx.close` را فراخوانی کنیم یا طرف فرستنده، یعنی `tx`، حذف شود. +- ما هیچ‌جا `rx.close` را فراخوانی نمی‌کنیم، و `tx` تا زمانی که بیرونی‌ترین بلوک async ارسال‌شده به `trpl::run` به پایان نرسد، حذف نمی‌شود. +- این بلوک نمی‌تواند به پایان برسد زیرا منتظر تکمیل شدن `trpl::join` است، که ما را دوباره به بالای این لیست بازمی‌گرداند. + +ما می‌توانیم به‌صورت دستی با فراخوانی `rx.close` کانال را ببندیم، اما این کار چندان منطقی نیست. توقف پس از پردازش تعداد دلخواهی از پیام‌ها باعث می‌شود برنامه خاموش شود، اما ممکن است پیام‌ها را از دست بدهیم. ما به راه دیگری نیاز داریم تا مطمئن شویم که `tx` _قبل از_ پایان تابع حذف می‌شود. + +در حال حاضر، بلوک async که پیام‌ها را ارسال می‌کند فقط `tx` را قرض می‌گیرد زیرا ارسال پیام نیاز به مالکیت ندارد، اما اگر می‌توانستیم `tx` را به داخل آن بلوک async منتقل کنیم، پس از پایان آن بلوک حذف می‌شد. در بخش فصل ۱۳ [گرفتن مراجع یا جابه‌جایی مالکیت][capture-or-move]<!-- ignore --> یاد گرفتید چگونه از کلمه کلیدی `move` با closures استفاده کنید، و همان‌طور که در بخش فصل ۱۶ [استفاده از closures `move` با Threadها][move-threads]<!-- ignore --> بحث شد، اغلب هنگام کار با Threadها نیاز داریم داده‌ها را به داخل closures منتقل کنیم. همان دینامیک‌های اساسی برای بلوک‌های async اعمال می‌شود، بنابراین کلمه کلیدی `move` با بلوک‌های async همان‌طور کار می‌کند که با closures کار می‌کند. + +در لیست ۱۷-۱۲، بلوک مورد استفاده برای ارسال پیام‌ها را از `async` به `async move` تغییر می‌دهیم. وقتی _این_ نسخه از کد را اجرا می‌کنیم، برنامه پس از ارسال و دریافت آخرین پیام به‌طور مرتب خاتمه می‌یابد. + +<Listing number="17-12" caption="نسخه بازبینی‌شده کد از لیست ۱۷-۱۱ که به‌درستی پس از اتمام خاتمه می‌یابد" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-12/src/main.rs:with-move}} @@ -353,11 +212,9 @@ after the last message is sent and received. </Listing> -This async channel is also a multiple-producer channel, so we can call `clone` -on `tx` if we want to send messages from multiple futures, as shown in Listing -17-13. +این کانال async همچنین یک کانال چند-تولیدی (_multiple-producer_) است، بنابراین اگر بخواهیم پیام‌ها را از چندین future ارسال کنیم، می‌توانیم `clone` را روی `tx` فراخوانی کنیم، همان‌طور که در لیست ۱۷-۱۳ نشان داده شده است. -<Listing number="17-13" caption="Using multiple producers with async blocks" file-name="src/main.rs"> +<Listing number="17-13" caption="استفاده از تولیدکنندگان متعدد با بلوک‌های async" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-13/src/main.rs:here}} @@ -365,21 +222,11 @@ on `tx` if we want to send messages from multiple futures, as shown in Listing </Listing> -First, we clone `tx`, creating `tx1` outside the first async block. We move -`tx1` into that block just as we did before with `tx`. Then, later, we move the -original `tx` into a _new_ async block, where we send more messages on a -slightly slower delay. We happen to put this new async block after the async -block for receiving messages, but it could go before it just as well. The key is -the order in which the futures are awaited, not in which they’re created. +ابتدا، `tx` را clone کرده و `tx1` را خارج از بلوک async اول ایجاد می‌کنیم. `tx1` را همانند قبل با `tx` به داخل آن بلوک منتقل می‌کنیم. سپس، در ادامه، `tx` اصلی را به یک بلوک _جدید_ async منتقل می‌کنیم، جایی که پیام‌های بیشتری با یک تأخیر کمی کندتر ارسال می‌کنیم. ما این بلوک async جدید را بعد از بلوک async برای دریافت پیام‌ها قرار می‌دهیم، اما می‌توانستیم به همان اندازه آن را قبل از آن قرار دهیم. نکته کلیدی ترتیب منتظر ماندن برای futures است، نه ترتیب ایجاد آن‌ها. -Both of the async blocks for sending messages need to be `async move` blocks so -that both `tx` and `tx1` get dropped when those blocks finish. Otherwise, we’ll -end up back in the same infinite loop we started out in. Finally, we switch from -`trpl::join` to `trpl::join3` to handle the additional future. +هر دو بلوک async برای ارسال پیام‌ها باید بلوک‌های `async move` باشند تا `tx` و `tx1` هر دو پس از پایان آن بلوک‌ها حذف شوند. در غیر این صورت، دوباره به همان حلقه بی‌نهایت اولیه بازمی‌گردیم. در نهایت، از `trpl::join` به `trpl::join3` تغییر می‌دهیم تا future اضافی را مدیریت کنیم. -Now we see all the messages from both sending futures, and because the sending -futures use slightly different delays after sending, the messages are also -received at those different intervals. +اکنون تمام پیام‌های هر دو future ارسال را می‌بینیم، و چون futures ارسال از تأخیرهای کمی متفاوت پس از ارسال استفاده می‌کنند، پیام‌ها نیز در این فواصل مختلف دریافت می‌شوند. <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the threads running differently rather than @@ -396,8 +243,7 @@ received 'for' received 'you' ``` -This is a good start, but it limits us to just a handful of futures: two with -`join`, or three with `join3`. Let’s see how we might work with more futures. +این یک شروع خوب است، اما ما را به تعداد محدودی از futures محدود می‌کند: دو عدد با `join` یا سه عدد با `join3`. بیایید ببینیم چگونه می‌توانیم با تعداد بیشتری از futures کار کنیم. [thread-spawn]: ch16-01-threads.html#creating-a-new-thread-with-spawn [join-handles]: ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles @@ -405,3 +251,4 @@ This is a good start, but it limits us to just a handful of futures: two with [if-let]: ch06-03-if-let.html [capture-or-move]: ch13-01-closures.html#capturing-references-or-moving-ownership [move-threads]: ch16-01-threads.html#using-move-closures-with-threads + diff --git a/src/ch17-03-more-futures.md b/src/ch17-03-more-futures.md index 10bfdf1b62..268eef5fc0 100644 --- a/src/ch17-03-more-futures.md +++ b/src/ch17-03-more-futures.md @@ -1,14 +1,9 @@ -## Working with Any Number of Futures +## کار با تعداد دلخواهی از Futures -When we switched from using two futures to three in the previous section, we -also had to switch from using `join` to using `join3`. It would be annoying to -have to call a different function every time we changed the number of futures we -wanted to join. Happily, we have a macro form of `join` to which we can pass an -arbitrary number of arguments. It also handles awaiting the futures itself. -Thus, we could rewrite the code from Listing 17-13 to use `join!` instead of -`join3`, as in Listing 17-14. -<Listing number="17-14" caption="Using `join!` to wait for multiple futures" file-name="src/main.rs"> +وقتی در بخش قبلی از استفاده از دو future به سه future تغییر دادیم، مجبور شدیم به جای استفاده از `join` از `join3` استفاده کنیم. این مسئله آزاردهنده خواهد بود اگر هر بار که تعداد futuresی که می‌خواهیم join کنیم تغییر می‌کند، مجبور به فراخوانی یک تابع متفاوت باشیم. خوشبختانه، یک فرم ماکروی `join` داریم که می‌توانیم به آن تعداد دلخواهی از آرگومان‌ها را ارسال کنیم. این ماکرو همچنین خودش مدیریت انتظار برای futures را انجام می‌دهد. بنابراین، می‌توانیم کد لیست ۱۷-۱۳ را بازنویسی کنیم تا به جای `join3` از `join!` استفاده کنیم، همان‌طور که در لیست ۱۷-۱۴ نشان داده شده است. + +<Listing number="17-14" caption="استفاده از `join!` برای منتظر ماندن چندین آینده" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-14/src/main.rs:here}} @@ -16,20 +11,16 @@ Thus, we could rewrite the code from Listing 17-13 to use `join!` instead of </Listing> -This is definitely an improvement over swapping between `join` and -`join3` and `join4` and so on! However, even this macro form only works -when we know the number of futures ahead of time. In real-world Rust, -though, pushing futures into a collection and then waiting on some or -all the futures of them to complete is a common pattern. +این قطعاً نسبت به جابجایی بین `join`، `join3`، `join4` و موارد دیگر بهبود یافته است! با این حال، حتی این فرم ماکرو نیز فقط زمانی کار می‌کند که تعداد futures را از قبل بدانیم. اما در دنیای واقعی Rust، اضافه کردن futures به یک مجموعه و سپس انتظار برای کامل شدن برخی یا تمام آن‌ها یک الگوی رایج است. -To check all the futures in some collection, we’ll need to iterate over and -join on _all_ of them. The `trpl::join_all` function accepts any type that -implements the `Iterator` trait, which you learned about back in [The Iterator -Trait and the `next` Method][iterator-trait]<!-- ignore --> Chapter 13, so -it seems like just the ticket. Let’s try putting our futures in a vector and -replacing `join!` with `join_all` as shown in Listing 17-15. +برای بررسی همه‌ی futureها در یک مجموعه، باید روی همه‌ی آن‌ها پیمایش کنیم و روی همه join کنیم. +تابع `trpl::join_all` هر نوعی را می‌پذیرد که trait `Iterator` را پیاده‌سازی کرده باشد، +که در فصل ۱۳ در بخش [trait پیمایشگر و متد `next`][iterator-trait] درباره‌ی آن آموختید، +پس به نظر می‌رسد که دقیقاً مناسب باشد. +بیایید futureهایمان را در یک بردار قرار دهیم و `join!` را با `join_all` جایگزین کنیم، +همان‌طور که در لیستینگ 17-15 نشان داده شده است. -<Listing number="17-15" caption="Storing anonymous futures in a vector and calling `join_all`"> +<Listing number="17-15" caption="ذخیره آینده‌های ناشناس در یک بردار و فراخوانی `join_all`"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch17-async-await/listing-17-15/src/main.rs:here}} @@ -37,7 +28,7 @@ replacing `join!` with `join_all` as shown in Listing 17-15. </Listing> -Unfortunately, this code doesn’t compile. Instead, we get this error: +متأسفانه، این کد کامپایل نمی‌شود. در عوض، با این خطا مواجه می‌شویم: <!-- manual-regeneration cd listings/ch17-async-await/listing-17-15/ @@ -64,30 +55,22 @@ error[E0308]: mismatched types = help: consider pinning your async block and casting it to a trait object ``` -This might be surprising. After all, none of the async blocks returns anything, -so each one produces a `Future<Output = ()>`. Remember that `Future` is a trait, -though, and that the compiler creates a unique enum for each async block. You -can’t put two different hand-written structs in a `Vec`, and the same rule -applies to the different enums generated by the compiler. +این ممکن است شگفت‌آور باشد. بالاخره، هیچ‌یک از بلوک‌های async چیزی بازنمی‌گردانند، بنابراین هر کدام یک `Future<Output = ()>` تولید می‌کنند. اما به یاد داشته باشید که `Future` یک ویژگی (_trait_) است و کامپایلر برای هر بلوک async یک enum منحصربه‌فرد ایجاد می‌کند. نمی‌توانید دو struct مختلف را که دستی نوشته شده‌اند در یک `Vec` قرار دهید، و همین قانون برای enumهای مختلفی که توسط کامپایلر تولید می‌شوند اعمال می‌شود. + -To make this work, we need to use _trait objects_, just as we did in [“Returning -Errors from the run function”][dyn]<!-- ignore --> in Chapter 12. (We’ll cover -trait objects in detail in Chapter 18.) Using trait objects lets us treat each -of the anonymous futures produced by these types as the same type, because all -of them implement the `Future` trait. +> توجه: در بخش [استفاده از یک enum برای نگهداری چند مقدار][enum-alt]<!-- ignore --> در فصل ۸، +> روش دیگری برای گنجاندن چند نوع مختلف در یک `Vec` را بررسی کردیم: +> استفاده از یک `enum` برای نمایش هر نوعی که ممکن است در بردار وجود داشته باشد. +> اما در اینجا نمی‌توانیم این کار را انجام دهیم. +> اولاً، هیچ راهی برای نام‌گذاری نوع‌های مختلف نداریم چون آن‌ها ناشناس (anonymous) هستند. +> ثانیاً، دلیل اصلی استفاده‌ی ما از بردار و `join_all` این بود که بتوانیم با مجموعه‌ای پویا از futureها کار کنیم، +> جایی که تنها مهم است همه خروجی‌های آن‌ها یکسان باشند. -> Note: In [Using an Enum to Store Multiple Values][enum-alt]<!-- ignore --> in -> Chapter 8, we discussed another way to include multiple types in a `Vec`: -> using an enum to represent each type that can appear in the vector. We can’t -> do that here, though. For one thing, we have no way to name the different -> types, because they are anonymous. For another, the reason we reached for a -> vector and `join_all` in the first place was to be able to work with a dynamic -> collection of futures where we only care that they have the same output type. +> نکته: در بخش فصل ۸ [استفاده از یک Enum برای ذخیره مقادیر متعدد][enum-alt]<!-- ignore -->، درباره یک روش دیگر برای شامل کردن چندین نوع در یک `Vec` صحبت کردیم: استفاده از یک enum برای نمایش هر نوعی که می‌تواند در وکتور ظاهر شود. اما نمی‌توانیم اینجا از آن استفاده کنیم. از یک طرف، هیچ راهی برای نام‌گذاری انواع مختلف نداریم، زیرا آن‌ها ناشناس هستند. از طرف دیگر، دلیلی که ما در وهله اول به دنبال یک وکتور و `join_all` رفتیم، این بود که بتوانیم با یک مجموعه پویا از futures کار کنیم، جایی که فقط به این اهمیت می‌دهیم که همه آن‌ها خروجی یکسانی دارند. -We start by wrapping each future in the `vec!` in a `Box::new`, as shown in -Listing 17-16. +ابتدا هر future درون `vec!` را در یک `Box::new` بسته‌بندی می‌کنیم، همان‌طور که در لیست ۱۷-۱۶ نشان داده شده است. -<Listing number="17-16" caption="Using `Box::new` to align the types of the futures in a `Vec`" file-name="src/main.rs"> +<Listing number="17-16" caption="استفاده از `Box::new` برای تطبیق انواع futures در یک `Vec`" file-name="src/main.rs"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch17-async-await/listing-17-16/src/main.rs:here}} @@ -95,13 +78,9 @@ Listing 17-16. </Listing> -Unfortunately, this code still doesn’t compile. In fact, we get the same basic -error we got before for both the second and third `Box::new` calls, as well as -new errors referring to the `Unpin` trait. We’ll come back to the `Unpin` errors -in a moment. First, let’s fix the type errors on the `Box::new` calls by -explicitly annotating the type of the `futures` variable (see Listing 17-17). +متأسفانه، این کد هنوز هم کامپایل نمی‌شود. در واقع، همان خطای پایه‌ای که قبلاً دریافت کردیم، برای فراخوانی‌های دوم و سوم `Box::new` نیز رخ می‌دهد، به همراه خطاهای جدیدی که به ویژگی `Unpin` اشاره دارند. به زودی به خطاهای مرتبط با `Unpin` بازمی‌گردیم. ابتدا، بیایید خطاهای نوع در فراخوانی‌های `Box::new` را با مشخص کردن صریح نوع متغیر `futures` رفع کنیم (نگاه کنید به لیست ۱۷-۱۷). -<Listing number="17-17" caption="Fixing the rest of the type mismatch errors by using an explicit type declaration" file-name="src/main.rs"> +<Listing number="17-17" caption="برطرف کردن بقیه خطاهای ناسازگاری نوع با استفاده از اعلان صریح نوع" file-name="src/main.rs"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch17-async-await/listing-17-17/src/main.rs:here}} @@ -109,18 +88,14 @@ explicitly annotating the type of the `futures` variable (see Listing 17-17). </Listing> -This type declaration is a little involved, so let’s walk through it: +این type declaration کمی پیچیده است، بنابراین بیایید آن را مرحله به مرحله بررسی کنیم: -1. The innermost type is the future itself. We note explicitly that the output - of the future is the unit type `()` by writing `Future<Output = ()>`. -2. Then we annotate the trait with `dyn` to mark it as dynamic. -3. The entire trait reference is wrapped in a `Box`. -4. Finally, we state explicitly that `futures` is a `Vec` containing these - items. +1. نوع داخلی‌ترین، خود future است. به‌طور صریح اعلام می‌کنیم که خروجی future نوع واحد `()` است، با نوشتن `Future<Output = ()>`. +2. سپس ویژگی را با `dyn` علامت‌گذاری می‌کنیم تا به‌صورت دینامیک باشد. +3. کل مرجع ویژگی در یک `Box` بسته‌بندی می‌شود. +4. در نهایت، به‌طور صریح بیان می‌کنیم که `futures` یک `Vec` است که شامل این آیتم‌ها است. -That already made a big difference. Now when we run the compiler, we get only -the errors mentioning `Unpin`. Although there are three of them, their contents -are very similar. +این تغییر تأثیر قابل‌توجهی داشت. اکنون وقتی کامپایلر را اجرا می‌کنیم، فقط خطاهایی که به `Unpin` اشاره دارند باقی می‌مانند. اگرچه سه خطا وجود دارد، اما محتوای آن‌ها بسیار مشابه است. <!-- manual-regeneration cd listings/ch17-async-await/listing-17-17 @@ -190,16 +165,17 @@ For more information about this error, try `rustc --explain E0277`. error: could not compile `async_await` (bin "async_await") due to 3 previous errors ``` -That is a _lot_ to digest, so let’s pull it apart. The first part of the message -tell us that the first async block (`src/main.rs:8:23: 20:10`) does not -implement the `Unpin` trait and suggests using `pin!` or `Box::pin` to resolve -it. Later in the chapter, we’ll dig into a few more details about `Pin` and -`Unpin`. For the moment, though, we can just follow the compiler’s advice to get -unstuck. In Listing 17-18, we start by importing `Pin` from `std::pin`. Next we -update the type annotation for `futures`, with a `Pin` wrapping each `Box`. -Finally, we use `Box::pin` to pin the futures themselves. +این پیام حجم زیادی از اطلاعات را دارد، پس بیایید آن را بخش‌بندی کنیم. +بخش اول پیام می‌گوید که اولین بلاک async (`src/main.rs:8:23: 20:10`) trait `Unpin` را پیاده‌سازی نکرده است +و پیشنهاد می‌کند برای رفع این مشکل از `pin!` یا `Box::pin` استفاده کنیم. +در ادامه‌ی فصل، به جزئیات بیشتری درباره‌ی `Pin` و `Unpin` خواهیم پرداخت. +فعلاً می‌توانیم فقط از توصیه‌ی کامپایلر پیروی کنیم تا مشکل برطرف شود. +در لیستینگ 17-18، ابتدا `Pin` را از `std::pin` وارد می‌کنیم. +سپس نوع `futures` را به‌روزرسانی می‌کنیم، به‌طوری که هر `Box` داخل یک `Pin` قرار گیرد. +در نهایت، از `Box::pin` برای pin کردن خود futureها استفاده می‌کنیم. -<Listing number="17-18" caption="Using `Pin` and `Box::pin` to make the `Vec` type check" file-name="src/main.rs"> + +<Listing number="17-18" caption="استفاده از `Pin` و `Box::pin` برای برطرف کردن نوع `Vec`" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-18/src/main.rs:here}} @@ -207,7 +183,7 @@ Finally, we use `Box::pin` to pin the futures themselves. </Listing> -If we compile and run this, we finally get the output we hoped for: +اگر این کد را کامپایل و اجرا کنیم، در نهایت خروجی موردنظر خود را دریافت می‌کنیم: <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the threads running differently rather than @@ -224,25 +200,21 @@ received 'future' received 'you' ``` -Phew! +آه! + +اینجا چیزهای بیشتری برای بررسی وجود دارد. برای یک مورد، استفاده از `Pin<Box<T>>` یک مقدار کمی سربار اضافه می‌کند، زیرا این futures را با `Box` روی heap قرار می‌دهیم—و ما فقط این کار را برای هم‌تراز کردن انواع انجام می‌دهیم. بعد از همه این‌ها، ما _واقعاً_ نیازی به تخصیص heap نداریم: این futures به این تابع خاص محدود هستند. همان‌طور که قبلاً ذکر شد، `Pin` خودش یک نوع wrapper است، بنابراین می‌توانیم از مزیت داشتن یک نوع واحد در `Vec` بهره‌مند شویم—دلیل اصلی که به دنبال `Box` رفتیم—بدون انجام تخصیص heap. می‌توانیم مستقیماً از `Pin` با هر future استفاده کنیم، با استفاده از ماکروی `std::pin::pin`. -There’s a bit more to explore here. For one thing, using `Pin<Box<T>>` adds a -small amount of overhead from putting these futures on the heap with `Box`—and -we’re only doing that to get the types to line up. We don’t actually _need_ the -heap allocation, after all: these futures are local to this particular function. -As noted before, `Pin` is itself a wrapper type, so we can get the benefit of -having a single type in the `Vec`—the original reason we reached for -`Box`—without doing a heap allocation. We can use `Pin` directly with each -future, using the `std::pin::pin` macro. +با این حال، همچنان باید به‌صورت صریح نوع رفرنس پین‌شده را مشخص کنیم؛ +در غیر این صورت، Rust نمی‌داند که این‌ها باید به عنوان trait objectهای داینامیک تفسیر شوند، +که این همان چیزی است که در `Vec` به آن نیاز داریم. +بنابراین، `pin` را به لیست واردات‌مان از `std::pin` اضافه می‌کنیم. +سپس می‌توانیم هر future را هنگام تعریف آن با `pin!` پین کنیم +و `futures` را به‌صورت یک `Vec` شامل رفرنس‌های mutable پین‌شده به نوع dynamic future تعریف کنیم، +همان‌طور که در لیستینگ 17-19 نشان داده شده است. -However, we must still be explicit about the type of the pinned reference; -otherwise, Rust will still not know to interpret these as dynamic trait objects, -which is what we need them to be in the `Vec`. We therefore add `pin` to our -list of imports from `std::pin`. Then we can `pin!` each future when we define -it and define `futures` as a `Vec` containing pinned mutable references to the -dynamic future type, as in Listing 17-19. +با این حال، باید به‌صراحت نوع مرجع pinned را مشخص کنیم؛ در غیر این صورت، راست همچنان نمی‌داند که این‌ها را به‌عنوان شیءهای ویژگی دینامیک تفسیر کند، که همان چیزی است که برای قرار گرفتن در `Vec` نیاز داریم. بنابراین، هر آینده را وقتی تعریف می‌کنیم `pin!` می‌کنیم و `futures` را به‌عنوان یک `Vec` که شامل مراجع متغیر pinned به نوع ویژگی دینامیک `Future` است تعریف می‌کنیم، همانطور که در فهرست 17-19 نشان داده شده است. -<Listing number="17-19" caption="Using `Pin` directly with the `pin!` macro to avoid unnecessary heap allocations" file-name="src/main.rs"> +<Listing number="17-19" caption="استفاده مستقیم از `Pin` با ماکروی `pin!` برای اجتناب از تخصیص‌های غیرضروری heap" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-19/src/main.rs:here}} @@ -250,12 +222,9 @@ dynamic future type, as in Listing 17-19. </Listing> -We got this far by ignoring the fact that we might have different `Output` -types. For example, in Listing 17-20, the anonymous future for `a` implements -`Future<Output = u32>`, the anonymous future for `b` implements `Future<Output = -&str>`, and the anonymous future for `c` implements `Future<Output = bool>`. +تا اینجا با نادیده گرفتن این واقعیت که ممکن است نوع‌های `Output` مختلفی داشته باشیم، پیش رفتیم. برای مثال، در فهرست 17-20، آینده ناشناس برای `a` ویژگی `Future<Output = u32>` را پیاده‌سازی می‌کند، آینده ناشناس برای `b` ویژگی `Future<Output = &str>` را پیاده‌سازی می‌کند، و آینده ناشناس برای `c` ویژگی `Future<Output = bool>` را پیاده‌سازی می‌کند. -<Listing number="17-20" caption="Three futures with distinct types" file-name="src/main.rs"> +<Listing number="17-20" caption="سه آینده با نوع‌های متفاوت" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-20/src/main.rs:here}} @@ -263,30 +232,17 @@ types. For example, in Listing 17-20, the anonymous future for `a` implements </Listing> -We can use `trpl::join!` to await them, because it allows us to pass in multiple -future types and produces a tuple of those types. We _cannot_ use -`trpl::join_all`, because it requires all of the futures passed in to have the -same type. Remember, that error is what got us started on this adventure with -`Pin`! +می‌توانیم از `trpl::join!` برای منتظر ماندن استفاده کنیم، زیرا به ما اجازه می‌دهد چندین نوع future را ارسال کنیم و یک tuple از آن انواع تولید می‌کند. اما _نمی‌توانیم_ از `trpl::join_all` استفاده کنیم، زیرا این تابع نیاز دارد که همه futures ارسال‌شده نوع یکسانی داشته باشند. به یاد داشته باشید، همین خطا بود که ما را به این ماجراجویی با `Pin` کشاند! -This is a fundamental tradeoff: we can either deal with a dynamic number of -futures with `join_all`, as long as they all have the same type, or we can deal -with a set number of futures with the `join` functions or the `join!` macro, -even if they have different types. This is the same scenario we’d face when -working with any other types in Rust. Futures are not special, even though we -have some nice syntax for working with them, and that’s a good thing. +این یک معاوضه بنیادی است: می‌توانیم با تعداد پویایی از futures با استفاده از `join_all` کار کنیم، به شرطی که همه آن‌ها نوع یکسانی داشته باشند، یا می‌توانیم با تعداد مشخصی از futures با توابع `join` یا ماکروی `join!` کار کنیم، حتی اگر آن‌ها انواع مختلفی داشته باشند. این همان شرایطی است که هنگام کار با هر نوع دیگری در Rust با آن مواجه می‌شویم. Futures خاص نیستند، حتی اگر سینتکس مناسبی برای کار با آن‌ها داشته باشیم، و این یک نکته مثبت است. ### Racing Futures -When we “join” futures with the `join` family of functions and macros, we -require _all_ of them to finish before we move on. Sometimes, though, we only -need _some_ future from a set to finish before we move on—kind of similar to -racing one future against another. +وقتی آینده‌ها را با خانواده توابع و ماکروهای `join` "منتظر می‌مانیم"، نیاز داریم _همه_ آن‌ها تمام شوند قبل از اینکه به مرحله بعدی برویم. گاهی اوقات، اما، فقط نیاز داریم _یکی_ از آینده‌ها از مجموعه‌ای تمام شود قبل از اینکه به مرحله بعدی برویم—کمی شبیه به مسابقه دادن یک آینده در برابر دیگری. -In Listing 17-21, we once again use `trpl::race` to run two futures, `slow` and -`fast`, against each other. +در لیست ۱۷-۲۱، ما دوباره از `trpl::race` استفاده می‌کنیم تا دو future، یعنی `slow` و `fast`، را در برابر یکدیگر اجرا کنیم. -<Listing number="17-21" caption="Using `race` to get the result of whichever future finishes first" file-name="src/main.rs"> +<Listing number="17-21" caption="استفاده از `race` برای دریافت نتیجه اولین آینده‌ای که تمام می‌شود" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-21/src/main.rs:here}} @@ -294,42 +250,17 @@ In Listing 17-21, we once again use `trpl::race` to run two futures, `slow` and </Listing> -Each future prints a message when it starts running, pauses for some amount of -time by calling and awaiting `sleep`, and then prints another message when it -finishes. Then we pass both `slow` and `fast` to `trpl::race` and wait for one -of them to finish. (The outcome here isn’t too surprising: `fast` wins.) Unlike -when we used `race` back in [“Our First Async Program”][async-program]<!-- -ignore -->, we just ignore the `Either` instance it returns here, because all of -the interesting behavior happens in the body of the async blocks. - -Notice that if you flip the order of the arguments to `race`, the order of the -“started” messages changes, even though the `fast` future always completes -first. That’s because the implementation of this particular `race` function is -not fair. It always runs the futures passed in as arguments in the order in -which they’re passed. Other implementations _are_ fair and will randomly choose -which future to poll first. Regardless of whether the implementation of race -we’re using is fair, though, _one_ of the futures will run up to the first -`await` in its body before another task can start. - -Recall from [Our First Async Program][async-program]<!-- ignore --> that at each -await point, Rust gives a runtime a chance to pause the task and switch to -another one if the future being awaited isn’t ready. The inverse is also true: -Rust _only_ pauses async blocks and hands control back to a runtime at an await -point. Everything between await points is synchronous. - -That means if you do a bunch of work in an async block without an await point, -that future will block any other futures from making progress. You may sometimes -hear this referred to as one future _starving_ other futures. In some cases, -that may not be a big deal. However, if you are doing some kind of expensive -setup or long-running work, or if you have a future that will keep doing some -particular task indefinitely, you’ll need to think about when and where to hand -control back to the runtime. - -By the same token, if you have long-running blocking operations, async can be a -useful tool for providing ways for different parts of the program to relate to -each other. - -But _how_ would you hand control back to the runtime in those cases? +هر future یک پیام هنگام شروع اجرا چاپ می‌کند، با فراخوانی و انتظار برای `sleep` به مدت مشخصی مکث می‌کند، و سپس یک پیام دیگر هنگام اتمام چاپ می‌کند. سپس، هر دو future یعنی `slow` و `fast` را به `trpl::race` ارسال می‌کنیم و منتظر می‌مانیم تا یکی از آن‌ها به پایان برسد. (نتیجه اینجا چندان شگفت‌آور نیست: `fast` برنده می‌شود.) برخلاف زمانی که در [“اولین برنامه Async ما”][async-program]<!-- ignore --> از `race` استفاده کردیم، اینجا به نمونه `Either` که بازمی‌گرداند توجه نمی‌کنیم، زیرا تمام رفتار جالب در بدنه بلوک‌های async رخ می‌دهد. + +توجه کنید که اگر ترتیب آرگومان‌ها به `race` را جابه‌جا کنید، ترتیب پیام‌های "started" تغییر می‌کند، حتی اگر future `fast` همیشه زودتر به پایان برسد. دلیل این است که پیاده‌سازی این تابع خاص `race` منصفانه نیست. این تابع همیشه futures ارسال‌شده را به ترتیب آرگومان‌ها اجرا می‌کند. سایر پیاده‌سازی‌ها _منصفانه_ هستند و به صورت تصادفی انتخاب می‌کنند که کدام future را ابتدا poll کنند. با این حال، صرف‌نظر از اینکه پیاده‌سازی `race` ما منصفانه باشد یا نه، _یکی_ از futures تا اولین `await` در بدنه‌اش اجرا می‌شود قبل از اینکه task دیگری بتواند شروع شود. + +به یاد بیاورید از [اولین برنامه Async ما][async-program]<!-- ignore --> که در هر نقطه `await`، Rust به runtime اجازه می‌دهد تا task را متوقف کند و به task دیگری سوئیچ کند اگر future در حال انتظار آماده نباشد. عکس این موضوع هم صادق است: Rust فقط بلوک‌های async را متوقف می‌کند و کنترل را به runtime بازمی‌گرداند در یک نقطه `await`. + +این بدان معناست که اگر در یک بلوک async بدون نقطه `await` مقدار زیادی کار انجام دهید، آن future دیگر futures را از پیشرفت باز می‌دارد. گاهی اوقات ممکن است به این موضوع اشاره شود که یک future _دیگر futures را گرسنه می‌کند_. در برخی موارد، این ممکن است مشکل بزرگی نباشد. با این حال، اگر در حال انجام برخی تنظیمات پرهزینه یا کار طولانی‌مدت هستید، یا اگر futureای دارید که به طور نامحدود یک کار خاص را انجام می‌دهد، باید به این فکر کنید که چه زمانی و کجا کنترل را به runtime بازگردانید. + +به همان اندازه، اگر عملیات‌های مسدودکننده طولانی‌مدت دارید، async می‌تواند ابزاری مفید برای ارائه راه‌هایی باشد که بخش‌های مختلف برنامه بتوانند با یکدیگر تعامل داشته باشند. + +اما در این موارد _چگونه_ کنترل را به runtime بازمی‌گردانید؟ <!-- Old headings. Do not remove or links may break. --> @@ -337,10 +268,9 @@ But _how_ would you hand control back to the runtime in those cases? ### Yielding Control to the Runtime -Let’s simulate a long-running operation. Listing 17-22 introduces a `slow` -function. +بیایید یک عملیات طولانی‌مدت را شبیه‌سازی کنیم. لیست ۱۷-۲۲ یک تابع به نام `slow` معرفی می‌کند. -<Listing number="17-22" caption="Using `thread::sleep` to simulate slow operations" file-name="src/main.rs"> +<Listing number="17-22" caption="استفاده از `thread::sleep` برای شبیه‌سازی عملیات کند" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-22/src/main.rs:slow}} @@ -348,15 +278,11 @@ function. </Listing> -This code uses `std::thread::sleep` instead of `trpl::sleep` so that calling -`slow` will block the current thread for some number of milliseconds. We can use -`slow` to stand in for real-world operations that are both long-running and -blocking. +این کد از `std::thread::sleep` به جای `trpl::sleep` استفاده می‌کند، به طوری که فراخوانی `slow`، Thread فعلی را برای مدت مشخصی از میلی‌ثانیه‌ها مسدود می‌کند. می‌توانیم از `slow` به عنوان جایگزینی برای عملیات‌های واقعی که هم طولانی‌مدت هستند و هم مسدودکننده، استفاده کنیم. -In Listing 17-23, we use `slow` to emulate doing this kind of CPU-bound work in -a pair of futures. +در لیست ۱۷-۲۳، از `slow` برای شبیه‌سازی انجام این نوع کارهای CPU-bound در یک جفت future استفاده می‌کنیم. -<Listing number="17-23" caption="Using `thread::sleep` to simulate slow operations" file-name="src/main.rs"> +<Listing number="17-23" caption="استفاده از `thread::sleep` برای شبیه‌سازی عملیات کند" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-23/src/main.rs:slow-futures}} @@ -364,8 +290,7 @@ a pair of futures. </Listing> -To begin, each future only hands control back to the runtime _after_ carrying -out a bunch of slow operations. If you run this code, you will see this output: +برای شروع، هر future فقط _پس از_ انجام یک سری عملیات کند، کنترل را به runtime بازمی‌گرداند. اگر این کد را اجرا کنید، این خروجی را مشاهده خواهید کرد: <!-- manual-regeneration cd listings/ch17-async-await/listing-17-23/ @@ -386,21 +311,11 @@ copy just the output 'a' finished. ``` -As with our earlier example, `race` still finishes as soon as `a` is done. -There’s no interleaving between the two futures, though. The `a` future does all -of its work until the `trpl::sleep` call is awaited, then the `b` future does -all of its work until its own `trpl::sleep` call is awaited, and finally the `a` -future completes. To allow both futures to make progress between their slow -tasks, we need await points so we can hand control back to the runtime. That -means we need something we can await! +همان‌طور که در مثال قبلی دیدیم، `race` همچنان به محض اینکه `a` تمام شود، کار را تمام می‌کند. اما بین دو future هیچ تداخل یا جابه‌جایی وجود ندارد. future `a` تمام کار خود را انجام می‌دهد تا زمانی که فراخوانی `trpl::sleep` منتظر بماند، سپس future `b` تمام کار خود را انجام می‌دهد تا زمانی که فراخوانی `trpl::sleep` خودش منتظر بماند، و در نهایت future `a` کامل می‌شود. برای اینکه هر دو future بتوانند بین taskهای کند خود پیشرفت کنند، به نقاط `await` نیاز داریم تا بتوانیم کنترل را به runtime بازگردانیم. این به این معناست که به چیزی نیاز داریم که بتوانیم برای آن منتظر بمانیم! -We can already see this kind of handoff happening in Listing 17-23: if we -removed the `trpl::sleep` at the end of the `a` future, it would complete -without the `b` future running _at all_. Let’s try using the `sleep` function as -a starting point for letting operations switch off making progress, as shown in -Listing 17-24. +هم‌اکنون می‌توانیم این نوع انتقال کنترل را در لیست ۱۷-۲۳ مشاهده کنیم: اگر `trpl::sleep` در انتهای future `a` را حذف کنیم، این future بدون اجرای future `b` به طور کامل به پایان می‌رسد. بیایید از تابع `sleep` به‌عنوان نقطه شروعی برای اجازه دادن به عملیات‌ها برای جابه‌جا شدن و پیشرفت استفاده کنیم، همان‌طور که در لیست ۱۷-۲۴ نشان داده شده است. -<Listing number="17-24" caption="Using `sleep` to let operations switch off making progress" file-name="src/main.rs"> +<Listing number="17-24" caption="استفاده از `sleep` برای اجازه دادن به عملیات‌ها برای پیشرفت متناوب" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-24/src/main.rs:here}} @@ -408,8 +323,7 @@ Listing 17-24. </Listing> -In Listing 17-24, we add `trpl::sleep` calls with await points between each call -to `slow`. Now the two futures’ work is interleaved: +در فهرست 17-24، فراخوانی‌های `trpl::sleep` با نقاط انتظار بین هر فراخوانی به `slow` اضافه می‌کنیم. اکنون کار دو آینده درهم‌تنیده شده است: <!-- manual-regeneration cd listings/ch17-async-await/listing-17-24 @@ -429,18 +343,11 @@ copy just the output 'a' finished. ``` -The `a` future still runs for a bit before handing off control to `b`, because -it calls `slow` before ever calling `trpl::sleep`, but after that the futures -swap back and forth each time one of them hits an await point. In this case, we -have done that after every call to `slow`, but we could break up the work in -whatever way makes the most sense to us. +future `a` هنوز برای مدتی اجرا می‌شود قبل از اینکه کنترل را به `b` منتقل کند، زیرا ابتدا `slow` را فراخوانی می‌کند قبل از اینکه `trpl::sleep` را فراخوانی کند. اما پس از آن، futures هر بار که یکی از آن‌ها به یک نقطه `await` می‌رسد، به صورت متناوب جابه‌جا می‌شوند. در این مورد، ما این کار را پس از هر فراخوانی به `slow` انجام داده‌ایم، اما می‌توانستیم کار را به هر شکلی که برای ما منطقی‌تر است تقسیم کنیم. -We don’t really want to _sleep_ here, though: we want to make progress as fast -as we can. We just need to hand back control to the runtime. We can do that -directly, using the `yield_now` function. In Listing 17-25, we replace all those -`sleep` calls with `yield_now`. +با این حال، واقعاً نمی‌خواهیم اینجا _sleep_ کنیم؛ می‌خواهیم به سریع‌ترین شکلی که می‌توانیم پیشرفت کنیم. فقط نیاز داریم کنترل را به runtime بازگردانیم. می‌توانیم این کار را به‌طور مستقیم با استفاده از تابع `yield_now` انجام دهیم. در فهرست 17-25، تمام این فراخوانی‌های `sleep` را با `yield_now` جایگزین می‌کنیم. -<Listing number="17-25" caption="Using `yield_now` to let operations switch off making progress" file-name="src/main.rs"> +<Listing number="17-25" caption="استفاده از `yield_now` برای اجازه دادن به عملیات‌ها برای پیشرفت متناوب" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-25/src/main.rs:yields}} @@ -448,18 +355,11 @@ directly, using the `yield_now` function. In Listing 17-25, we replace all those </Listing> -This code is both clearer about the actual intent and can be significantly -faster than using `sleep`, because timers such as the one used by `sleep` often -have limits on how granular they can be. The version of `sleep` we are using, -for example, will always sleep for at least a millisecond, even if we pass it a -`Duration` of one nanosecond. Again, modern computers are _fast_: they can do a -lot in one millisecond! +این کد هم از نظر بیان هدف واقعی واضح‌تر است و هم می‌تواند به طور قابل‌توجهی سریع‌تر از استفاده از `sleep` باشد، زیرا تایمرهایی مانند آنچه که توسط `sleep` استفاده می‌شود اغلب محدودیت‌هایی در دقت خود دارند. نسخه‌ای از `sleep` که ما استفاده می‌کنیم، برای مثال، همیشه حداقل به مدت یک میلی‌ثانیه می‌خوابد، حتی اگر یک `Duration` یک نانوثانیه‌ای به آن بدهیم. دوباره، کامپیوترهای مدرن _سریع_ هستند: آن‌ها می‌توانند در یک میلی‌ثانیه کارهای زیادی انجام دهند! -You can see this for yourself by setting up a little benchmark, such as the one -in Listing 17-26. (This isn’t an especially rigorous way to do performance -testing, but it suffices to show the difference here.) +می‌توانید خودتان این را ببینید با راه‌اندازی یک بنچمارک کوچک، مانند آنچه در لیست ۱۷-۲۶ نشان داده شده است. (این روش به‌ویژه دقیقی برای انجام تست عملکرد نیست، اما برای نشان دادن تفاوت در اینجا کافی است.) -<Listing number="17-26" caption="Comparing the performance of `sleep` and `yield_now`" file-name="src/main.rs"> +<Listing number="17-26" caption="مقایسه عملکرد `sleep` و `yield_now`" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-26/src/main.rs:here}} @@ -467,65 +367,40 @@ testing, but it suffices to show the difference here.) </Listing> -Here, we skip all the status printing, pass a one-nanosecond `Duration` to -`trpl::sleep`, and let each future run by itself, with no switching between the -futures. Then we run for 1,000 iterations and see how long the future using -`trpl::sleep` takes compared to the future using `trpl::yield_now`. +در اینجا، تمام چاپ وضعیت را کنار می‌گذاریم، یک `Duration` یک نانوثانیه‌ای به `trpl::sleep` می‌دهیم و اجازه می‌دهیم هر future به‌صورت مستقل اجرا شود، بدون هیچ جابه‌جایی بین futures. سپس ۱,۰۰۰ بار این عملیات را تکرار می‌کنیم و می‌بینیم که futureی که از `trpl::sleep` استفاده می‌کند در مقایسه با futureی که از `trpl::yield_now` استفاده می‌کند چقدر زمان می‌برد. -The version with `yield_now` is _way_ faster! +نسخه‌ای که از `yield_now` استفاده می‌کند، _بسیار_ سریع‌تر است! -This means that async can be useful even for compute-bound tasks, depending on -what else your program is doing, because it provides a useful tool for -structuring the relationships between different parts of the program. This is a -form of _cooperative multitasking_, where each future has the power to determine -when it hands over control via await points. Each future therefore also has the -responsibility to avoid blocking for too long. In some Rust-based embedded -operating systems, this is the _only_ kind of multitasking! +این بدان معناست که async حتی برای وظایف وابسته به CPU می‌تواند مفید باشد، بسته به اینکه برنامه شما چه کار دیگری انجام می‌دهد، زیرا ابزاری مفید برای ساختاردهی روابط بین بخش‌های مختلف برنامه فراهم می‌کند. این نوعی از _چندوظیفه‌گی مشارکتی_ است، جایی که هر آینده قدرت تصمیم‌گیری درباره زمان واگذاری کنترل از طریق نقاط انتظار را دارد. بنابراین، هر آینده نیز مسئولیت دارد که از مسدود کردن بیش از حد طولانی اجتناب کند. در برخی سیستم‌عامل‌های مبتنی بر راست برای سیستم‌های تعبیه‌شده، این _تنها_ نوع چندوظیفه‌گی است! -In real-world code, you won’t usually be alternating function calls with await -points on every single line, of course. While yielding control in this way is -relatively inexpensive, it’s not free. In many cases, trying to break up a -compute-bound task might make it significantly slower, so sometimes it’s better -for _overall_ performance to let an operation block briefly. Always -measure to see what your code’s actual performance bottlenecks are. The -underlying dynamic is important to keep in mind, though, if you _are_ seeing a -lot of work happening in serial that you expected to happen concurrently! +در کد واقعی، معمولاً فراخوانی توابع را با نقاط `await` در هر خط متناوب نمی‌کنید، البته. در حالی که واگذاری کنترل به این روش نسبتاً کم‌هزینه است، اما رایگان نیست. در بسیاری از موارد، تلاش برای تقسیم یک task که CPU-bound است ممکن است آن را به‌طور قابل توجهی کندتر کند، بنابراین گاهی اوقات برای _عملکرد کلی_ بهتر است که اجازه دهید یک عملیات به‌طور مختصر مسدود شود. همیشه اندازه‌گیری کنید تا ببینید تنگناهای عملکرد واقعی کد شما کجا هستند. اما، این دینامیک اساسی را باید در ذهن داشته باشید، به‌ویژه اگر _واقعاً_ شاهد انجام مقدار زیادی کار به‌صورت ترتیبی باشید، در حالی که انتظار داشتید به‌طور همزمان انجام شود! -### Building Our Own Async Abstractions +### ساخت انتزاعات Async خودمان -We can also compose futures together to create new patterns. For example, we can -build a `timeout` function with async building blocks we already have. When -we’re done, the result will be another building block we could use to create -still more async abstractions. +ما همچنین می‌توانیم futures را با هم ترکیب کنیم تا الگوهای جدیدی ایجاد کنیم. برای مثال، می‌توانیم یک تابع `timeout` با استفاده از بلوک‌های سازنده async که از قبل داریم، بسازیم. هنگامی که کارمان تمام شد، نتیجه یک بلوک سازنده دیگر خواهد بود که می‌توانیم برای ایجاد انتزاعات async بیشتری از آن استفاده کنیم. -Listing 17-27 shows how we would expect this `timeout` to work with a slow -future. +فهرست 17-27 نشان می‌دهد که چگونه انتظار داریم این `timeout` با یک آینده کند کار کند. -<Listing number="17-27" caption="Using our imagined `timeout` to run a slow operation with a time limit" file-name="src/main.rs"> +<Listing number="17-27" caption="تعریف نحوه کار `timeout` با یک آینده کند" file-name="src/main.rs"> -```rust,ignore +```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-27/src/main.rs:here}} ``` </Listing> -Let’s implement this! To begin, let’s think about the API for `timeout`: +بیایید این را پیاده‌سازی کنیم! برای شروع، بیایید به API مورد نیاز برای `timeout` فکر کنیم: -- It needs to be an async function itself so we can await it. -- Its first parameter should be a future to run. We can make it generic to allow - it to work with any future. -- Its second parameter will be the maximum time to wait. If we use a `Duration`, - that will make it easy to pass along to `trpl::sleep`. -- It should return a `Result`. If the future completes successfully, the - `Result` will be `Ok` with the value produced by the future. If the timeout - elapses first, the `Result` will be `Err` with the duration that the timeout - waited for. +- باید خودش یک تابع async باشد تا بتوانیم منتظر آن بمانیم. +- پارامتر اول آن باید یک آینده برای اجرا باشد. می‌توانیم آن را عمومی کنیم تا بتواند با هر آینده‌ای کار کند. +- پارامتر دوم آن مدت‌زمان حداکثری برای انتظار خواهد بود. اگر از یک `Duration` استفاده کنیم، این کار ارسال آن به `trpl::sleep` را آسان می‌کند. +- باید یک `Result` بازگرداند. اگر آینده با موفقیت کامل شود، `Result` شامل `Ok` با مقدار تولیدشده توسط آینده خواهد بود. اگر زمان محدودیت زودتر سپری شود، `Result` شامل `Err` با مدت‌زمانی که زمان محدودیت برای آن منتظر ماند خواهد بود. -Listing 17-28 shows this declaration. +فهرست 17-28 این اعلان را نشان می‌دهد. <!-- This is not tested because it intentionally does not compile. --> -<Listing number="17-28" caption="Defining the signature of `timeout`" file-name="src/main.rs"> +<Listing number="17-28" caption="تعریف امضای `timeout`" file-name="src/main.rs"> ```rust,ignore {{#rustdoc_include ../listings/ch17-async-await/listing-17-28/src/main.rs:declaration}} @@ -533,21 +408,13 @@ Listing 17-28 shows this declaration. </Listing> -That satisfies our goals for the types. Now let’s think about the _behavior_ we -need: we want to race the future passed in against the duration. We can use -`trpl::sleep` to make a timer future from the duration, and use `trpl::race` to -run that timer with the future the caller passes in. +این اهداف ما برای نوع‌ها را برآورده می‌کند. حالا بیایید به _رفتاری_ که نیاز داریم فکر کنیم: می‌خواهیم آینده ارسال‌شده به آن را در برابر مدت‌زمان محدودیت مسابقه دهیم. می‌توانیم از `trpl::sleep` برای ساختن یک آینده تایمر از مدت‌زمان استفاده کنیم و از `trpl::race` برای اجرای آن تایمر با آینده‌ای که کاربر ارسال می‌کند استفاده کنیم. -We also know that `race` is not fair, polling arguments in the order in which -they are passed. Thus, we pass `future_to_try` to `race` first so it gets a -chance to complete even if `max_time` is a very short duration. If -`future_to_try` finishes first, `race` will return `Left` with the output from -`future_to_try`. If `timer` finishes first, `race` will return `Right` with the -timer’s output of `()`. +ما همچنین می‌دانیم که `race` منصفانه نیست و آرگومان‌ها را به ترتیب ارسال‌شده poll می‌کند. بنابراین، ابتدا `future_to_try` را به `race` ارسال می‌کنیم تا حتی اگر `max_time` مدت زمان بسیار کوتاهی باشد، فرصتی برای تکمیل شدن داشته باشد. اگر `future_to_try` زودتر تمام شود، `race` مقدار `Left` را با خروجی `future_to_try` بازمی‌گرداند. اگر `timer` زودتر تمام شود، `race` مقدار `Right` را با خروجی `()` تایمر بازمی‌گرداند. -In Listing 17-29, we match on the result of awaiting `trpl::race`. +در لیست ۱۷-۲۹، نتیجه انتظار برای `trpl::race` را match می‌کنیم. -<Listing number="17-29" caption="Defining `timeout` with `race` and `sleep`" file-name="src/main.rs"> +<Listing number="17-29" caption="تعریف `timeout` با استفاده از `race` و `sleep`" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-29/src/main.rs:implementation}} @@ -555,40 +422,23 @@ In Listing 17-29, we match on the result of awaiting `trpl::race`. </Listing> -If the `future_to_try` succeeds and we get a `Left(output)`, we return -`Ok(output)`. If the sleep timer elapses instead and we get a `Right(())`, we -ignore the `()` with `_` and return `Err(max_time)` instead. +اگر `future_to_try` موفق شود و مقدار `Left(output)` دریافت کنیم، مقدار `Ok(output)` را بازمی‌گردانیم. اگر به جای آن تایمر خواب منقضی شود و مقدار `Right(())` دریافت کنیم، `()` را با `_` نادیده گرفته و به جای آن مقدار `Err(max_time)` را بازمی‌گردانیم. -With that, we have a working `timeout` built out of two other async helpers. If -we run our code, it will print the failure mode after the timeout: +با این کار، یک `timeout` عملیاتی داریم که از دو ابزار کمکی async دیگر ساخته شده است. اگر کد خود را اجرا کنیم، پس از انقضای timeout، حالت شکست را چاپ خواهد کرد: ```text Failed after 2 seconds ``` -Because futures compose with other futures, you can build really powerful tools -using smaller async building blocks. For example, you can use this same approach -to combine timeouts with retries, and in turn use those with operations such as -network calls (one of the examples from the beginning of the chapter). - -In practice, you’ll usually work directly with `async` and `await`, and -secondarily with functions and macros such as `join`, `join_all`, `race`, and so -on. You’ll only need to reach for `pin` now and again to use futures with those -APIs. - -We’ve now seen a number of ways to work with multiple futures at the same -time. Up next, we’ll look at how we can work with multiple futures in a -sequence over time with _streams_. Here are a couple more things you might want -to consider first, though: - -- We used a `Vec` with `join_all` to wait for all of the futures in some group - to finish. How could you use a `Vec` to process a group of futures in - sequence instead? What are the tradeoffs of doing that? - -- Take a look at the `futures::stream::FuturesUnordered` type from the `futures` - crate. How would using it be different from using a `Vec`? (Don’t worry about - the fact that it’s from the `stream` part of the crate; it works just fine - with any collection of futures.) +از آنجا که futures می‌توانند با دیگر futures ترکیب شوند، می‌توانید ابزارهای بسیار قدرتمندی با استفاده از بلوک‌های سازنده کوچک‌تر async بسازید. برای مثال، می‌توانید از همین رویکرد برای ترکیب timeoutها با retries استفاده کنید و به نوبه خود از آن‌ها با عملیاتی مانند تماس‌های شبکه (یکی از مثال‌های ابتدای فصل) استفاده کنید. + +در عمل، معمولاً مستقیماً با `async` و `await` کار می‌کنید و به طور ثانویه از توابع و ماکروهایی مانند `join`، `join_all`، `race` و غیره استفاده می‌کنید. فقط گاهی نیاز خواهید داشت از `pin` برای استفاده از futures با آن APIها استفاده کنید. + +اکنون روش‌های متعددی برای کار با چندین future به طور همزمان دیده‌ایم. در ادامه، بررسی خواهیم کرد که چگونه می‌توانیم با چندین future به صورت متوالی در طول زمان با _streams_ کار کنیم. با این حال، در ابتدا ممکن است بخواهید به چند نکته دیگر توجه کنید: + +- ما از یک `Vec` همراه با `join_all` استفاده کردیم تا منتظر بمانیم تمام futures در یک گروه به پایان برسند. چگونه می‌توانید از یک `Vec` برای پردازش یک گروه از futures به صورت متوالی استفاده کنید؟ معاوضه‌های انجام این کار چیست؟ + +- به نوع `futures::stream::FuturesUnordered` از crate `futures` نگاهی بیندازید. استفاده از آن چگونه می‌تواند با استفاده از یک `Vec` متفاوت باشد؟ (نگران این نباشید که این نوع از بخش `stream` crate آمده است؛ با هر مجموعه‌ای از futures به خوبی کار می‌کند.) [dyn]: ch12-03-improving-error-handling-and-modularity.html [enum-alt]: ch08-01-vectors.html#using-an-enum-to-store-multiple-types diff --git a/src/ch17-04-streams.md b/src/ch17-04-streams.md index 196da5ca1b..1d640cafdb 100644 --- a/src/ch17-04-streams.md +++ b/src/ch17-04-streams.md @@ -1,35 +1,14 @@ -## Streams: Futures in Sequence - -<!-- Old headings. Do not remove or links may break. --> - -<a id="streams"></a> - -So far in this chapter, we’ve mostly stuck to individual futures. The one big -exception was the async channel we used. Recall how we used the receiver for our -async channel earlier in this chapter in the [“Message -Passing”][17-02-messages]<!-- ignore --> section. The async `recv` method -produces a sequence of items over time. This is an instance of a much more -general pattern known as a _stream_. - -We saw a sequence of items back in Chapter 13, when we looked at the `Iterator` -trait in [The Iterator Trait and the `next` Method][iterator-trait]<!-- ignore ---> section, but there are two differences between iterators and the async -channel receiver. The first difference is time: iterators are synchronous, while -the channel receiver is asynchronous. The second is the API. When working -directly with `Iterator`, we call its synchronous `next` method. With the -`trpl::Receiver` stream in particular, we called an asynchronous `recv` method -instead. Otherwise, these APIs feel very similar, and that similarity -isn’t a coincidence. A stream is like an asynchronous form of iteration. Whereas -the `trpl::Receiver` specifically waits to receive messages, though, the -general-purpose stream API is much broader: it provides the next item the -way `Iterator` does, but asynchronously. - -The similarity between iterators and streams in Rust means we can actually -create a stream from any iterator. As with an iterator, we can work with a -stream by calling its `next` method and then awaiting the output, as in Listing -17-30. - -<Listing number="17-30" caption="Creating a stream from an iterator and printing its values" file-name="src/main.rs"> +## Stream‌ها: Futures به صورت متوالی + +تا اینجا در این فصل، بیشتر به آینده‌های فردی (_individual futures_) پایبند بوده‌ایم. یک استثنای بزرگ استفاده از کانال async بود. به یاد بیاورید چگونه در ابتدای این فصل در بخش [“ارسال پیام”][17-02-messages]<!-- ignore --> از گیرنده کانال async استفاده کردیم. متد async به نام `recv` یک دنباله از آیتم‌ها را در طول زمان تولید می‌کند. این یک نمونه از یک الگوی کلی‌تر به نام _stream_ است. + +ما پیش‌تر در فصل ۱۳ با یک توالی از آیتم‌ها مواجه شدیم، زمانی که به `Iterator` و متد `next` آن در بخش [ویژگی Iterator و متد `next`][iterator-trait]<!-- ignore --> پرداختیم، اما بین `Iterator`ها و گیرنده‌ی ناهمگام کانال‌ها دو تفاوت وجود دارد. +تفاوت اول مربوط به *زمان* است: `Iterator`ها همگام (synchronous) هستند، در حالی که گیرنده‌ی کانال ناهمگام (asynchronous) است. +تفاوت دوم در *رابط برنامه‌نویسی کاربردی (API)* است. وقتی به‌صورت مستقیم با `Iterator` کار می‌کنیم، از متد همگام `next` استفاده می‌کنیم. در `stream`‌ مربوط به `trpl::Receiver`، ما به جای آن متد ناهمگام `recv` را فراخوانی کردیم. +با این وجود، این APIها از لحاظ کارکرد بسیار مشابه هستند، و این شباهت اتفاقی نیست. یک *stream* در واقع شکل ناهمگام پیمایش (iteration) است. در حالی که `trpl::Receiver` به‌طور خاص منتظر دریافت پیام می‌ماند، API عمومی‌تر stream بسیار گسترده‌تر است: این API، آیتم بعدی را به همان شیوه‌ای که `Iterator` فراهم می‌کند، ولی به‌صورت ناهمگام ارائه می‌دهد. + + +<Listing number="17-30" caption="ایجاد یک stream از یک iterator و چاپ مقادیر آن" file-name="src/main.rs"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch17-async-await/listing-17-30/src/main.rs:stream}} @@ -37,13 +16,9 @@ stream by calling its `next` method and then awaiting the output, as in Listing </Listing> -We start with an array of numbers, which we convert to an iterator and then call -`map` on to double all the values. Then we convert the iterator into a stream -using the `trpl::stream_from_iter` function. Next, we loop over the items in the -stream as they arrive with the `while let` loop. +ما با یک آرایه از اعداد شروع می‌کنیم، آن را به یک iterator تبدیل کرده و سپس متد `map` را فراخوانی می‌کنیم تا تمام مقادیر را دو برابر کنیم. سپس با استفاده از تابع `trpl::stream_from_iter`، این iterator را به یک stream تبدیل می‌کنیم. در ادامه، با استفاده از حلقه `while let`، بر روی آیتم‌های موجود در stream که به مرور می‌رسند، حلقه می‌زنیم. -Unfortunately, when we try to run the code, it doesn’t compile, but instead it -reports that there’s no `next` method available: +متأسفانه، وقتی سعی می‌کنیم این کد را اجرا کنیم، کامپایل نمی‌شود و به جای آن گزارش می‌دهد که متد `next` در دسترس نیست: <!-- manual-regeneration cd listings/ch17-async-await/listing-17-30 @@ -77,25 +52,14 @@ help: there is a method `try_next` with a similar name | ~~~~~~~~ ``` -As this output explains, the reason for the compiler error is that we need the -right trait in scope to be able to use the `next` method. Given our discussion -so far, you might reasonably expect that trait to be `Stream`, but it’s actually -`StreamExt`. Short for _extension_, `Ext` is a common pattern in the -Rust community for extending one trait with another. +همان‌طور که این خروجی توضیح می‌دهد، دلیل خطای کامپایلر این است که برای استفاده از متد `next` باید ویژگی مناسب در دامنه باشد. با توجه به بحث‌هایی که تاکنون داشته‌ایم، ممکن است منطقی باشد که انتظار داشته باشید این ویژگی `Stream` باشد، اما در واقع `StreamExt` است. `Ext` که مخفف _extension_ است، یک الگوی رایج در جامعه Rust برای گسترش یک ویژگی با ویژگی دیگر است. + +ما در انتهای این فصل ویژگی‌های `Stream` و `StreamExt` را با جزئیات بیشتری توضیح خواهیم داد، اما فعلاً تنها چیزی که باید بدانید این است که ویژگی `Stream` یک رابط سطح پایین تعریف می‌کند که به طور مؤثری ویژگی‌های `Iterator` و `Future` را ترکیب می‌کند. `StreamExt` مجموعه‌ای از APIهای سطح بالاتر را روی `Stream` ارائه می‌دهد، از جمله متد `next` و همچنین متدهای کاربردی دیگر مشابه آنچه ویژگی `Iterator` ارائه می‌دهد. `Stream` و `StreamExt` هنوز بخشی از کتابخانه استاندارد Rust نیستند، اما بیشتر crateهای اکوسیستم از همین تعریف استفاده می‌کنند. -We’ll explain the `Stream` and `StreamExt` traits in a bit more detail at the -end of the chapter, but for now all you need to know is that the `Stream` trait -defines a low-level interface that effectively combines the `Iterator` and -`Future` traits. `StreamExt` supplies a higher-level set of APIs on top of -`Stream`, including the `next` method as well as other utility methods similar -to those provided by the `Iterator` trait. `Stream` and `StreamExt` are not yet -part of Rust’s standard library, but most ecosystem crates use the same -definition. -The fix to the compiler error is to add a `use` statement for `trpl::StreamExt`, -as in Listing 17-31. +برای رفع خطای کامپایل، باید یک دستور `use` برای `trpl::StreamExt` اضافه کنیم، همان‌طور که در فهرست 17-31 آمده است. -<Listing number="17-31" caption="Successfully using an iterator as the basis for a stream" file-name="src/main.rs"> +<Listing number="17-31" caption="استفاده موفق از یک iterator به‌عنوان پایه‌ای برای یک stream" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-31/src/main.rs:all}} @@ -103,12 +67,9 @@ as in Listing 17-31. </Listing> -With all those pieces put together, this code works the way we want! What’s -more, now that we have `StreamExt` in scope, we can use all of its utility -methods, just as with iterators. For example, in Listing 17-32, we use the -`filter` method to filter out everything but multiples of three and five. +با قرار دادن همه این قطعات در کنار هم، این کد به همان روشی که می‌خواهیم کار می‌کند! مهم‌تر از همه، اکنون که `StreamExt` در دامنه داریم، می‌توانیم از تمام متدهای کاربردی آن استفاده کنیم، درست مانند iteratorها. برای مثال، در فهرست 17-32، از متد `filter` برای فیلتر کردن همه چیز به جز مضرب‌های سه و پنج استفاده می‌کنیم. -<Listing number="17-32" caption="Filtering a stream with the `StreamExt::filter` method" file-name="src/main.rs"> +<Listing number="17-32" caption="فیلتر کردن یک `Stream` با استفاده از متد `StreamExt::filter`" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-32/src/main.rs:all}} @@ -116,26 +77,16 @@ methods, just as with iterators. For example, in Listing 17-32, we use the </Listing> -Of course, this isn’t very interesting, since we could do the same with normal -iterators and without any async at all. Let’s look at what -we can do that _is_ unique to streams. +البته این خیلی جالب نیست، چون می‌توانستیم همین کار را با iteratorهای معمولی و بدون هیچ async انجام دهیم. بیایید ببینیم چه کاری می‌توانیم انجام دهیم که _منحصربه‌فرد_ برای stream‌ها باشد. -### Composing Streams +### ترکیب Stream‌ها -Many concepts are naturally represented as streams: items becoming available in -a queue, chunks of data being pulled incrementally from the filesystem when the -full data set is too large for the computer’s memory, or data arriving over the -network over time. Because streams are futures, we can use them with any other -kind of future and combine them in interesting ways. For example, we can batch -up events to avoid triggering too many network calls, set timeouts on sequences -of long-running operations, or throttle user interface events to avoid doing -needless work. +بسیاری از مفاهیم به طور طبیعی به‌عنوان stream‌ها نمایش داده می‌شوند: آیتم‌هایی که در یک صف در دسترس می‌شوند، بخش‌هایی از داده که به صورت تدریجی از سیستم فایل خوانده می‌شوند وقتی مجموعه داده کامل برای حافظه کامپیوتر بیش از حد بزرگ است، یا داده‌هایی که به مرور زمان از طریق شبکه می‌رسند. چون stream‌ها نیز futures هستند، می‌توانیم از آن‌ها با هر نوع دیگر future استفاده کنیم و آن‌ها را به روش‌های جالبی ترکیب کنیم. برای مثال، می‌توانیم رویدادها را به صورت دسته‌ای جمع کنیم تا از ایجاد تعداد زیادی فراخوانی شبکه جلوگیری کنیم، تایم‌اوت‌هایی روی دنباله‌ای از عملیات‌های طولانی تنظیم کنیم، یا رویدادهای رابط کاربری را کنترل کنیم تا از انجام کارهای غیرضروری اجتناب کنیم. -Let’s start by building a little stream of messages as a stand-in for a stream -of data we might see from a WebSocket or another real-time communication -protocol, as shown in Listing 17-33. +بیایید با ساخت یک stream کوچک از پیام‌ها شروع کنیم که به‌عنوان یک جایگزین برای یک stream از داده‌هایی که ممکن است از یک WebSocket یا یک پروتکل ارتباطی بلادرنگ دیگر ببینیم، همان‌طور که در لیست ۱۷-۳۳ نشان داده شده است. -<Listing number="17-33" caption="Using the `rx` receiver as a `ReceiverStream`" file-name="src/main.rs"> + +<Listing number="17-33" caption="استفاده از گیرنده `rx` به‌عنوان یک `ReceiverStream`" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-33/src/main.rs:all}} @@ -143,15 +94,12 @@ protocol, as shown in Listing 17-33. </Listing> -First, we create a function called `get_messages` that returns `impl Stream<Item -= String>`. For its implementation, we create an async channel, loop over the -first 10 letters of the English alphabet, and send them across the channel. +ابتدا یک تابع به نام `get_messages` ایجاد می‌کنیم که `impl Stream<Item = String>` را بازمی‌گرداند. برای پیاده‌سازی آن، یک کانال async ایجاد می‌کنیم، بر روی ۱۰ حرف اول الفبای انگلیسی حلقه می‌زنیم، و آن‌ها را از طریق کانال ارسال می‌کنیم. + +همچنین از یک نوع جدید به نام `ReceiverStream` استفاده می‌کنیم، که `rx` گیرنده از `trpl::channel` را به یک `Stream` با متد `next` تبدیل می‌کند. دوباره در `main`، از یک حلقه `while let` برای چاپ تمام پیام‌ها از stream استفاده می‌کنیم. -We also use a new type: `ReceiverStream`, which converts the `rx` receiver from -the `trpl::channel` into a `Stream` with a `next` method. Back in `main`, we use -a `while let` loop to print all the messages from the stream. +وقتی این کد را اجرا می‌کنیم، دقیقاً نتایجی را که انتظار داریم دریافت می‌کنیم: -When we run this code, we get exactly the results we would expect: <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the threads running differently rather than @@ -170,12 +118,9 @@ Message: 'i' Message: 'j' ``` -Again, we could do this with the regular `Receiver` API or even the regular -`Iterator` API, though, so let’s add a feature that requires streams: adding a -timeout that applies to every item in the stream, and a delay on the items we -emit, as shown in Listing 17-34. +دوباره، می‌توانستیم این کار را با API معمولی `Receiver` یا حتی API معمولی `Iterator` انجام دهیم، اما بیایید ویژگی‌ای اضافه کنیم که نیاز به streams داشته باشد: اضافه کردن یک تایم‌اوت که برای هر آیتم در stream اعمال شود، و یک تأخیر روی آیتم‌هایی که ارسال می‌کنیم، همان‌طور که در لیست ۱۷-۳۴ نشان داده شده است. -<Listing number="17-34" caption="Using the `StreamExt::timeout` method to set a time limit on the items in a stream" file-name="src/main.rs"> +<Listing number="17-34" caption="استفاده از متد `StreamExt::timeout` برای تعیین یک محدودیت زمانی برای آیتم‌های موجود در یک stream" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-34/src/main.rs:timeout}} @@ -183,21 +128,11 @@ emit, as shown in Listing 17-34. </Listing> -We start by adding a timeout to the stream with the `timeout` method, which -comes from the `StreamExt` trait. Then we update the body of the `while let` -loop, because the stream now returns a `Result`. The `Ok` variant indicates a -message arrived in time; the `Err` variant indicates that the timeout elapsed -before any message arrived. We `match` on that result and either print the -message when we receive it successfully or print a notice about the timeout. -Finally, notice that we pin the messages after applying the timeout to them, -because the timeout helper produces a stream that needs to be pinned to be -polled. +ابتدا یک تایم‌اوت به stream با استفاده از متد `timeout` اضافه می‌کنیم، که از ویژگی `StreamExt` می‌آید. سپس بدنه حلقه `while let` را به‌روزرسانی می‌کنیم، زیرا اکنون stream یک `Result` بازمی‌گرداند. حالت `Ok` نشان‌دهنده این است که یک پیام به‌موقع رسیده است؛ حالت `Err` نشان می‌دهد که تایم‌اوت قبل از رسیدن هر پیامی منقضی شده است. روی این نتیجه یک `match` انجام می‌دهیم و یا پیام را وقتی با موفقیت دریافت می‌کنیم چاپ می‌کنیم، یا اخطاری درباره تایم‌اوت چاپ می‌کنیم. در نهایت، توجه کنید که پس از اعمال تایم‌اوت به پیام‌ها، آن‌ها را pin می‌کنیم، زیرا ابزار تایم‌اوت یک stream تولید می‌کند که باید pin شود تا بتوان آن را poll کرد. -However, because there are no delays between messages, this timeout does not -change the behavior of the program. Let’s add a variable delay to the messages -we send, as shown in Listing 17-35. +با این حال، چون بین پیام‌ها تأخیری وجود ندارد، این تایم‌اوت رفتار برنامه را تغییر نمی‌دهد. بیایید یک تأخیر متغیر به پیام‌هایی که ارسال می‌کنیم اضافه کنیم، همان‌طور که در لیست ۱۷-۳۵ نشان داده شده است. -<Listing number="17-35" caption="Sending messages through `tx` with an async delay without making `get_messages` an async function" file-name="src/main.rs"> +<Listing number="17-35" caption="ارسال پیام‌ها از طریق `tx` با یک تأخیر async بدون تبدیل `get_messages` به یک تابع async" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-35/src/main.rs:messages}} @@ -205,36 +140,14 @@ we send, as shown in Listing 17-35. </Listing> -In `get_messages`, we use the `enumerate` iterator method with the `messages` -array so that we can get the index of each item we’re sending along with the -item itself. Then we apply a 100-millisecond delay to even-index items and a -300-millisecond delay to odd-index items to simulate the different delays we -might see from a stream of messages in the real world. Because our timeout is -for 200 milliseconds, this should affect half of the messages. - -To sleep between messages in the `get_messages` function without blocking, we -need to use async. However, we can’t make `get_messages` itself into an async -function, because then we’d return a `Future<Output = Stream<Item = String>>` -instead of a `Stream<Item = String>>`. The caller would have to await -`get_messages` itself to get access to the stream. But remember: everything in a -given future happens linearly; concurrency happens _between_ futures. Awaiting -`get_messages` would require it to send all the messages, including the sleep -delay between each message, before returning the receiver stream. As a result, -the timeout would be useless. There would be no delays in the stream itself; -they would all happen before the stream was even available. - -Instead, we leave `get_messages` as a regular function that returns a stream, -and we spawn a task to handle the async `sleep` calls. - -> Note: Calling `spawn_task` in this way works because we already set up our -> runtime; had we not, it would cause a panic. Other implementations choose -> different tradeoffs: they might spawn a new runtime and avoid the panic but -> end up with a bit of extra overhead, or they may simply not provide a -> standalone way to spawn tasks without reference to a runtime. Make sure you -> know what tradeoff your runtime has chosen and write your code accordingly! - -Now our code has a much more interesting result. Between every other pair of -messages, a `Problem: Elapsed(())` error. + +برای خوابیدن بین پیام‌ها در تابع `get_messages` بدون مسدود کردن، باید از async استفاده کنیم. با این حال، نمی‌توانیم خود `get_messages` را به یک تابع async تبدیل کنیم، زیرا در این صورت یک `Future<Output = Stream<Item = String>>` به جای یک `Stream<Item = String>` بازمی‌گرداند. کاربر باید خود `get_messages` را منتظر بماند تا به stream دسترسی پیدا کند. اما به یاد داشته باشید: هر چیزی در یک آینده مشخص به‌صورت خطی اتفاق می‌افتد؛ همزمانی _بین_ آینده‌ها اتفاق می‌افتد. انتظار برای `get_messages` نیاز دارد که تمام پیام‌ها را ارسال کند، از جمله خوابیدن بین ارسال هر پیام، قبل از بازگرداندن stream گیرنده. در نتیجه، زمان محدود بی‌فایده می‌شود. هیچ تأخیری در خود stream وجود نخواهد داشت: تمام تأخیرها قبل از در دسترس قرار گرفتن stream اتفاق می‌افتد. + +در عوض، `get_messages` را به‌عنوان یک تابع معمولی که یک stream بازمی‌گرداند باقی می‌گذاریم و یک تسک برای مدیریت فراخوانی‌های async `sleep` ایجاد می‌کنیم. + +> نکته: فراخوانی `spawn_task` به این روش کار می‌کند زیرا ما از قبل runtime خود را تنظیم کرده‌ایم. فراخوانی این پیاده‌سازی خاص از `spawn_task` _بدون_ تنظیم اولیه یک runtime باعث panic می‌شود. پیاده‌سازی‌های دیگر معاملات متفاوتی انتخاب می‌کنند: ممکن است یک runtime جدید ایجاد کنند و بنابراین از panic اجتناب کنند، اما با کمی سربار اضافی مواجه شوند، یا به سادگی راهی مستقل برای ایجاد تسک‌ها بدون ارجاع به یک runtime ارائه ندهند. باید مطمئن شوید که می‌دانید runtime شما چه معامله‌ای انتخاب کرده است و کد خود را بر این اساس بنویسید! + +اکنون کد ما نتیجه بسیار جالب‌تری دارد! بین هر جفت پیام، یک خطا گزارش می‌شود: `Problem: Elapsed(())`. <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the threads running differently rather than @@ -258,27 +171,16 @@ Problem: Elapsed(()) Message: 'j' ``` -The timeout doesn’t prevent the messages from arriving in the end. We still get -all of the original messages, because our channel is _unbounded_: it can hold as -many messages as we can fit in memory. If the message doesn’t arrive before the -timeout, our stream handler will account for that, but when it polls the stream -again, the message may now have arrived. +تایم‌اوت از رسیدن پیام‌ها در نهایت جلوگیری نمی‌کند. ما همچنان تمام پیام‌های اصلی را دریافت می‌کنیم، زیرا کانال ما _بدون محدودیت_ است: می‌تواند به اندازه‌ای که در حافظه جا شود پیام‌ها را نگه دارد. اگر پیام قبل از تایم‌اوت نرسد، handler stream ما آن را مدیریت می‌کند، اما وقتی دوباره stream را poll کند، ممکن است پیام اکنون رسیده باشد. + +اگر به رفتار متفاوتی نیاز دارید، می‌توانید از انواع دیگر کانال‌ها یا به طور کلی انواع دیگر streamها استفاده کنید. بیایید یکی از این موارد را در عمل ببینیم، با ترکیب یک stream از فواصل زمانی با این stream از پیام‌ها. -You can get different behavior if needed by using other kinds of channels or -other kinds of streams more generally. Let’s see one of those in practice by -combining a stream of time intervals with this stream of messages. +### ترکیب Streamها -### Merging Streams +ابتدا، یک stream دیگر ایجاد می‌کنیم که اگر به طور مستقیم اجرا شود، هر میلی‌ثانیه یک آیتم ارسال می‌کند. برای سادگی، می‌توانیم از تابع `sleep` برای ارسال یک پیام با تأخیر استفاده کنیم و آن را با همان روشی که در `get_messages` استفاده کردیم—ایجاد یک stream از یک کانال—ترکیب کنیم. تفاوت این است که این بار، می‌خواهیم تعداد فواصل زمانی که گذشته‌اند را بازگردانیم، بنابراین نوع بازگشتی `impl Stream<Item = u32>` خواهد بود، و می‌توانیم تابع را `get_intervals` بنامیم (نگاه کنید به لیست ۱۷-۳۶). -First, let’s create another stream, which will emit an item every millisecond if -we let it run directly. For simplicity, we can use the `sleep` function to send -a message on a delay and combine it with the same approach we used in -`get_messages` of creating a stream from a channel. The difference is that this -time, we’re going to send back the count of intervals that have elapsed, so the -return type will be `impl Stream<Item = u32>`, and we can call the function -`get_intervals` (see Listing 17-36). -<Listing number="17-36" caption="Creating a stream with a counter that will be emitted once every millisecond" file-name="src/main.rs"> +<Listing number="17-36" caption="ایجاد یک stream با یک شمارنده که هر میلی‌ثانیه یک بار ارسال می‌شود" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-36/src/main.rs:intervals}} @@ -286,22 +188,13 @@ return type will be `impl Stream<Item = u32>`, and we can call the function </Listing> -We start by defining a `count` in the task. (We could define it outside the -task, too, but it’s clearer to limit the scope of any given variable.) Then we -create an infinite loop. Each iteration of the loop asynchronously sleeps for -one millisecond, increments the count, and then sends it over the channel. -Because this is all wrapped in the task created by `spawn_task`, all of -it—including the infinite loop—will get cleaned up along with the runtime. +ابتدا یک متغیر `count` را درون task تعریف می‌کنیم. (می‌توانستیم آن را خارج از task نیز تعریف کنیم، اما محدود کردن دامنه هر متغیر داده‌شده واضح‌تر است.) سپس یک حلقه بی‌نهایت ایجاد می‌کنیم. در هر تکرار حلقه، به صورت ناهمزمان به مدت یک میلی‌ثانیه می‌خوابد، مقدار `count` را افزایش می‌دهد و سپس آن را از طریق کانال ارسال می‌کند. از آنجا که همه این‌ها درون taskی که توسط `spawn_task` ایجاد شده است قرار دارد، همه آن—از جمله حلقه بی‌نهایت—همراه با runtime پاک‌سازی می‌شود. -This kind of infinite loop, which ends only when the whole runtime gets torn -down, is fairly common in async Rust: many programs need to keep running -indefinitely. With async, this doesn’t block anything else, as long as there is -at least one await point in each iteration through the loop. +این نوع حلقه بی‌نهایت، که تنها زمانی به پایان می‌رسد که کل runtime از بین برود، در async Rust نسبتاً رایج است: بسیاری از برنامه‌ها نیاز دارند که به طور نامحدود اجرا شوند. با async، این کار چیزی دیگر را مسدود نمی‌کند، تا زمانی که حداقل یک نقطه انتظار (_await point_) در هر تکرار از حلقه وجود داشته باشد. -Now, back in our main function’s async block, we can attempt to merge the -`messages` and `intervals` streams, as shown in Listing 17-37. +حالا، درون بلوک async تابع اصلی ما، می‌توانیم تلاش کنیم که streamهای `messages` و `intervals` را با هم ترکیب کنیم، همان‌طور که در لیست ۱۷-۳۷ نشان داده شده است. -<Listing number="17-37" caption="Attempting to merge the `messages` and `intervals` streams" file-name="src/main.rs"> +<Listing number="17-37" caption="تلاش برای ترکیب streamهای `messages` و `intervals`" file-name="src/main.rs"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch17-async-await/listing-17-37/src/main.rs:main}} @@ -309,26 +202,13 @@ Now, back in our main function’s async block, we can attempt to merge the </Listing> -We start by calling `get_intervals`. Then we merge the `messages` and -`intervals` streams with the `merge` method, which combines multiple streams -into one stream that produces items from any of the source streams as soon as -the items are available, without imposing any particular ordering. Finally, we -loop over that combined stream instead of over `messages`. - -At this point, neither `messages` nor `intervals` needs to be pinned or mutable, -because both will be combined into the single `merged` stream. However, this -call to `merge` doesn’t compile! (Neither does the `next` call in the `while -let` loop, but we’ll come back to that.) This is because the two streams have -different types. The `messages` stream has the type `Timeout<impl Stream<Item = -String>>`, where `Timeout` is the type that implements `Stream` for a `timeout` -call. The `intervals` stream has the type `impl Stream<Item = u32>`. To merge -these two streams, we need to transform one of them to match the other. We’ll -rework the intervals stream, because messages is already in the basic format we -want and has to handle timeout errors (see Listing 17-38). +ابتدا `get_intervals` را فراخوانی می‌کنیم. سپس streamهای `messages` و `intervals` را با استفاده از متد `merge` ترکیب می‌کنیم. این متد چندین stream را به یک stream ترکیب می‌کند که آیتم‌ها را از هر یک از streamهای منبع، به محض در دسترس بودن، تولید می‌کند، بدون اینکه ترتیب خاصی را اعمال کند. در نهایت، به جای اینکه روی `messages` حلقه بزنیم، روی این stream ترکیبی حلقه می‌زنیم. + +در این مرحله، نه `messages` و نه `intervals` نیازی به pin یا mutable بودن ندارند، زیرا هر دو در یک stream واحد به نام `merged` ترکیب می‌شوند. با این حال، این فراخوانی به `merge` کامپایل نمی‌شود! (فراخوانی `next` در حلقه `while let` هم کامپایل نمی‌شود، اما به آن برمی‌گردیم.) دلیل آن این است که این دو stream انواع مختلفی دارند. stream `messages` نوع `Timeout<impl Stream<Item = String>>` دارد، جایی که `Timeout` نوعی است که ویژگی `Stream` را برای فراخوانی `timeout` پیاده‌سازی می‌کند. stream `intervals` نوع `impl Stream<Item = u32>` دارد. برای ترکیب این دو stream، باید یکی از آن‌ها را به نوع دیگری تبدیل کنیم. ما stream `intervals` را بازبینی می‌کنیم، زیرا `messages` قبلاً در قالب اصلی مورد نظر ما است و باید خطاهای timeout را مدیریت کند (نگاه کنید به لیست ۱۷-۳۸). <!-- We cannot directly test this one, because it never stops. --> -<Listing number="17-38" caption="Aligning the type of the the `intervals` stream with the type of the `messages` stream" file-name="src/main.rs"> +<Listing number="17-38" caption="هماهنگ کردن نوع‌های stream `intervals` با نوع stream `messages`" file-name="src/main.rs"> ```rust,ignore {{#rustdoc_include ../listings/ch17-async-await/listing-17-38/src/main.rs:main}} @@ -336,17 +216,8 @@ want and has to handle timeout errors (see Listing 17-38). </Listing> -First, we can use the `map` helper method to transform the `intervals` into a -string. Second, we need to match the `Timeout` from `messages`. Because we don’t -actually _want_ a timeout for `intervals`, though, we can just create a timeout -which is longer than the other durations we are using. Here, we create a -10-second timeout with `Duration::from_secs(10)`. Finally, we need to make -`stream` mutable, so that the `while let` loop’s `next` calls can iterate -through the stream, and pin it so that it’s safe to do so. That gets us _almost_ -to where we need to be. Everything type checks. If you run this, though, there -will be two problems. First, it will never stop! You’ll need to stop it with -<span class="keystroke">ctrl-c</span>. Second, the messages from the English -alphabet will be buried in the midst of all the interval counter messages: +ابتدا می‌توانیم از متد کمکی `map` برای تبدیل `intervals` به یک رشته استفاده کنیم. دوم، نیاز داریم که `Timeout` از `messages` را مدیریت کنیم. با این حال، چون واقعاً _نمی‌خواهیم_ تایم‌اوتی برای `intervals` داشته باشیم، می‌توانیم یک تایم‌اوت ایجاد کنیم که طولانی‌تر از مدت‌های دیگر مورد استفاده ما باشد. در اینجا، یک تایم‌اوت ۱۰ ثانیه‌ای با استفاده از `Duration::from_secs(10)` ایجاد می‌کنیم. در نهایت، نیاز داریم که `stream` را متغیر (`mutable`) کنیم تا فراخوانی‌های `next` در حلقه `while let` بتوانند روی stream تکرار کنند و آن را pin کنیم تا این کار ایمن باشد. این ما را _تقریباً_ به جایی که باید برسیم می‌رساند. همه چیز از نظر نوع بررسی می‌شود. اما اگر این کد را اجرا کنید، دو مشکل وجود خواهد داشت. اول، هیچ‌گاه متوقف نمی‌شود! باید با زدن <span class="keystroke">ctrl-c</span> آن را متوقف کنید. دوم، پیام‌های الفبای انگلیسی در میان تمام پیام‌های شمارنده interval دفن خواهند شد: + <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the tasks running differently rather than @@ -364,9 +235,10 @@ Interval: 43 --snip-- ``` -Listing 17-39 shows one way to solve these last two problems. +لیست ۱۷-۳۹ یک روش برای حل این دو مشکل آخر را نشان می‌دهد. + -<Listing number="17-39" caption="Using `throttle` and `take` to manage the merged streams" file-name="src/main.rs"> +<Listing number="17-39" caption="استفاده از `throttle` و `take` برای مدیریت streams ترکیب‌شده" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-39/src/main.rs:throttle}} @@ -374,26 +246,12 @@ Listing 17-39 shows one way to solve these last two problems. </Listing> -First, we use the `throttle` method on the `intervals` stream so that it doesn’t -overwhelm the `messages` stream. _Throttling_ is a way of limiting the rate at -which a function will be called—or, in this case, how often the stream will be -polled. Once every 100 milliseconds should do, because that’s roughly how often -our messages arrive. - -To limit the number of items we will accept from a stream, we apply the `take` -method to the `merged` stream, because we want to limit the final output, not -just one stream or the other. - -Now when we run the program, it stops after pulling 20 items from the stream, -and the intervals don’t overwhelm the messages. We also don’t get `Interval: -100` or `Interval: 200` or so on, but instead get `Interval: 1`, `Interval: 2`, -and so on—even though we have a source stream that _can_ produce an event every -millisecond. That’s because the `throttle` call produces a new stream that wraps -the original stream so that the original stream gets polled only at the throttle -rate, not its own “native” rate. We don’t have a bunch of unhandled interval -messages we’re choosing to ignore. Instead, we never produce those interval -messages in the first place! This is the inherent “laziness” of Rust’s futures -at work again, allowing us to choose our performance characteristics. +ابتدا از متد `throttle` روی stream `intervals` استفاده می‌کنیم تا این stream باعث غرق شدن stream `messages` نشود. _Throttling_ روشی برای محدود کردن نرخ فراخوانی یک تابع است—یا در این مورد، محدود کردن نرخ poll کردن یک stream. یک بار در هر ۱۰۰ میلی‌ثانیه کافی خواهد بود، زیرا تقریباً به همان اندازه پیام‌های ما می‌رسند. + +برای محدود کردن تعداد آیتم‌هایی که از یک stream قبول می‌کنیم، متد `take` را روی stream `merged` اعمال می‌کنیم، زیرا می‌خواهیم خروجی نهایی را محدود کنیم، نه فقط یکی از streamها را. + +اکنون وقتی برنامه را اجرا می‌کنیم، پس از دریافت ۲۰ آیتم از stream متوقف می‌شود و intervals باعث غرق شدن messages نمی‌شود. همچنین، ما دیگر `Interval: 100` یا `Interval: 200` و موارد مشابه را نمی‌بینیم، بلکه به جای آن `Interval: 1`، `Interval: 2` و به همین ترتیب دریافت می‌کنیم—حتی اگر یک stream منبع داریم که _می‌تواند_ هر میلی‌ثانیه یک رویداد تولید کند. دلیل این است که فراخوانی `throttle` یک stream جدید تولید می‌کند که stream اصلی را بسته‌بندی می‌کند تا stream اصلی فقط با نرخ throttle و نه با نرخ "ذاتی" خود poll شود. ما یک سری پیام interval غیرقابل پردازش نداریم که انتخاب کرده باشیم آن‌ها را نادیده بگیریم. بلکه، ما هرگز آن پیام‌های interval را در وهله اول تولید نمی‌کنیم! این همان "تنبلی" ذاتی futures در Rust است که دوباره به کار گرفته می‌شود و به ما اجازه می‌دهد ویژگی‌های عملکردی خود را انتخاب کنیم. + <!-- Not extracting output because changes to this output aren't significant; the changes are likely to be due to the threads running differently rather than @@ -422,16 +280,10 @@ Problem: Elapsed(()) Interval: 12 ``` -There’s one last thing we need to handle: errors! With both of these -channel-based streams, the `send` calls could fail when the other side of the -channel closes—and that’s just a matter of how the runtime executes the futures -that make up the stream. Up until now, we’ve ignored this possibility by calling -`unwrap`, but in a well-behaved app, we should explicitly handle the error, at -minimum by ending the loop so we don’t try to send any more messages. Listing -17-40 shows a simple error strategy: print the issue and then `break` from the -loops. +تنها یک مورد باقی مانده که باید مدیریت کنیم: خطاها! با هر دو stream مبتنی بر کانال، فراخوانی‌های `send` ممکن است در صورتی که طرف دیگر کانال بسته شود، با شکست مواجه شوند—و این به نحوه اجرای runtime برای futures که stream را تشکیل می‌دهند بستگی دارد. تاکنون این احتمال را با فراخوانی `unwrap` نادیده گرفته‌ایم، اما در یک برنامه با رفتار مناسب، باید به‌طور صریح خطا را مدیریت کنیم، حداقل با پایان دادن به حلقه تا دیگر پیام ارسال نکنیم. لیست ۱۷-۴۰ یک استراتژی ساده برای مدیریت خطا را نشان می‌دهد: چاپ مشکل و سپس `break` از حلقه‌ها. -<Listing number="17-40" caption="Handling errors and shutting down the loops"> + +<Listing number="17-40" caption="مدیریت خطاها و خاتمه دادن به حلقه‌ها"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-40/src/main.rs:errors}} @@ -439,12 +291,10 @@ loops. </Listing> -As usual, the correct way to handle a message send error will vary; just make -sure you have a strategy. +همان‌طور که معمول است، روش درست برای مدیریت یک خطای ارسال پیام می‌تواند متفاوت باشد؛ فقط مطمئن شوید که یک استراتژی دارید. + +اکنون که مقدار زیادی از کد async را در عمل مشاهده کردیم، بیایید کمی به عقب برگردیم و به جزئیات نحوه کارکرد `Future`، `Stream` و ویژگی‌های کلیدی دیگر که Rust برای اجرای async استفاده می‌کند، بپردازیم. -Now that we’ve seen a bunch of async in practice, let’s take a step back and dig -into a few of the details of how `Future`, `Stream`, and the other key traits -Rust uses to make async work. [17-02-messages]: ch17-02-concurrency-with-async.html#message-passing [iterator-trait]: ch13-02-iterators.html#the-iterator-trait-and-the-next-method diff --git a/src/ch17-05-traits-for-async.md b/src/ch17-05-traits-for-async.md index d4447a34b5..8fe5b3e104 100644 --- a/src/ch17-05-traits-for-async.md +++ b/src/ch17-05-traits-for-async.md @@ -1,25 +1,14 @@ -## A Closer Look at the Traits for Async +## بررسی دقیق‌تر ویژگی‌ها برای Async -<!-- Old headings. Do not remove or links may break. --> - -<a id="digging-into-the-traits-for-async"></a> - -Throughout the chapter, we’ve used the `Future`, `Pin`, `Unpin`, `Stream`, and -`StreamExt` traits in various ways. So far, though, we’ve avoided getting too -far into the details of how they work or how they fit together, which is fine -most of the time for your day-to-day Rust work. Sometimes, though, you’ll -encounter situations where you’ll need to understand a few more of these -details. In this section, we’ll dig in just enough to help in those scenarios, -still leaving the _really_ deep dive for other documentation. +در طول این فصل، از ویژگی‌های `Future`، `Pin`، `Unpin`، `Stream`، و `StreamExt` به روش‌های مختلفی استفاده کرده‌ایم. تاکنون، از ورود بیش از حد به جزئیات نحوه کارکرد یا چگونگی تطبیق آن‌ها با یکدیگر اجتناب کرده‌ایم، که برای بیشتر کارهای روزمره شما با Rust کافی است. با این حال، گاهی اوقات با موقعیت‌هایی مواجه می‌شوید که نیاز دارید کمی بیشتر از این جزئیات را بفهمید. در این بخش، به اندازه‌ای به این موضوع می‌پردازیم که در این سناریوها کمک کند، در حالی که بررسی _عمیق‌تر_ را به مستندات دیگر می‌سپاریم. <!-- Old headings. Do not remove or links may break. --> <a id="future"></a> -### The `Future` Trait +### ویژگی `Future` -Let’s start by taking a closer look at how the `Future` trait works. Here’s how -Rust defines it: +بیایید با بررسی دقیق‌تر نحوه عملکرد ویژگی `Future` شروع کنیم. در اینجا نحوه تعریف آن در Rust آمده است: ```rust use std::pin::Pin; @@ -32,16 +21,9 @@ pub trait Future { } ``` -That trait definition includes a bunch of new types and also some syntax we -haven’t seen before, so let’s walk through the definition piece by piece. +این تعریف Trait شامل چندین نوع جدید و همچنین نحوی است که قبلاً ندیده‌ایم، بنابراین بیایید قطعه به قطعه آن را بررسی کنیم. -First, `Future`’s associated type `Output` says what the future resolves to. -This is analogous to the `Item` associated type for the `Iterator` trait. -Second, `Future` also has the `poll` method, which takes a special `Pin` -reference for its `self` parameter and a mutable reference to a `Context` type, -and returns a `Poll<Self::Output>`. We’ll talk more about `Pin` and -`Context` in a moment. For now, let’s focus on what the method returns, -the `Poll` type: +ابتدا، نوع وابسته `Output` در ویژگی `Future` مشخص می‌کند که نتیجه future چه خواهد بود. این شبیه به نوع وابسته `Item` در ویژگی `Iterator` است. دوم، ویژگی `Future` همچنین متد `poll` را دارد که یک مرجع خاص `Pin` برای پارامتر `self` و یک مرجع متغیر به نوع `Context` می‌گیرد و یک `Poll<Self::Output>` بازمی‌گرداند. در ادامه درباره `Pin` و `Context` بیشتر صحبت خواهیم کرد. فعلاً بیایید روی چیزی که متد بازمی‌گرداند، یعنی نوع `Poll`، تمرکز کنیم: ```rust enum Poll<T> { @@ -50,22 +32,11 @@ enum Poll<T> { } ``` -This `Poll` type is similar to an `Option`. It has one variant that has a value, -`Ready(T)`, and one which does not, `Pending`. `Poll` means something quite -different from `Option`, though! The `Pending` variant indicates that the future -still has work to do, so the caller will need to check again later. The `Ready` -variant indicates that the future has finished its work and the `T` value is -available. +نوع `Poll` شبیه به یک `Option` است. این نوع دو حالت دارد: یکی `Ready(T)` که شامل یک مقدار است و دیگری `Pending` که شامل مقدار نیست. با این حال، `Poll` معنای کاملاً متفاوتی از `Option` دارد! حالت `Pending` نشان می‌دهد که future هنوز کارهایی برای انجام دادن دارد، بنابراین فراخواننده باید بعداً دوباره بررسی کند. حالت `Ready` نشان می‌دهد که future کار خود را به پایان رسانده و مقدار `T` در دسترس است. -> Note: With most futures, the caller should not call `poll` again after the -> future has returned `Ready`. Many futures will panic if polled again after -> becoming ready. Futures that are safe to poll again will say so explicitly in -> their documentation. This is similar to how `Iterator::next` behaves. +> نکته: برای بیشتر futures، فراخواننده نباید پس از اینکه future مقدار `Ready` بازگرداند، دوباره `poll` را فراخوانی کند. بسیاری از futures اگر پس از آماده شدن دوباره `poll` شوند، دچار وحشت (_panic_) می‌شوند. futuresی که ایمن برای poll دوباره هستند، به‌طور صریح این موضوع را در مستندات خود ذکر خواهند کرد. این شبیه به نحوه رفتار `Iterator::next` است. -When you see code that uses `await`, Rust compiles it under the hood to code -that calls `poll`. If you look back at Listing 17-4, where we printed out the -page title for a single URL once it resolved, Rust compiles it into something -kind of (although not exactly) like this: +وقتی کدی را می‌بینید که از `await` استفاده می‌کند، Rust آن را در پشت صحنه به کدی که `poll` را فراخوانی می‌کند کامپایل می‌کند. اگر به لیست ۱۷-۴ که در آن عنوان صفحه برای یک URL واحد پس از حل‌شدن چاپ شد، نگاهی بیندازید، Rust آن را به چیزی که (اگرچه دقیقاً نه، اما تقریباً) شبیه به این است کامپایل می‌کند: ```rust,ignore match page_title(url).poll() { @@ -79,9 +50,7 @@ match page_title(url).poll() { } ``` -What should we do when the future is still `Pending`? We need some way to try -again, and again, and again, until the future is finally ready. In other words, -we need a loop: +وقتی که future هنوز در حالت `Pending` است، چه کاری باید انجام دهیم؟ نیاز داریم به نوعی دوباره امتحان کنیم، و این کار را بارها تکرار کنیم، تا زمانی که future در نهایت آماده شود. به عبارت دیگر، نیاز به یک حلقه داریم: ```rust,ignore let mut page_title_fut = page_title(url); @@ -98,35 +67,17 @@ loop { } ``` -If Rust compiled it to exactly that code, though, every `await` would be -blocking—exactly the opposite of what we were going for! Instead, Rust makes -sure that the loop can hand off control to something that can pause work on this -future to work on other futures and then check this one again later. As we’ve -seen, that something is an async runtime, and this scheduling and coordination -work is one of its main jobs. - -Earlier in the chapter, we described waiting on `rx.recv`. The `recv` call -returns a future, and awaiting the future polls it. We noted that a runtime will -pause the future until it’s ready with either `Some(message)` or `None` when the -channel closes. With our deeper understanding of the `Future` trait, and -specifically `Future::poll`, we can see how that works. The runtime knows the -future isn’t ready when it returns `Poll::Pending`. Conversely, the runtime -knows the future _is_ ready and advances it when `poll` returns -`Poll::Ready(Some(message))` or `Poll::Ready(None)`. - -The exact details of how a runtime does that are beyond the scope of this book, -but the key is to see the basic mechanics of futures: a runtime _polls_ each -future it is responsible for, putting the future back to sleep when it is not -yet ready. +اگر Rust دقیقاً این کد را کامپایل می‌کرد، هر `await` مسدودکننده (_blocking_) می‌شد—دقیقاً برعکس چیزی که می‌خواستیم! در عوض، Rust اطمینان حاصل می‌کند که حلقه بتواند کنترل را به چیزی واگذار کند که بتواند کار روی این future را متوقف کرده، روی futures دیگر کار کند، و سپس دوباره این یکی را بررسی کند. همان‌طور که دیدیم، این وظیفه یک runtime async است، و این برنامه‌ریزی و هماهنگی یکی از وظایف اصلی آن است. -<!-- Old headings. Do not remove or links may break. --> +در ابتدای فصل، درباره انتظار برای `rx.recv` صحبت کردیم. فراخوانی `recv` یک future بازمی‌گرداند و منتظر شدن برای future آن را poll می‌کند. اشاره کردیم که یک runtime future را تا زمانی که آماده شود—چه با `Some(message)` یا با `None` در صورت بسته شدن کانال—متوقف می‌کند. با درک عمیق‌تر از ویژگی `Future` و به‌طور خاص `Future::poll`، می‌توانیم ببینیم این چگونه کار می‌کند. وقتی future مقدار `Poll::Pending` بازمی‌گرداند، runtime می‌داند که آماده نیست. برعکس، وقتی `poll` مقدار `Poll::Ready(Some(message))` یا `Poll::Ready(None)` بازمی‌گرداند، runtime می‌داند که future آماده است و آن را پیش می‌برد. + +جزئیات دقیق نحوه انجام این کار توسط یک runtime فراتر از محدوده این کتاب است، اما نکته کلیدی این است که مکانیک پایه‌ای futures را ببینیم: یک runtime هر future که مسئول آن است را _poll_ می‌کند و وقتی هنوز آماده نیست، future را دوباره به حالت خواب می‌برد. <a id="pinning-and-the-pin-and-unpin-traits"></a> -### The `Pin` and `Unpin` Traits +### ویژگی‌های `Pin` و `Unpin` -When we introduced the idea of pinning in Listing 17-16, we ran into a very -gnarly error message. Here is the relevant part of it again: +وقتی مفهوم pinning را در لیست ۱۷-۱۶ معرفی کردیم، با یک پیام خطای بسیار پیچیده مواجه شدیم. در اینجا بخش مرتبط با آن دوباره آمده است: <!-- manual-regeneration cd listings/ch17-async-await/listing-17-16 @@ -154,23 +105,14 @@ note: required by a bound in `futures_util::future::join_all::JoinAll` | ^^^^^^ required by this bound in `JoinAll` ``` -This error message tells us not only that we need to pin the values but also why -pinning is required. The `trpl::join_all` function returns a struct called -`JoinAll`. That struct is generic over a type `F`, which is constrained to -implement the `Future` trait. Directly awaiting a future with `await` pins the -future implicitly. That’s why we don’t need to use `pin!` everywhere we want to -await futures. +این پیام خطا نه تنها به ما می‌گوید که باید مقادیر را pin کنیم، بلکه دلیل نیاز به pinning را نیز توضیح می‌دهد. تابع `trpl::join_all` یک ساختار به نام `JoinAll` بازمی‌گرداند. این ساختار به نوعی عمومی به نام `F` وابسته است که محدود به پیاده‌سازی ویژگی `Future` است. منتظر شدن مستقیم یک future با `await`، future را به‌طور ضمنی pin می‌کند. به همین دلیل نیازی نیست که از `pin!` در همه جاهایی که می‌خواهیم برای futures منتظر بمانیم، استفاده کنیم. -However, we’re not directly awaiting a future here. Instead, we construct a new -future, `JoinAll`, by passing a collection of futures to the `join_all` -function. The signature for `join_all` requires that the types of the items in -the collection all implement the `Future` trait, and `Box<T>` implements -`Future` only if the `T` it wraps is a future that implements the `Unpin` trait. -That’s a lot to absorb! To really understand it, let’s dive a little further -into how the `Future` trait actually works, in particular around _pinning_. +با این حال، ما اینجا مستقیماً منتظر یک future نیستیم. در عوض، یک future جدید به نام `JoinAll` می‌سازیم با ارسال مجموعه‌ای از futures به تابع `join_all`. امضای `join_all` نیاز دارد که نوع آیتم‌های مجموعه، ویژگی `Future` را پیاده‌سازی کنند، و `Box<T>` فقط در صورتی ویژگی `Future` را پیاده‌سازی می‌کند که `T` که بسته‌بندی می‌کند، یک future باشد که ویژگی `Unpin` را پیاده‌سازی کرده است. -Look again at the definition of the `Future` trait: +این اطلاعات زیادی برای هضم کردن است! برای درک واقعی آن، بیایید کمی بیشتر به نحوه کار واقعی ویژگی `Future`، به‌ویژه در ارتباط با _pinning_، بپردازیم. + +دوباره به تعریف ویژگی `Future` نگاه کنید: ```rust use std::pin::Pin; @@ -179,232 +121,118 @@ use std::task::{Context, Poll}; pub trait Future { type Output; - // Required method + // متد مورد نیاز fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; } ``` -The `cx` parameter and its `Context` type are the key to how a runtime actually -knows when to check any given future while still being lazy. Again, the details -of how that works are beyond the scope of this chapter, and you generally only -need to think about this when writing a custom `Future` implementation. We’ll -focus instead on the type for `self`, as this is the first time we’ve seen a -method where `self` has a type annotation. A type annotation for `self` works -like type annotations for other function parameters, but with two key -differences: - -- It tells Rust what type `self` must be for the method to be called. - -- It can’t be just any type. It’s restricted to the type on which the method is - implemented, a reference or smart pointer to that type, or a `Pin` wrapping a - reference to that type. - -We’ll see more on this syntax in [Chapter 18][ch-18]<!-- ignore -->. For now, -it’s enough to know that if we want to poll a future to check whether it is -`Pending` or `Ready(Output)`, we need a `Pin`-wrapped mutable reference to the -type. - -`Pin` is a wrapper for pointer-like types such as `&`, `&mut`, `Box`, and `Rc`. -(Technically, `Pin` works with types that implement the `Deref` or `DerefMut` -traits, but this is effectively equivalent to working only with pointers.) `Pin` -is not a pointer itself and doesn’t have any behavior of its own like `Rc` and -`Arc` do with reference counting; it’s purely a tool the compiler can use to -enforce constraints on pointer usage. - -Recalling that `await` is implemented in terms of calls to `poll` starts to -explain the error message we saw earlier, but that was in terms of `Unpin`, not -`Pin`. So how exactly does `Pin` relate to `Unpin`, and why does `Future` need -`self` to be in a `Pin` type to call `poll`? - -Remember from earlier in this chapter a series of await points in a future get -compiled into a state machine, and the compiler makes sure that state machine -follows all of Rust’s normal rules around safety, including borrowing and -ownership. To make that work, Rust looks at what data is needed between one -await point and either the next await point or the end of the async block. It -then creates a corresponding variant in the compiled state machine. Each variant -gets the access it needs to the data that will be used in that section of the -source code, whether by taking ownership of that data or by getting a mutable or -immutable reference to it. - -So far, so good: if we get anything wrong about the ownership or references in a -given async block, the borrow checker will tell us. When we want to move around -the future that corresponds to that block—like moving it into a `Vec` to pass to -`join_all`—things get trickier. - -When we move a future—whether by pushing it into a data structure to use as an -iterator with `join_all` or by returning it from a function—that actually means -moving the state machine Rust creates for us. And unlike most other types in -Rust, the futures Rust creates for async blocks can end up with references to -themselves in the fields of any given variant, as shown in the simplified illustration in Figure 17-4. +پارامتر `cx` و نوع آن، `Context`، کلید اصلی برای این است که یک runtime چگونه می‌داند چه زمانی یک future خاص را بررسی کند، در حالی که همچنان تنبلی (_lazy_) باقی می‌ماند. باز هم، جزئیات نحوه کار این فرآیند فراتر از محدوده این فصل است، و معمولاً تنها زمانی که بخواهید یک پیاده‌سازی سفارشی برای `Future` بنویسید، نیاز به فکر کردن به این موضوع دارید. در عوض، ما بر روی نوع `self` تمرکز می‌کنیم، زیرا این اولین باری است که یک متد با یک نوع مشخص برای `self` روبرو می‌شویم. یک نوع مشخص برای `self` مانند نوع‌های مشخص برای سایر پارامترهای تابع عمل می‌کند، اما با دو تفاوت کلیدی: + +- به Rust می‌گوید که نوع `self` برای فراخوانی متد باید چه باشد. + +- نمی‌تواند هر نوعی باشد. این نوع به نوعی که متد روی آن پیاده‌سازی شده است، یا یک مرجع یا اشاره‌گر هوشمند به آن نوع، یا یک `Pin` که یک مرجع به آن نوع را بسته‌بندی می‌کند، محدود است. + +در [فصل ۱۸][ch-18]<!-- ignore --> بیشتر درباره این سینتکس صحبت خواهیم کرد. فعلاً کافی است بدانیم که اگر بخواهیم یک future را poll کنیم تا بررسی کنیم که آیا `Pending` یا `Ready(Output)` است، به یک مرجع متغیر بسته‌بندی‌شده در `Pin` برای آن نوع نیاز داریم. + +`Pin` یک بسته‌بندی برای انواع اشاره‌گر مانند `&`، `&mut`، `Box`، و `Rc` است. (به‌طور فنی، `Pin` با نوع‌هایی کار می‌کند که ویژگی‌های `Deref` یا `DerefMut` را پیاده‌سازی می‌کنند، اما این به طور مؤثر معادل کار با اشاره‌گرها است.) `Pin` خودش یک اشاره‌گر نیست و هیچ رفتاری مانند `Rc` و `Arc` که شمارش مرجع انجام می‌دهند ندارد؛ این صرفاً یک ابزار است که کامپایلر می‌تواند برای اعمال محدودیت‌ها در استفاده از اشاره‌گرها استفاده کند. + +به یاد آوردن این که `await` بر اساس فراخوانی‌های `poll` پیاده‌سازی شده است، شروع به توضیح پیام خطایی که قبلاً دیدیم می‌کند، اما آن پیام در مورد `Unpin` بود، نه `Pin`. پس دقیقاً چگونه `Pin` با `Unpin` مرتبط است، و چرا `Future` نیاز دارد که `self` در یک نوع `Pin` باشد تا بتواند `poll` را فراخوانی کند؟ + +به یاد بیاورید که در اوایل این فصل، یک سری از نقاط انتظار (_await points_) در یک future به یک ماشین حالت کامپایل می‌شوند، و کامپایلر اطمینان حاصل می‌کند که این ماشین حالت تمام قوانین معمول ایمنی Rust، از جمله قرض‌گیری و مالکیت، را دنبال می‌کند. برای اینکه این کار انجام شود، Rust بررسی می‌کند که چه داده‌ای بین یک نقطه انتظار و یا نقطه انتظار بعدی یا پایان بلوک async مورد نیاز است. سپس یک حالت متناظر در ماشین حالت کامپایل‌شده ایجاد می‌کند. هر حالت دسترسی لازم به داده‌هایی که در آن بخش از کد منبع استفاده می‌شوند را دریافت می‌کند، چه با گرفتن مالکیت آن داده‌ها یا با دریافت یک مرجع متغیر یا غیرمتغیر به آن. + +تا اینجا خوب است: اگر در مورد مالکیت یا مراجع در یک بلوک async خطایی داشته باشیم، borrow checker به ما اطلاع می‌دهد. اما وقتی بخواهیم futureای که به آن بلوک مربوط می‌شود را جابه‌جا کنیم—مثلاً آن را به یک ساختار داده push کنیم تا به‌عنوان یک iterator با `join_all` استفاده شود یا آن را از یک تابع بازگردانیم—مسائل پیچیده‌تر می‌شوند. + +وقتی یک future را جابه‌جا می‌کنیم—چه با push کردن آن به یک ساختار داده برای استفاده به‌عنوان iterator با `join_all` یا با بازگرداندن آن از یک تابع—این در واقع به معنای جابه‌جا کردن ماشین حالتی است که Rust برای ما ایجاد می‌کند. و برخلاف بیشتر انواع دیگر در Rust، futureهایی که Rust برای بلوک‌های async ایجاد می‌کند، می‌توانند در فیلدهای هر حالت معین، دارای مراجع به خودشان باشند، همان‌طور که در تصویر ساده‌شده‌ای که در شکل ۱۷-۴ نشان داده شده است. <figure> <img alt="A single-column, three-row table representing a future, fut1, which has data values 0 and 1 in the first two rows and an arrow pointing from the third row back to the second row, representing an internal reference within the future." src="img/trpl17-04.svg" class="center" /> -<figcaption>Figure 17-4: A self-referential data type.</figcaption> +<figcaption>شکل 17-4: یک نوع داده خودارجاعی.</figcaption> </figure> -By default, though, any object that has a reference to itself is unsafe to move, -because references always point to the actual memory address of whatever they -refer to (see Figure 17-5). If you move the data structure itself, those -internal references will be left pointing to the old location. However, that -memory location is now invalid. For one thing, its value will not be updated -when you make changes to the data structure. For another—more important—thing, -the computer is now free to reuse that memory for other purposes! You could end -up reading completely unrelated data later. +به‌طور پیش‌فرض، هر شیئی که مرجعی به خودش دارد، جابه‌جا کردن آن ناایمن است، زیرا مراجع همیشه به آدرس حافظه واقعی چیزی که به آن اشاره می‌کنند اشاره دارند (نگاه کنید به شکل ۱۷-۵). اگر خود ساختار داده را جابه‌جا کنید، آن مراجع داخلی همچنان به مکان قدیمی اشاره می‌کنند. با این حال، آن مکان حافظه اکنون نامعتبر است. از یک طرف، مقدار آن هنگام ایجاد تغییرات در ساختار داده به‌روزرسانی نمی‌شود. از طرف دیگر—و مهم‌تر—کامپیوتر اکنون می‌تواند آن مکان حافظه را برای مقاصد دیگر بازاستفاده کند! ممکن است بعداً داده‌هایی کاملاً نامرتبط بخوانید. <figure> <img alt="Two tables, depicting two futures, fut1 and fut2, each of which has one column and three rows, representing the result of having moved a future out of fut1 into fut2. The first, fut1, is grayed out, with a question mark in each index, representing unknown memory. The second, fut2, has 0 and 1 in the first and second rows and an arrow pointing from its third row back to the second row of fut1, representing a pointer that is referencing the old location in memory of the future before it was moved." src="img/trpl17-05.svg" class="center" /> -<figcaption>Figure 17-5: The unsafe result of moving a self-referential data type</figcaption> +<figcaption>شکل ۱۷-۵: نتیجه ناایمن جابه‌جایی یک نوع داده که به خودش ارجاع دارد</figcaption> </figure> -Theoretically, the Rust compiler could try to update every reference to an -object whenever it gets moved, but that could add a lot of performance overhead, -especially if a whole web of references needs updating. If we could instead make -sure the data structure in question _doesn’t move in memory_, we wouldn’t have -to update any references. This is exactly what Rust’s borrow checker requires: -in safe code, it prevents you from moving any item with an active reference to -it. +از نظر تئوری، کامپایلر Rust می‌تواند سعی کند هر مرجع به یک شیء را هر زمان که جابه‌جا می‌شود، به‌روزرسانی کند، اما این کار می‌تواند سربار عملکرد زیادی ایجاد کند، به‌ویژه اگر یک شبکه کامل از مراجع نیاز به به‌روزرسانی داشته باشد. اگر بتوانیم به جای آن مطمئن شویم که ساختار داده مورد نظر _در حافظه جابه‌جا نمی‌شود_، نیازی به به‌روزرسانی مراجع نخواهیم داشت. این دقیقاً همان چیزی است که borrow checker در Rust نیاز دارد: در کد ایمن، از جابه‌جا کردن هر آیتمی که مرجع فعالی به آن دارد جلوگیری می‌کند. -`Pin` builds on that to give us the exact guarantee we need. When we _pin_ a -value by wrapping a pointer to that value in `Pin`, it can no longer move. Thus, -if you have `Pin<Box<SomeType>>`, you actually pin the `SomeType` value, _not_ -the `Box` pointer. Figure 17-6 illustrates this process. +`Pin` بر اساس این اصل عمل می‌کند و تضمین دقیقی که نیاز داریم را ارائه می‌دهد. وقتی یک مقدار را با بسته‌بندی یک اشاره‌گر به آن مقدار در `Pin` _pin_ می‌کنیم، دیگر نمی‌تواند جابه‌جا شود. بنابراین، اگر `Pin<Box<SomeType>>` داشته باشید، در واقع مقدار `SomeType` را pin می‌کنید، _نه_ اشاره‌گر `Box`. شکل ۱۷-۶ این فرآیند را نشان می‌دهد. <figure> <img alt="Three boxes laid out side by side. The first is labeled “Pin”, the second “b1”, and the third “pinned”. Within “pinned” is a table labeled “fut”, with a single column; it represents a future with cells for each part of the data structure. Its first cell has the value “0”, its second cell has an arrow coming out of it and pointing to the fourth and final cell, which has the value “1” in it, and the third cell has dashed lines and an ellipsis to indicate there may be other parts to the data structure. All together, the “fut” table represents a future which is self-referential. An arrow leaves the box labeled “Pin”, goes through the box labeled “b1” and has terminates inside the “pinned” box at the “fut” table." src="img/trpl17-06.svg" class="center" /> -<figcaption>Figure 17-6: Pinning a `Box` that points to a self-referential future type.</figcaption> +<figcaption>شکل 17-6: pin کردن یک `Box` که به یک نوع آینده خودارجاعی اشاره می‌کند.</figcaption> </figure> -In fact, the `Box` pointer can still move around freely. Remember: we care about -making sure the data ultimately being referenced stays in place. If a pointer -moves around, _but the data it points to is in the same place_, as in Figure -17-7, there’s no potential problem. As an independent exercise, look at the docs -for the types as well as the `std::pin` module and try to work out how you’d do -this with a `Pin` wrapping a `Box`.) The key is that the self-referential type -itself cannot move, because it is still pinned. +در واقع، اشاره‌گر (Pointer) `Box` هنوز می‌تواند به‌طور آزاد جابه‌جا شود. به یاد داشته باشید: ما به مطمئن شدن از اینکه داده‌ای که در نهایت به آن ارجاع داده می‌شود در جای خود باقی می‌ماند اهمیت می‌دهیم. اگر یک اشاره‌گر (Pointer) جابه‌جا شود اما داده‌ای که به آن اشاره می‌کند در همان مکان باقی بماند، همانطور که در شکل 17-7 نشان داده شده است، هیچ مشکلی پیش نمی‌آید. (چگونگی انجام این کار با یک `Pin` که یک `Box` را می‌پیچد فراتر از بحث این بخش خاص است، اما می‌تواند تمرین خوبی باشد! اگر به مستندات نوع‌ها و همچنین ماژول `std::pin` نگاه کنید، ممکن است بتوانید بفهمید چگونه این کار را انجام دهید.) نکته کلیدی این است که نوع خودارجاعی خود نمی‌تواند جابه‌جا شود، زیرا همچنان pin شده است. <figure> <img alt="Four boxes laid out in three rough columns, identical to the previous diagram with a change to the second column. Now there are two boxes in the second column, labeled “b1” and “b2”, “b1” is grayed out, and the arrow from “Pin” goes through “b2” instead of “b1”, indicating that the pointer has moved from “b1” to “b2”, but the data in “pinned” has not moved." src="img/trpl17-07.svg" class="center" /> -<figcaption>Figure 17-7: Moving a `Box` which points to a self-referential future type.</figcaption> +<figcaption>شکل 17-7: جابه‌جایی یک `Box` که به یک نوع آینده خودارجاعی اشاره می‌کند.</figcaption> </figure> -However, most types are perfectly safe to move around, even if they happen to be -behind a `Pin` wrapper. We only need to think about pinning when items have -internal references. Primitive values such as numbers and Booleans are safe -because they obviously don’t have any internal references. Neither do most types -you normally work with in Rust. You can move around a `Vec`, for example, -without worrying. Given only what we have seen so far, if you have a -`Pin<Vec<String>>`, you’d have to do everything via the safe but restrictive -APIs provided by `Pin`, even though a `Vec<String>` is always safe to move if -there are no other references to it. We need a way to tell the compiler that -it’s fine to move items around in cases like this—and that’s where `Unpin` comes -into play. - -`Unpin` is a marker trait, similar to the `Send` and `Sync` traits we saw in -Chapter 16, and thus has no functionality of its own. Marker traits exist only -to tell the compiler it’s safe to use the type implementing a given trait in a -particular context. `Unpin` informs the compiler that a given type does _not_ -need to uphold any guarantees about whether the value in question can be safely -moved. +با این حال، اکثر نوع‌ها کاملاً ایمن هستند که در حافظه جابه‌جا شوند، حتی اگر درون یک پوشش `Pin` قرار داشته باشند. ما فقط زمانی نیاز داریم به موضوع pinning فکر کنیم که آیتم‌ها دارای رفرانس‌های داخلی باشند. مقادیر اولیه‌ای مثل اعداد و Booleanها ایمن هستند، چون به‌وضوح هیچ رفرانس داخلی‌ای ندارند. بیشتر نوع‌هایی که معمولاً در Rust با آن‌ها کار می‌کنید نیز رفرانس داخلی ندارند. برای مثال، شما می‌توانید یک `Vec` را بدون نگرانی جابه‌جا کنید. -<!-- - The inline `<code>` in the next block is to allow the inline `<em>` inside it, - matching what NoStarch does style-wise, and emphasizing within the text here - that it is something distinct from a normal type. ---> +با توجه به چیزهایی که تا این‌جا دیده‌ایم، اگر یک `Pin<Vec<String>>` داشته باشید، مجبورید تمام عملیات را از طریق APIهای امن اما محدودکننده‌ای که `Pin` ارائه می‌دهد انجام دهید، حتی با این‌که یک `Vec<String>` همیشه ایمن است که جابه‌جا شود—مشروط به این‌که رفرانس دیگری به آن وجود نداشته باشد. ما به روشی نیاز داریم تا به کامپایلر اعلام کنیم که در چنین مواردی جابه‌جایی آیتم‌ها مشکلی ندارد—و این دقیقاً جایی است که `Unpin` وارد می‌شود. + +`Unpin` یک ویژگی علامت‌گذار (_marker trait_) است، مشابه ویژگی‌های `Send` و `Sync` که در فصل ۱۶ دیدیم، و بنابراین هیچ عملکردی از خود ندارد. ویژگی‌های علامت‌گذار فقط برای این وجود دارند که به کامپایلر بگویند استفاده از نوعی که یک ویژگی خاص را پیاده‌سازی می‌کند در یک زمینه خاص ایمن است. `Unpin` به کامپایلر اطلاع می‌دهد که یک نوع خاص _نیازی_ به تضمین اینکه مقدار مربوطه به‌صورت ایمن جابه‌جا می‌شود، ندارد. -Just as with `Send` and `Sync`, the compiler implements `Unpin` automatically -for all types where it can prove it is safe. A special case, again similar to -`Send` and `Sync`, is where `Unpin` is _not_ implemented for a type. The -notation for this is <code>impl !Unpin for <em>SomeType</em></code>, where -<code><em>SomeType</em></code> is the name of a type that _does_ need to uphold -those guarantees to be safe whenever a pointer to that type is used in a `Pin`. +مشابه `Send` و `Sync`، کامپایلر به‌طور خودکار `Unpin` را برای تمام انواعی که می‌تواند ثابت کند ایمن هستند، پیاده‌سازی می‌کند. یک مورد خاص، دوباره مشابه `Send` و `Sync`، این است که `Unpin` برای یک نوع _پیاده‌سازی نمی‌شود_. نشانه‌گذاری برای این حالت به شکل <code>impl !Unpin for <em>SomeType</em></code> است، که در آن <code><em>SomeType</em></code> نام نوعی است که _باید_ آن تضمین‌ها را برای ایمن بودن، هر زمان که اشاره‌گری به آن نوع در یک `Pin` استفاده می‌شود، حفظ کند. -In other words, there are two things to keep in mind about the relationship -between `Pin` and `Unpin`. First, `Unpin` is the “normal” case, and `!Unpin` is -the special case. Second, whether a type implements `Unpin` or `!Unpin` _only_ -matters when you’re using a pinned pointer to that type like <code>Pin<&mut -<em>SomeType</em>></code>. +به عبارت دیگر، دو نکته در مورد رابطه بین `Pin` و `Unpin` باید در نظر داشته باشید. اول، `Unpin` حالت "معمولی" است و `!Unpin` حالت خاص. دوم، اینکه آیا یک نوع ویژگی `Unpin` یا `!Unpin` را پیاده‌سازی می‌کند _فقط_ زمانی اهمیت دارد که در حال استفاده از یک اشاره‌گر pin شده به آن نوع مانند <code>Pin<&mut <em>SomeType</em>></code> باشید. -To make that concrete, think about a `String`: it has a length and the Unicode -characters that make it up. We can wrap a `String` in `Pin`, as seen in Figure -17-8. However, `String` automatically implements `Unpin`, as do most other types -in Rust. +برای روشن‌تر کردن این موضوع، به یک `String` فکر کنید: این نوع دارای طول و کاراکترهای Unicode است که آن را تشکیل می‌دهند. ما می‌توانیم یک `String` را در `Pin` بسته‌بندی کنیم، همان‌طور که در شکل ۱۷-۸ دیده می‌شود. با این حال، `String` به طور خودکار ویژگی `Unpin` را پیاده‌سازی می‌کند، همان‌طور که بیشتر انواع دیگر در Rust این کار را انجام می‌دهند. <figure> <img alt="Concurrent work flow" src="img/trpl17-08.svg" class="center" /> -<figcaption>Figure 17-8: Pinning a `String`; the dotted line indicates that the `String` implements the `Unpin` trait, and thus is not pinned.</figcaption> +<figcaption>شکل ۱۷-۸: Pin کردن یک `String`؛ خط نقطه‌چین نشان می‌دهد که `String` ویژگی `Unpin` را پیاده‌سازی می‌کند و بنابراین pin نشده است.</figcaption> </figure> -As a result, we can do things that would be illegal if `String` implemented -`!Unpin` instead, such as replacing one string with another at the exact same -location in memory as in Figure 17-9. This doesn’t violate the `Pin` contract, -because `String` has no internal references that make it unsafe to move around! -That is precisely why it implements `Unpin` rather than `!Unpin`. +در نتیجه، می‌توانیم کارهایی انجام دهیم که اگر `String` ویژگی `!Unpin` را پیاده‌سازی می‌کرد غیرقانونی بود، مانند جایگزین کردن یک رشته با رشته‌ای دیگر در همان مکان حافظه، همان‌طور که در شکل ۱۷-۹ نشان داده شده است. این کار قرارداد `Pin` را نقض نمی‌کند، زیرا `String` هیچ مرجع داخلی ندارد که جابه‌جایی آن را ناایمن کند! این دقیقاً دلیلی است که ویژگی `Unpin` را به جای `!Unpin` پیاده‌سازی می‌کند. <figure> <img alt="Concurrent work flow" src="img/trpl17-09.svg" class="center" /> -<figcaption>Figure 17-9: Replacing the `String` with an entirely different `String` in memory.</figcaption> + +<figcaption>شکل 17-9: جایگزینی یک String با یک String کاملاً متفاوت در حافظه.</figcaption> </figure> -Now we know enough to understand the errors reported for that `join_all` call -from back in Listing 17-17. We originally tried to move the futures produced by -async blocks into a `Vec<Box<dyn Future<Output = ()>>>`, but as we’ve seen, -those futures may have internal references, so they don’t implement `Unpin`. -They need to be pinned, and then we can pass the `Pin` type into the `Vec`, -confident that the underlying data in the futures will _not_ be moved. - -`Pin` and `Unpin` are mostly important for building lower-level libraries, or -when you’re building a runtime itself, rather than for day-to-day Rust code. -When you see these traits in error messages, though, now you’ll have a better -idea of how to fix your code! - -> Note: This combination of `Pin` and `Unpin` makes it possible to safely -> implement a whole class of complex types in Rust that would otherwise prove -> challenging because they’re self-referential. Types that require `Pin` show up -> most commonly in async Rust today, but every once in a while, you might see -> them in other contexts, too. +اکنون به‌اندازه کافی می‌دانیم تا خطاهایی که برای آن فراخوانی `join_all` در فهرست 17-17 گزارش شدند را درک کنیم. ما در ابتدا سعی کردیم آینده‌های تولیدشده توسط بلوک‌های async را به یک `Vec<Box<dyn Future<Output = ()>>>` منتقل کنیم، اما همان‌طور که دیدیم، این آینده‌ها ممکن است ارجاعات داخلی داشته باشند، بنابراین ویژگی `Unpin` را پیاده‌سازی نمی‌کنند. آن‌ها نیاز به pin شدن دارند، و سپس می‌توانیم نوع `Pin` را به `Vec` ارسال کنیم، با اطمینان از اینکه داده‌های زیربنایی در آینده‌ها _جابه‌جا نخواهند شد_. + +`Pin` و `Unpin` بیشتر برای ساخت کتابخانه‌های سطح پایین یا وقتی که خودتان یک runtime می‌سازید مهم هستند، نه برای کد روزمره راست. وقتی این Traits را در پیام‌های خطا مشاهده می‌کنید، اکنون ایده بهتری از نحوه رفع کد خواهید داشت! + +> نکته: این ترکیب `Pin` و `Unpin` اجازه می‌دهد که یک کلاس کامل از نوع‌های پیچیده در راست ایمن باشند که در غیر این صورت به دلیل خودارجاعی بودن دشوار برای پیاده‌سازی هستند. نوع‌هایی که نیاز به `Pin` دارند _بیشتر_ در راست async امروزی ظاهر می‌شوند، اما ممکن است—بسیار به‌ندرت!—در زمینه‌های دیگر نیز ببینید. +> +> جزئیات نحوه کار `Pin` و `Unpin` و قوانینی که باید رعایت کنند، به‌طور گسترده در مستندات API برای `std::pin` پوشش داده شده‌اند، بنابراین اگر می‌خواهید آن‌ها را عمیق‌تر درک کنید، این مکان خوبی برای شروع است. > -> The specifics of how `Pin` and `Unpin` work, and the rules they’re required -> to uphold, are covered extensively in the API documentation for `std::pin`, so -> if you’re interested in learning more, that’s a great place to start. +> اگر می‌خواهید بفهمید که "در پشت صحنه" چگونه کار می‌کنند، کتاب رسمی [_برنامه‌نویسی ناهمگام در راست_][async-book] پاسخگوی شماست: > -> If you want to understand how things work under the hood in even more detail, -> see Chapters [2][under-the-hood] and [4][pinning] of [_Asynchronous -> Programming in Rust_][async-book]. - -### The `Stream` Trait - -Now that you have a deeper grasp on the `Future`, `Pin`, and `Unpin` traits, we -can turn our attention to the `Stream` trait. As you learned earlier in the -chapter, streams are similar to asynchronous iterators. Unlike `Iterator` and -`Future`, however, `Stream` has no definition in the standard library as of this -writing, but there _is_ a very common definition from the `futures` crate used -throughout the ecosystem. - -Let’s review the definitions of the `Iterator` and `Future` traits before -looking at how a `Stream` trait might merge them together. From `Iterator`, we -have the idea of a sequence: its `next` method provides an `Option<Self::Item>`. -From `Future`, we have the idea of readiness over time: its `poll` method -provides a `Poll<Self::Output>`. To represent a sequence of items that become -ready over time, we define a `Stream` trait that puts those features together: +> - [Chapter 2: Under the Hood: Executing Futures and Tasks][under-the-hood] +> - [Chapter 4: Pinning][pinning] + +### The Stream Trait + +اکنون که درک عمیق‌تری از Traits‌های `Future`، `Pin`، و `Unpin` داریم، می‌توانیم توجه خود را به Trait `Stream` معطوف کنیم. همانطور که در بخش معرفی streams توضیح داده شد، streams مشابه iteratorهای ناهمگام هستند. برخلاف `Iterator` و `Future`، در زمان نگارش این متن، تعریف `Stream` در کتابخانه استاندارد وجود ندارد، اما یک تعریف بسیار رایج از crate `futures` وجود دارد که در سراسر اکوسیستم استفاده می‌شود. + +بیایید تعاریف Traits‌های `Iterator` و `Future` را مرور کنیم تا بتوانیم تصور کنیم یک Trait `Stream` که این دو را ترکیب می‌کند چگونه ممکن است به نظر برسد. از `Iterator`، مفهوم یک توالی را داریم: متد `next` آن یک `Option<Self::Item>` فراهم می‌کند. از `Future`، مفهوم آماده شدن در طول زمان را داریم: متد `poll` آن یک `Poll<Self::Output>` فراهم می‌کند. برای نمایش یک توالی از آیتم‌هایی که در طول زمان آماده می‌شوند، یک Trait `Stream` تعریف می‌کنیم که این ویژگی‌ها را ترکیب می‌کند: ```rust use std::pin::Pin; @@ -420,29 +248,15 @@ trait Stream { } ``` -The `Stream` trait defines an associated type called `Item` for the type of the -items produced by the stream. This is similar to `Iterator`, where there may be -zero to many items, and unlike `Future`, where there is always a single -`Output`, even if it’s the unit type `()`. - -`Stream` also defines a method to get those items. We call it `poll_next`, to -make it clear that it polls in the same way `Future::poll` does and produces a -sequence of items in the same way `Iterator::next` does. Its return type -combines `Poll` with `Option`. The outer type is `Poll`, because it has to be -checked for readiness, just as a future does. The inner type is `Option`, -because it needs to signal whether there are more messages, just as an iterator -does. - -Something very similar to this definition will likely end up as part of Rust’s -standard library. In the meantime, it’s part of the toolkit of most runtimes, so -you can rely on it, and everything we cover next should generally apply! - -In the example we saw in the section on streaming, though, we didn’t use -`poll_next` _or_ `Stream`, but instead used `next` and `StreamExt`. We _could_ -work directly in terms of the `poll_next` API by hand-writing our own `Stream` -state machines, of course, just as we _could_ work with futures directly via -their `poll` method. Using `await` is much nicer, though, and the `StreamExt` -trait supplies the `next` method so we can do just that: + +Trait `Stream` یک نوع مرتبط به نام `Item` برای نوع آیتم‌هایی که توسط stream تولید می‌شوند تعریف می‌کند. این مشابه با `Iterator` است: ممکن است تعداد این آیتم‌ها صفر تا بی‌نهایت باشد، برخلاف `Future` که همیشه یک `Output` واحد دارد (حتی اگر نوع واحد `()` باشد). + +`Stream` همچنین یک متد برای دریافت این آیتم‌ها تعریف می‌کند. ما آن را `poll_next` می‌نامیم تا واضح باشد که این متد به همان روشی که `Future::poll` بررسی می‌کند، آیتم‌ها را بررسی می‌کند و به همان روشی که `Iterator::next` یک توالی از آیتم‌ها تولید می‌کند، آیتم‌ها را تولید می‌کند. نوع بازگشتی آن `Poll` را با `Option` ترکیب می‌کند. نوع خارجی `Poll` است، زیرا باید برای آماده بودن بررسی شود، همان‌طور که یک آینده بررسی می‌شود. نوع داخلی `Option` است، زیرا باید نشان دهد که آیا پیام‌های بیشتری وجود دارد یا نه، همان‌طور که یک iterator انجام می‌دهد. + + +چیزی بسیار مشابه با این احتمالاً در نهایت به‌عنوان بخشی از کتابخانه استاندارد راست استانداردسازی خواهد شد. در حال حاضر، این Trait بخشی از ابزار اکثر runtime‌ها است، بنابراین می‌توانید روی آن حساب کنید و همه چیزهایی که در ادامه می‌بینید عموماً قابل اعمال هستند! + +با این حال، در مثالی که در بخش مربوط به streams دیدیم، ما از `poll_next` یا `Stream` استفاده نکردیم، بلکه از `next` و `StreamExt` استفاده کردیم. البته می‌توانیم مستقیماً از API `poll_next` استفاده کنیم و ماشین‌های حالت `Stream` خود را با دست بنویسیم، همان‌طور که می‌توانیم مستقیماً از طریق متد `poll` با آینده‌ها کار کنیم. اما استفاده از `await` بسیار دلپذیرتر است، بنابراین Trait `StreamExt` متد `next` را فراهم می‌کند تا بتوانیم دقیقاً این کار را انجام دهیم. ```rust {{#rustdoc_include ../listings/ch17-async-await/no-listing-stream-ext/src/lib.rs:here}} @@ -453,34 +267,20 @@ TODO: update this if/when tokio/etc. update their MSRV and switch to using async in traits, since the lack thereof is the reason they do not yet have this. --> -> Note: The actual definition we used earlier in the chapter looks slightly -> different than this, because it supports versions of Rust that did not yet -> support using async functions in traits. As a result, it looks like this: + +> نکته: تعریف واقعی که قبلاً در این فصل استفاده کردیم کمی متفاوت به نظر می‌رسد، زیرا از نسخه‌هایی از راست پشتیبانی می‌کند که هنوز از استفاده از توابع async در Traits پشتیبانی نمی‌کنند. در نتیجه، این‌گونه به نظر می‌رسد: > > ```rust,ignore > fn next(&mut self) -> Next<'_, Self> where Self: Unpin; > ``` > -> That `Next` type is a `struct` that implements `Future` and allows us to name -> the lifetime of the reference to `self` with `Next<'_, Self>`, so that `await` -> can work with this method. - -The `StreamExt` trait is also the home of all the interesting methods available -to use with streams. `StreamExt` is automatically implemented for every type -that implements `Stream`, but these traits are defined separately to enable the -community to iterate on convenience APIs without affecting the foundational -trait. - -In the version of `StreamExt` used in the `trpl` crate, the trait not only -defines the `next` method but also supplies a default implementation of `next` -that correctly handles the details of calling `Stream::poll_next`. This means -that even when you need to write your own streaming data type, you _only_ have -to implement `Stream`, and then anyone who uses your data type can use -`StreamExt` and its methods with it automatically. - -That’s all we’re going to cover for the lower-level details on these traits. To -wrap up, let’s consider how futures (including streams), tasks, and threads all -fit together! +> نوع `Next` یک `struct` است که `Future` را پیاده‌سازی می‌کند و راهی برای نام‌گذاری طول عمر ارجاع به `self` با `Next<'_, Self>` فراهم می‌کند، به‌طوری که `await` بتواند با این متد کار کند! + +Trait `StreamExt` همچنین محل تمام متدهای جالبی است که می‌توان با streams استفاده کرد. `StreamExt` به‌طور خودکار برای هر نوعی که `Stream` را پیاده‌سازی کند، پیاده‌سازی می‌شود، اما این Traits به‌طور جداگانه تعریف شده‌اند تا جامعه بتواند به‌صورت جداگانه روی Trait بنیادی و API‌های راحتی کار کند. + +در نسخه `StreamExt` استفاده‌شده در crate `trpl`، این Trait نه تنها متد `next` را تعریف می‌کند، بلکه یک پیاده‌سازی از `next` ارائه می‌دهد که جزئیات فراخوانی `Stream::poll_next` را به‌درستی مدیریت می‌کند. این بدان معناست که حتی زمانی که نیاز دارید نوع داده‌های جریان خود را بنویسید، فقط کافی است `Stream` را پیاده‌سازی کنید، و سپس هرکسی که از نوع داده شما استفاده کند، می‌تواند به‌طور خودکار از `StreamExt` و متدهای آن با آن استفاده کند. + +این تمام چیزی است که درباره جزئیات سطح پایین این Traits پوشش خواهیم داد. برای جمع‌بندی، بیایید در نظر بگیریم که چگونه آینده‌ها (شامل streams)، تسک‌ها، و نخ‌ها همگی با هم سازگار هستند! [ch-18]: ch18-00-oop.html [async-book]: https://rust-lang.github.io/async-book/ diff --git a/src/ch17-06-futures-tasks-threads.md b/src/ch17-06-futures-tasks-threads.md index 9830087849..289a62455a 100644 --- a/src/ch17-06-futures-tasks-threads.md +++ b/src/ch17-06-futures-tasks-threads.md @@ -1,35 +1,15 @@ -## Putting It All Together: Futures, Tasks, and Threads - -As we saw in [Chapter 16][ch16]<!-- ignore -->, threads provide one approach to -concurrency. We’ve seen another approach in this chapter: using async with -futures and streams. If you‘re wondering when to choose method over the other, -the answer is: it depends! And in many cases, the choice isn’t threads _or_ -async but rather threads _and_ async. - -Many operating systems have supplied threading-based concurrency models for -decades now, and many programming languages support them as a result. However, -these models are not without their tradeoffs. On many operating systems, they -use a fair bit of memory for each thread, and they come with some overhead for -starting up and shutting down. Threads are also only an option when your -operating system and hardware support them. Unlike mainstream desktop and mobile -computers, some embedded systems don’t have an OS at all, so they also don’t -have threads. - -The async model provides a different—and ultimately complementary—set of -tradeoffs. In the async model, concurrent operations don’t require their own -threads. Instead, they can run on tasks, as when we used `trpl::spawn_task` to -kick off work from a synchronous function in the streams section. A task is -similar to a thread, but instead of being managed by the operating system, it’s -managed by library-level code: the runtime. - -In the previous section, we saw that we could build a stream by using an async -channel and spawning an async task we could call from synchronous code. We can -do the exact same thing with a thread. In Listing 17-40, we used -`trpl::spawn_task` and `trpl::sleep`. In Listing 17-41, we replace those with -the `thread::spawn` and `thread::sleep` APIs from the standard library in the -`get_intervals` function. - -<Listing number="17-41" caption="Using the `std::thread` APIs instead of the async `trpl` APIs for the `get_intervals` function" file-name="src/main.rs"> +## جمع‌بندی: Futures، Tasks، و Threads + +همان‌طور که در [فصل ۱۶][ch16]<!-- ignore --> دیدیم، Threads یکی از روش‌های همزمانی را فراهم می‌کنند. در این فصل با روش دیگری آشنا شدیم: استفاده از async با Futures و Streams. اگر برایتان سؤال پیش آمده که چه زمانی باید یکی از این روش‌ها را انتخاب کنید، پاسخ این است: بستگی دارد! و در بسیاری از موارد، انتخاب فقط بین Threads _یا_ async نیست، بلکه ترکیبی از Threads _و_ async است. + +بسیاری از سیستم‌عامل‌ها مدل‌های همزمانی مبتنی بر Threads را دهه‌هاست که فراهم کرده‌اند و بسیاری از زبان‌های برنامه‌نویسی از این مدل‌ها پشتیبانی می‌کنند. با این حال، این مدل‌ها بدون نقاط ضعف نیستند. در بسیاری از سیستم‌عامل‌ها، هر Thread مقدار زیادی حافظه استفاده می‌کند و راه‌اندازی و خاموش کردن آن‌ها نیز هزینه‌ای به همراه دارد. Threads همچنین فقط زمانی قابل استفاده هستند که سیستم‌عامل و سخت‌افزار شما از آن‌ها پشتیبانی کنند. برخلاف کامپیوترهای دسکتاپ و موبایل اصلی، برخی از سیستم‌های تعبیه‌شده (_embedded systems_) هیچ سیستم‌عاملی ندارند و بنابراین Threads هم ندارند. + +مدل async مجموعه‌ای متفاوت و در نهایت مکمل از مصالحه‌ها را فراهم می‌کند. در مدل async، عملیات همزمان نیازی به Threadهای جداگانه ندارند. در عوض، می‌توانند بر روی Tasks اجرا شوند، همان‌طور که در بخش Streams از `trpl::spawn_task` برای شروع کار از یک تابع همزمان استفاده کردیم. یک Task مشابه یک Thread است، اما به جای اینکه توسط سیستم‌عامل مدیریت شود، توسط کد سطح کتابخانه‌ای یعنی Runtime مدیریت می‌شود. + +در بخش قبلی، دیدیم که می‌توانیم یک Stream با استفاده از یک کانال async و ایجاد یک Task async که می‌توانیم از کد همزمان فراخوانی کنیم، بسازیم. می‌توانیم همین کار را با یک Thread انجام دهیم. در لیست ۱۷-۴۰ از `trpl::spawn_task` و `trpl::sleep` استفاده کردیم. در لیست ۱۷-۴۱، این موارد را با APIهای `thread::spawn` و `thread::sleep` از کتابخانه استاندارد در تابع `get_intervals` جایگزین می‌کنیم. + + +<Listing number="17-41" caption="استفاده از APIهای `std::thread` به جای APIهای async `trpl` برای تابع `get_intervals`" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-41/src/main.rs:threads}} @@ -37,69 +17,26 @@ the `thread::spawn` and `thread::sleep` APIs from the standard library in the </Listing> -If you run this code, the output is identical to that of Listing 17-40. And -notice how little changes here from the perspective of the calling code. What’s -more, even though one of our functions spawned an async task on the runtime and -the other spawned an OS thread, the resulting streams were unaffected by the -differences. - -Despite their similarities, these two approaches behave very differently, -although we might have a hard time measuring it in this very simple example. We -could spawn millions of async tasks on any modern personal computer. If we tried -to do that with threads, we would literally run out of memory! - -However, there’s a reason these APIs are so similar. Threads act as a boundary -for sets of synchronous operations; concurrency is possible _between_ threads. -Tasks act as a boundary for sets of _asynchronous_ operations; concurrency is -possible both _between_ and _within_ tasks, because a task can switch between -futures in its body. Finally, futures are Rust’s most granular unit of -concurrency, and each future may represent a tree of other futures. The -runtime—specifically, its executor—manages tasks, and tasks manage futures. In -that regard, tasks are similar to lightweight, runtime-managed threads with -added capabilities that come from being managed by a runtime instead of by the -operating system. - -This doesn’t mean that async tasks are always better than threads (or vice -versa). Concurrency with threads is in some ways a simpler programming model -than concurrency with `async`. That can be a strength or a weakness. Threads are -somewhat “fire and forget”; they have no native equivalent to a future, so they -simply run to completion without being interrupted except by the operating -system itself. That is, they have no built-in support for _intratask -concurrency_ the way futures do. Threads in Rust also have no mechanisms for -cancellation—a subject we haven’t covered explicitly in this chapter but was -implied by the fact that whenever we ended a future, its state got cleaned up -correctly. - -These limitations also make threads harder to compose than futures. It’s much -more difficult, for example, to use threads to build helpers such as the -`timeout` and `throttle` methods we built earlier in this chapter. The fact that -futures are richer data structures means they can be composed together more -naturally, as we have seen. - -Tasks, then, give us _additional_ control over futures, allowing us to choose -where and how to group them. And it turns out that threads and tasks often work -very well together, because tasks can (at least in some runtimes) be moved -around between threads. In fact, under the hood, the runtime we’ve been -using—including the `spawn_blocking` and `spawn_task` functions—is multithreaded -by default! Many runtimes use an approach called _work stealing_ to -transparently move tasks around between threads, based on how the threads are -currently being utilized, to improve the system’s overall performance. That -approach actually requires threads _and_ tasks, and therefore futures. - -When thinking about which method to use when, consider these rules of thumb: - -- If the work is _very parallelizable_, such as processing a bunch of data where - each part can be processed separately, threads are a better choice. -- If the work is _very concurrent_, such as handling messages from a bunch of - different sources that may come in at different intervals or different rates, - async is a better choice. - -And if you need both parallelism and concurrency, you don’t have to choose -between threads and async. You can use them together freely, letting each one -play the part it’s best at. For example, Listing 17-42 shows a fairly common -example of this kind of mix in real-world Rust code. - -<Listing number="17-42" caption="Sending messages with blocking code in a thread and awaiting the messages in an async block" file-name="src/main.rs"> +اگر این کد را اجرا کنید، خروجی آن دقیقاً مشابه لیست ۱۷-۴۰ خواهد بود. و توجه کنید که از دید کدی که فراخوانی انجام می‌دهد، تغییرات بسیار کمی وجود دارد. علاوه بر این، حتی اگر یکی از توابع ما یک Task async را روی Runtime ایجاد کرده و دیگری یک Thread سیستم‌عامل را ایجاد کرده باشد، Streamهای حاصل از این تفاوت‌ها تأثیری نمی‌گیرند. + +با وجود شباهت‌هایشان، این دو رویکرد رفتارهای بسیار متفاوتی دارند، اگرچه ممکن است در این مثال بسیار ساده سخت باشد این تفاوت‌ها را اندازه‌گیری کنیم. می‌توانیم میلیون‌ها Task async را روی هر کامپیوتر شخصی مدرن ایجاد کنیم. اما اگر بخواهیم همین کار را با Threads انجام دهیم، واقعاً از حافظه خارج خواهیم شد! + +اما دلیلی وجود دارد که این APIها این‌قدر مشابه هستند. نخ‌ها به عنوان مرزی برای مجموعه‌ای از عملیات همزمان عمل می‌کنند؛ همزمانی _بین_ نخ‌ها ممکن است. tasks به عنوان مرزی برای مجموعه‌ای از عملیات _غیرهمزمان_ عمل می‌کنند؛ همزمانی هم _بین_ و هم _درون_ tasks ممکن است، زیرا یک task می‌تواند بین futures در بدنه خود جابه‌جا شود. در نهایت، futures کوچک‌ترین واحد همزمانی در Rust هستند و هر future ممکن است یک درخت از futures دیگر را نمایندگی کند. runtime—به‌ویژه، executor آن—tasks را مدیریت می‌کند و tasks futures را مدیریت می‌کنند. از این نظر، tasks شبیه نخ‌های سبک و مدیریت‌شده توسط runtime هستند که قابلیت‌های بیشتری دارند زیرا توسط runtime به جای سیستم‌عامل مدیریت می‌شوند. + +این بدان معنا نیست که Taskهای async همیشه بهتر از Threads هستند (یا برعکس). همزمانی با Threads از برخی جهات مدل برنامه‌نویسی ساده‌تری نسبت به همزمانی با `async` است. این می‌تواند یک نقطه قوت یا ضعف باشد. Threads تا حدودی "آتش و فراموشی" (_fire and forget_) هستند؛ آن‌ها معادل ذاتی برای یک Future ندارند، بنابراین بدون اینکه جز توسط خود سیستم‌عامل متوقف شوند، تا انتها اجرا می‌شوند. به عبارت دیگر، آن‌ها پشتیبانی داخلی برای _همزمانی درون وظیفه‌ای_ (_intratask concurrency_) مانند Futures ندارند. همچنین، Threads در Rust هیچ مکانیزمی برای لغو ندارند—موضوعی که به‌طور صریح در این فصل به آن پرداخته نشده است، اما از این واقعیت که هر زمان یک Future به پایان می‌رسید، وضعیت آن به درستی پاک‌سازی می‌شد، به‌طور ضمنی بیان شده است. + +این محدودیت‌ها همچنین باعث می‌شوند Threads سخت‌تر از Futures ترکیب شوند. برای مثال، استفاده از Threads برای ساخت ابزارهایی مانند متدهای `timeout` و `throttle` که قبلاً در این فصل ساخته‌ایم، بسیار دشوارتر است. این واقعیت که Futures ساختار داده غنی‌تری هستند به این معناست که آن‌ها می‌توانند به‌طور طبیعی‌تر با هم ترکیب شوند، همان‌طور که دیده‌ایم. + +Tasks، در نتیجه، کنترل _اضافه‌ای_ بر روی Futures به ما می‌دهند و به ما اجازه می‌دهند که انتخاب کنیم کجا و چگونه آن‌ها را گروه‌بندی کنیم. و معلوم می‌شود که Threads و Tasks اغلب به خوبی با هم کار می‌کنند، زیرا Tasks می‌توانند (حداقل در برخی Runtimeها) بین Threads جابه‌جا شوند. در واقع، در پس‌زمینه، Runtimeی که استفاده کرده‌ایم—از جمله توابع `spawn_blocking` و `spawn_task`—به طور پیش‌فرض چند Threadی (_multithreaded_) است! بسیاری از Runtimeها از رویکردی به نام _دزدیدن کار_ (_work stealing_) استفاده می‌کنند تا Tasks را به‌طور شفاف بین Threads جابه‌جا کنند، بر اساس اینکه چگونه Threads در حال حاضر استفاده می‌شوند، تا عملکرد کلی سیستم را بهبود بخشند. این رویکرد در واقع به Threads _و_ Tasks، و بنابراین Futures نیاز دارد. + +وقتی در مورد استفاده از روش‌های مختلف فکر می‌کنید، این قوانین کلی را در نظر بگیرید: + +- اگر کار _به شدت قابل موازی‌سازی_ است، مانند پردازش مقدار زیادی داده که هر بخش می‌تواند جداگانه پردازش شود، Threads انتخاب بهتری هستند. +- اگر کار _به شدت همزمان_ است، مانند مدیریت پیام‌ها از منابع مختلفی که ممکن است در فواصل یا نرخ‌های مختلف وارد شوند، async انتخاب بهتری است. + +و اگر به هر دو موازی‌سازی و همزمانی نیاز دارید، لازم نیست بین Threads و async یکی را انتخاب کنید. می‌توانید از هر دو به طور آزادانه استفاده کنید و اجازه دهید هر کدام نقشی که در آن بهتر هستند را بازی کنند. برای مثال، لیست ۱۷-۴۲ یک نمونه نسبتاً رایج از این نوع ترکیب در کد Rust دنیای واقعی را نشان می‌دهد. + +<Listing number="17-42" caption="ارسال پیام‌ها با کد مسدودکننده در یک نخ و انتظار برای پیام‌ها در یک بلوک async" file-name="src/main.rs"> ```rust {{#rustdoc_include ../listings/ch17-async-await/listing-17-42/src/main.rs:all}} @@ -107,33 +44,22 @@ example of this kind of mix in real-world Rust code. </Listing> -We begin by creating an async channel, then spawn a thread that takes -ownership of the sender side of the channel. Within the thread, we send the -numbers 1 through 10, sleeping for a second between each. Finally, we run a -future created with an async block passed to `trpl::run` just as we have -throughout the chapter. In that future, we await those messages, just as in -the other message-passing examples we have seen. +ما با ایجاد یک کانال async شروع می‌کنیم، سپس یک Thread ایجاد می‌کنیم که مالکیت بخش ارسال‌کننده کانال را به دست می‌گیرد. درون Thread، اعداد ۱ تا ۱۰ را ارسال می‌کنیم و بین هر ارسال یک ثانیه می‌خوابیم. در نهایت، یک Future که با یک بلوک async ایجاد شده و به `trpl::run` ارسال شده است را اجرا می‌کنیم، درست همان‌طور که در طول این فصل انجام داده‌ایم. در آن Future، منتظر دریافت پیام‌ها می‌مانیم، دقیقاً مانند سایر مثال‌های ارسال پیام که دیده‌ایم. + +برای بازگشت به سناریویی که فصل را با آن آغاز کردیم، تصور کنید که مجموعه‌ای از وظایف کدگذاری ویدئو را با استفاده از یک Thread اختصاصی (زیرا کدگذاری ویدئو به شدت وابسته به پردازش است) اجرا می‌کنید، اما با استفاده از یک کانال async به رابط کاربری اطلاع می‌دهید که آن عملیات به پایان رسیده‌اند. در موارد استفاده واقعی، بی‌شمار نمونه از این نوع ترکیب‌ها وجود دارد. -To return to the scenario we opened the chapter with, imagine running a set of -video encoding tasks using a dedicated thread (because video encoding is -compute-bound) but notifying the UI that those operations are done with an async -channel. There are countless examples of these kinds of combinations in -real-world use cases. -## Summary +## خلاصه -This isn’t the last you’ll see of concurrency in this book. The project in -[Chapter 21][ch21] will apply these concepts in a more realistic situation -than the simpler examples discussed here and compare problem-solving with threading versus tasks more directly. +این آخرین باری نیست که در این کتاب با همزمانی مواجه می‌شوید. پروژه موجود در [فصل ۲۱][ch21]<!-- ignore --> این مفاهیم را در یک موقعیت واقعی‌تر از مثال‌های ساده‌ای که در اینجا بحث شد، به کار خواهد گرفت و حل مسئله با استفاده از Threadها در مقابل Tasks را به طور مستقیم‌تر مقایسه خواهد کرد. -No matter which of these approaches you choose, Rust gives you the tools you need to write safe, fast, concurrent -code—whether for a high-throughput web server or an embedded operating system. +صرف‌نظر از اینکه کدام یک از این رویکردها را انتخاب می‌کنید، Rust ابزارهای لازم برای نوشتن کدی ایمن، سریع و همزمان را در اختیار شما قرار می‌دهد—چه برای یک وب سرور با توان عملیاتی بالا و چه برای یک سیستم‌عامل تعبیه‌شده. -Next, we’ll talk about idiomatic ways to model problems and structure solutions -as your Rust programs get bigger. In addition, we’ll discuss how Rust’s idioms -relate to those you might be familiar with from object-oriented programming. + +در ادامه، درباره روش‌های ایدئوماتیک برای مدل‌سازی مشکلات و ساختاردهی راه‌حل‌ها به‌عنوان برنامه‌های Rust شما بزرگ‌تر می‌شوند صحبت خواهیم کرد. علاوه بر این، درباره اینکه ایدئوم‌های Rust چگونه با آن‌هایی که ممکن است از برنامه‌نویسی شی‌گرا با آن‌ها آشنا باشید مرتبط هستند بحث خواهیم کرد. [ch16]: http://localhost:3000/ch16-00-concurrency.html [combining-futures]: ch17-03-more-futures.html#building-our-own-async-abstractions + [streams]: ch17-04-streams.html#composing-streams [ch21]: ch21-00-final-project-a-web-server.html diff --git a/src/ch18-00-oop.md b/src/ch18-00-oop.md index 5f56abb8d7..ac25a248df 100644 --- a/src/ch18-00-oop.md +++ b/src/ch18-00-oop.md @@ -1,17 +1,7 @@ -# Object-Oriented Programming Features +# ویژگی‌های برنامه‌نویسی شی‌گرا -<!-- Old link, do not remove --> +<!-- پیوند قدیمی، حذف نشود --> <a id="object-oriented-programming-features-of-rust"></a> -Object-oriented programming (OOP) is a way of modeling programs. Objects as a -programmatic concept were introduced in the programming language Simula in the -1960s. Those objects influenced Alan Kay’s programming architecture in which -objects pass messages to each other. To describe this architecture, he coined -the term _object-oriented programming_ in 1967. Many competing definitions -describe what OOP is, and by some of these definitions Rust is object oriented -but by others it is not. In this chapter, we’ll explore certain characteristics -that are commonly considered object oriented and how those characteristics -translate to idiomatic Rust. We’ll then show you how to implement an -object-oriented design pattern in Rust and discuss the trade-offs of doing so -versus implementing a solution using some of Rust’s strengths instead. +برنامه‌نویسی شی‌گرا (OOP) روشی برای مدل‌سازی برنامه‌ها است. مفهوم شی به عنوان یک مفهوم برنامه‌نویسی نخستین‌بار در زبان Simula در دهه ۱۹۶۰ معرفی شد. این اشیاء بر معماری برنامه‌نویسی آلن کی تأثیر گذاشتند؛ معماری‌ای که در آن اشیاء به یکدیگر پیام ارسال می‌کنند. برای توصیف این معماری، او در سال ۱۹۶۷ اصطلاح *برنامه‌نویسی شی‌گرا* را ابداع کرد. تعاریف متعددی از برنامه‌نویسی شی‌گرا وجود دارد که گاه با یکدیگر رقابت می‌کنند، و بر اساس برخی از این تعاریف، Rust زبانی شی‌گرا به شمار می‌رود، در حالی‌که بر اساس برخی دیگر چنین نیست. در این فصل، برخی ویژگی‌هایی را بررسی خواهیم کرد که معمولاً شی‌گرا تلقی می‌شوند و خواهیم دید چگونه این ویژگی‌ها به صورت ایدیوما‌تیک در Rust پیاده‌سازی می‌شوند. سپس به شما نشان خواهیم داد چگونه یک الگوی طراحی شی‌گرا را در Rust پیاده‌سازی کنید و در مورد مزایا و معایب این کار، در مقایسه با پیاده‌سازی راه‌حلی با استفاده از نقاط قوت Rust، بحث خواهیم کرد. diff --git a/src/ch18-01-what-is-oo.md b/src/ch18-01-what-is-oo.md index dd4223da26..5c3bb0125d 100644 --- a/src/ch18-01-what-is-oo.md +++ b/src/ch18-01-what-is-oo.md @@ -1,48 +1,20 @@ -## Characteristics of Object-Oriented Languages - -There is no consensus in the programming community about what features a -language must have to be considered object oriented. Rust is influenced by many -programming paradigms, including OOP; for example, we explored the features -that came from functional programming in Chapter 13. Arguably, OOP languages -share certain common characteristics, namely objects, encapsulation, and -inheritance. Let’s look at what each of those characteristics means and whether -Rust supports it. - -### Objects Contain Data and Behavior - -The book _Design Patterns: Elements of Reusable Object-Oriented Software_ by -Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley, -1994), colloquially referred to as _The Gang of Four_ book, is a catalog of -object-oriented design patterns. It defines OOP in this way: - -> Object-oriented programs are made up of objects. An **object** packages both -> data and the procedures that operate on that data. The procedures are -> typically called **methods** or **operations**. - -Using this definition, Rust is object oriented: structs and enums have data, -and `impl` blocks provide methods on structs and enums. Even though structs and -enums with methods aren’t _called_ objects, they provide the same -functionality, according to the Gang of Four’s definition of objects. - -### Encapsulation That Hides Implementation Details - -Another aspect commonly associated with OOP is the idea of _encapsulation_, -which means that the implementation details of an object aren’t accessible to -code using that object. Therefore, the only way to interact with an object is -through its public API; code using the object shouldn’t be able to reach into -the object’s internals and change data or behavior directly. This enables the -programmer to change and refactor an object’s internals without needing to -change the code that uses the object. - -We discussed how to control encapsulation in Chapter 7: we can use the `pub` -keyword to decide which modules, types, functions, and methods in our code -should be public, and by default everything else is private. For example, we -can define a struct `AveragedCollection` that has a field containing a vector -of `i32` values. The struct can also have a field that contains the average of -the values in the vector, meaning the average doesn’t have to be computed on -demand whenever anyone needs it. In other words, `AveragedCollection` will -cache the calculated average for us. Listing 18-1 has the definition of the -`AveragedCollection` struct. +## ویژگی‌های زبان‌های شی‌گرا + +در جامعه برنامه‌نویسی هیچ توافقی درباره اینکه یک زبان باید چه ویژگی‌هایی داشته باشد تا به‌عنوان شی‌گرا در نظر گرفته شود، وجود ندارد. Rust تحت تأثیر بسیاری از پارادایم‌های برنامه‌نویسی قرار گرفته است، از جمله OOP؛ برای مثال، ما ویژگی‌هایی که از برنامه‌نویسی تابعی آمده بودند را در فصل 13 بررسی کردیم. می‌توان گفت که زبان‌های شی‌گرا برخی ویژگی‌های مشترک دارند، یعنی اشیاء، کپسوله‌سازی (encapsulation) و وراثت (inheritance). بیایید بررسی کنیم که هر یک از این ویژگی‌ها چه معنایی دارند و آیا Rust از آن‌ها پشتیبانی می‌کند یا خیر. + +### اشیاء شامل داده‌ها و رفتار هستند + +کتاب _Design Patterns: Elements of Reusable Object-Oriented Software_ نوشته Erich Gamma، Richard Helm، Ralph Johnson و John Vlissides (انتشارات Addison-Wesley Professional، 1994)، که به طور غیررسمی به عنوان کتاب _Gang of Four_ شناخته می‌شود، یک فهرست از الگوهای طراحی شی‌گرا است. این کتاب OOP را به این صورت تعریف می‌کند: + +> برنامه‌های شی‌گرا از اشیاء تشکیل شده‌اند. یک _شیء_ شامل داده‌ها و روش‌هایی که بر روی آن داده‌ها عمل می‌کنند، است. این روش‌ها معمولاً به نام _متدها_ یا _عملیات_ شناخته می‌شوند. + +با استفاده از این تعریف، Rust یک زبان شی‌گرا است: structها و enumها داده دارند، و بلوک‌های `impl` متدهایی را برای structها و enumها ارائه می‌دهند. حتی اگر structها و enumها با متدهایی که دارند _اشیاء_ نامیده نشوند، بر اساس تعریف Gang of Four، آن‌ها همان عملکرد را ارائه می‌دهند. + +### کپسوله‌سازی برای مخفی کردن جزئیات پیاده‌سازی + +یکی دیگر از جنبه‌هایی که معمولاً با OOP مرتبط است، مفهوم _کپسوله‌سازی_ است، که به این معناست که جزئیات پیاده‌سازی یک شیء برای کدی که از آن شیء استفاده می‌کند قابل دسترسی نیست. بنابراین تنها راه تعامل با یک شیء از طریق API عمومی آن است؛ کدی که از شیء استفاده می‌کند نباید بتواند به جزئیات داخلی شیء دسترسی پیدا کند و داده‌ها یا رفتار را به صورت مستقیم تغییر دهد. این امکان را به برنامه‌نویس می‌دهد که جزئیات داخلی شیء را تغییر داده و بازسازی کند بدون اینکه نیازی به تغییر کدی که از آن شیء استفاده می‌کند، داشته باشد. + +ما در فصل ۷ درباره‌ی نحوه‌ی کنترل کپسوله‌سازی (encapsulation) صحبت کردیم: می‌توانیم از کلیدواژه‌ی `pub` استفاده کنیم تا مشخص کنیم کدام ماژول‌ها، نوع‌ها، توابع و متدها در کد ما باید عمومی باشند، و به‌صورت پیش‌فرض سایر اعضا خصوصی هستند. برای مثال، می‌توانیم یک ساختار `AveragedCollection` تعریف کنیم که یک فیلد شامل یک بردار (`vector`) از مقادیر `i32` دارد. این ساختار همچنین می‌تواند فیلدی داشته باشد که میانگین مقادیر موجود در بردار را نگه می‌دارد، به این معنا که لازم نیست هر بار که کسی به میانگین نیاز دارد، آن را به‌صورت پویا محاسبه کنیم. به عبارت دیگر، `AveragedCollection` میانگین محاسبه‌شده را برای ما کش (ذخیره) می‌کند. لیستینگ 18-1 تعریف ساختار `AveragedCollection` را نشان می‌دهد. <Listing number="18-1" file-name="src/lib.rs" caption="An `AveragedCollection` struct that maintains a list of integers and the average of the items in the collection"> @@ -52,13 +24,9 @@ cache the calculated average for us. Listing 18-1 has the definition of the </Listing> -The struct is marked `pub` so that other code can use it, but the fields within -the struct remain private. This is important in this case because we want to -ensure that whenever a value is added or removed from the list, the average is -also updated. We do this by implementing `add`, `remove`, and `average` methods -on the struct, as shown in Listing 18-2. +ساختار `struct` با کلمه کلیدی `pub` علامت‌گذاری شده است تا کدهای دیگر بتوانند از آن استفاده کنند، اما فیلدهای داخل struct همچنان خصوصی باقی می‌مانند. این نکته در این مثال مهم است، زیرا می‌خواهیم اطمینان حاصل کنیم که هر زمان مقداری به لیست اضافه یا از آن حذف می‌شود، میانگین نیز به‌روزرسانی می‌شود. این کار را با پیاده‌سازی متدهای `add`، `remove` و `average` روی struct انجام می‌دهیم، همان‌طور که در لیستینگ 18-2 نشان داده شده است: -<Listing number="18-2" file-name="src/lib.rs" caption="Implementations of the public methods `add`, `remove`, and `average` on `AveragedCollection`"> +<Listing number="18-2" file-name="src/lib.rs" caption="پیاده‌سازی متدهای عمومی `add`، `remove` و `average` در `AveragedCollection`"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-02/src/lib.rs:here}} @@ -66,84 +34,32 @@ on the struct, as shown in Listing 18-2. </Listing> -The public methods `add`, `remove`, and `average` are the only ways to access -or modify data in an instance of `AveragedCollection`. When an item is added to -`list` using the `add` method or removed using the `remove` method, the -implementations of each call the private `update_average` method that handles -updating the `average` field as well. - -We leave the `list` and `average` fields private so there is no way for -external code to add or remove items to or from the `list` field directly; -otherwise, the `average` field might become out of sync when the `list` -changes. The `average` method returns the value in the `average` field, -allowing external code to read the `average` but not modify it. - -Because we’ve encapsulated the implementation details of the struct -`AveragedCollection`, we can easily change aspects, such as the data structure, -in the future. For instance, we could use a `HashSet<i32>` instead of a -`Vec<i32>` for the `list` field. As long as the signatures of the `add`, -`remove`, and `average` public methods stayed the same, code using -`AveragedCollection` wouldn’t need to change. If we made `list` public instead, -this wouldn’t necessarily be the case: `HashSet<i32>` and `Vec<i32>` have -different methods for adding and removing items, so the external code would -likely have to change if it were modifying `list` directly. - -If encapsulation is a required aspect for a language to be considered object -oriented, then Rust meets that requirement. The option to use `pub` or not for -different parts of code enables encapsulation of implementation details. - -### Inheritance as a Type System and as Code Sharing - -_Inheritance_ is a mechanism whereby an object can inherit elements from -another object’s definition, thus gaining the parent object’s data and behavior -without you having to define them again. - -If a language must have inheritance to be object oriented, then Rust is not -such a language. There is no way to define a struct that inherits the parent -struct’s fields and method implementations without using a macro. - -However, if you’re used to having inheritance in your programming toolbox, you -can use other solutions in Rust, depending on your reason for reaching for -inheritance in the first place. - -You would choose inheritance for two main reasons. One is for reuse of code: -you can implement particular behavior for one type, and inheritance enables you -to reuse that implementation for a different type. You can do this in a limited -way in Rust code using default trait method implementations, which you saw in -Listing 10-14 when we added a default implementation of the `summarize` method -on the `Summary` trait. Any type implementing the `Summary` trait would have -the `summarize` method available on it without any further code. This is -similar to a parent class having an implementation of a method and an -inheriting child class also having the implementation of the method. We can -also override the default implementation of the `summarize` method when we -implement the `Summary` trait, which is similar to a child class overriding the -implementation of a method inherited from a parent class. - -The other reason to use inheritance relates to the type system: to enable a -child type to be used in the same places as the parent type. This is also -called _polymorphism_, which means that you can substitute multiple objects for -each other at runtime if they share certain characteristics. - -> ### Polymorphism +متدهای عمومی `add`، `remove` و `average` تنها راه‌های دسترسی یا تغییر داده‌ها در یک نمونه از `AveragedCollection` هستند. زمانی که یک آیتم با استفاده از متد `add` به `list` اضافه می‌شود یا با استفاده از متد `remove` از آن حذف می‌شود، پیاده‌سازی هر یک از آن‌ها متد خصوصی `update_average` را فراخوانی می‌کند که به‌روزرسانی فیلد `average` را مدیریت می‌کند. + +ما فیلدهای `list` و `average` را خصوصی نگه می‌داریم تا هیچ راهی برای کد خارجی وجود نداشته باشد که مستقیماً آیتم‌ها را به `list` اضافه یا از آن حذف کند. در غیر این صورت، فیلد `average` ممکن است با تغییرات `list` هماهنگ نباشد. متد `average` مقدار موجود در فیلد `average` را بازمی‌گرداند و به کد خارجی اجازه می‌دهد تا مقدار میانگین را بخواند اما آن را تغییر ندهد. + +از آنجایی که جزئیات پیاده‌سازی ساختار `AveragedCollection` را کپسوله کرده‌ایم، می‌توانیم به راحتی جنبه‌هایی از آن را در آینده تغییر دهیم. برای مثال، می‌توانیم به جای استفاده از `Vec<i32>` برای فیلد `list`، از یک `HashSet<i32>` استفاده کنیم. تا زمانی که امضای متدهای عمومی `add`، `remove` و `average` یکسان باقی بماند، کدی که از `AveragedCollection` استفاده می‌کند نیازی به تغییر برای کامپایل شدن نخواهد داشت. اگر `list` عمومی بود، این موضوع لزوماً صادق نبود: `HashSet<i32>` و `Vec<i32>` متدهای متفاوتی برای اضافه کردن و حذف آیتم‌ها دارند، بنابراین کد خارجی احتمالاً باید تغییر کند اگر مستقیماً `list` را تغییر می‌داد. + +اگر کپسوله‌سازی یکی از جنبه‌های ضروری برای در نظر گرفتن یک زبان به عنوان شی‌گرا باشد، Rust این نیاز را برآورده می‌کند. امکان استفاده یا عدم استفاده از `pub` برای بخش‌های مختلف کد، کپسوله‌سازی جزئیات پیاده‌سازی را ممکن می‌سازد. + +### وراثت به‌عنوان سیستم نوع و به‌عنوان اشتراک‌گذاری کد + +_وراثت_ مکانیزمی است که به یک شیء اجازه می‌دهد عناصر را از تعریف یک شیء دیگر به ارث ببرد و در نتیجه داده‌ها و رفتار شیء والد را بدون نیاز به تعریف مجدد آن‌ها به دست آورد. + +اگر وراثت باید برای یک زبان وجود داشته باشد تا شی‌گرا در نظر گرفته شود، Rust یک زبان شی‌گرا نیست. در Rust، نمی‌توانید یک struct تعریف کنید که فیلدها و پیاده‌سازی متدهای struct والد را بدون استفاده از یک ماکرو به ارث ببرد. + +با این حال، اگر به استفاده از وراثت در ابزارهای برنامه‌نویسی خود عادت کرده‌اید، می‌توانید بسته به دلیل خود برای استفاده از وراثت، از راه‌حل‌های دیگری در Rust استفاده کنید. + +دو دلیل اصلی برای انتخاب وراثت وجود دارد. یکی برای استفاده مجدد از کد: می‌توانید یک رفتار خاص را برای یک نوع پیاده‌سازی کنید و وراثت این امکان را فراهم می‌کند که از آن پیاده‌سازی برای یک نوع دیگر استفاده مجدد کنید. در Rust، این کار را به صورت محدود با استفاده از پیاده‌سازی‌های پیش‌فرض متدهای صفت (trait) انجام دهید، همان‌طور که در لیستینگ 10-14 دیدیم که یک پیاده‌سازی پیش‌فرض برای متد `summarize` در صفت `Summary` اضافه کردیم. هر نوعی که صفت `Summary` را پیاده‌سازی کند، متد `summarize` را بدون نیاز به کد اضافی خواهد داشت. این شبیه به این است که یک کلاس والد یک پیاده‌سازی از یک متد داشته باشد و یک کلاس فرزند ارث‌برده نیز آن پیاده‌سازی متد را داشته باشد. همچنین می‌توانیم پیاده‌سازی پیش‌فرض متد `summarize` را زمانی که صفت `Summary` را پیاده‌سازی می‌کنیم، بازنویسی کنیم که شبیه به بازنویسی پیاده‌سازی یک متد ارث‌برده شده در کلاس فرزند است. + +دلیل دیگر استفاده از وراثت مربوط به سیستم نوع است: برای این که یک نوع فرزند بتواند در همان مکان‌هایی که نوع والد استفاده می‌شود، مورد استفاده قرار گیرد. این مفهوم _چندریختی (polymorphism)_ نیز نامیده می‌شود، که به این معناست که می‌توانید چندین شیء را در زمان اجرا جایگزین یکدیگر کنید اگر آن‌ها ویژگی‌های خاصی را به اشتراک بگذارند. + +> ### چندریختی (Polymorphism) > -> To many people, polymorphism is synonymous with inheritance. But it’s -> actually a more general concept that refers to code that can work with data of -> multiple types. For inheritance, those types are generally subclasses. +> برای بسیاری از افراد، چندریختی مترادف با وراثت است. اما در واقع یک مفهوم عمومی‌تر است که به کدی اشاره دارد که می‌تواند با داده‌هایی از انواع مختلف کار کند. در مورد وراثت، این انواع معمولاً زیرکلاس‌ها هستند. > -> Rust instead uses generics to abstract over different possible types and -> trait bounds to impose constraints on what those types must provide. This is -> sometimes called _bounded parametric polymorphism_. - -Rust has chosen a different set of tradeoffs by not offering inheritance. -Inheritance is often at risk of sharing more code than necessary. Subclasses -shouldn’t always share all characteristics of their parent class but will do so -with inheritance. This can make a program’s design less flexible. It also -introduces the possibility of calling methods on subclasses that don’t make -sense or that cause errors because the methods don’t apply to the subclass. In -addition, some languages will only allow _single inheritance_ (meaning a -subclass can only inherit from one class), further restricting the flexibility -of a program’s design. - -For these reasons, Rust takes the different approach of using trait objects -instead of inheritance to enable polymorphism. Let’s look at how trait objects -work. +> در مقابل، Rust از جنریک‌ها برای انتزاع انواع ممکن مختلف استفاده می‌کند و محدودیت‌های صفت (trait bounds) را برای تحمیل این که این انواع باید چه ویژگی‌هایی ارائه دهند، اعمال می‌کند. این رویکرد گاهی _چندریختی پارامتریک محدودشده_ نامیده می‌شود. + +وراثت اخیراً به‌عنوان یک راه‌حل طراحی برنامه‌نویسی در بسیاری از زبان‌ها محبوبیت خود را از دست داده است زیرا اغلب خطر اشتراک‌گذاری بیش از حد کد را به همراه دارد. زیرکلاس‌ها نباید همیشه تمام ویژگی‌های کلاس والد خود را به اشتراک بگذارند، اما با وراثت این اتفاق می‌افتد. این می‌تواند طراحی برنامه را کمتر انعطاف‌پذیر کند. همچنین امکان فراخوانی متدهایی روی زیرکلاس‌ها را فراهم می‌کند که معنا ندارند یا باعث خطا می‌شوند زیرا متدها برای زیرکلاس اعمال نمی‌شوند. علاوه بر این، برخی زبان‌ها فقط اجازه وراثت تک (single inheritance) را می‌دهند (یعنی یک زیرکلاس فقط می‌تواند از یک کلاس ارث ببرد)، که انعطاف‌پذیری طراحی برنامه را بیشتر محدود می‌کند. + +به این دلایل، Rust رویکرد متفاوتی را با استفاده از اشیاء صفت (trait objects) به جای وراثت اتخاذ می‌کند. بیایید ببینیم که چگونه اشیاء صفت در Rust چندریختی را ممکن می‌سازند. diff --git a/src/ch18-02-trait-objects.md b/src/ch18-02-trait-objects.md index aa7921d5bc..736c81b20c 100644 --- a/src/ch18-02-trait-objects.md +++ b/src/ch18-02-trait-objects.md @@ -1,71 +1,24 @@ -## Using Trait Objects to Abstract over Shared Behavior +## استفاده از اشیاء صفت برای مقادیر با انواع مختلف <!-- Old headings. Do not remove or links may break. --> <a id="using-trait-objects-that-allow-for-values-of-different-types"></a> -In Chapter 8, we mentioned that one limitation of vectors is that they can -store elements of only one type. We created a workaround in Listing 8-9 where -we defined a `SpreadsheetCell` enum that had variants to hold integers, floats, -and text. This meant we could store different types of data in each cell and -still have a vector that represented a row of cells. This is a perfectly good -solution when our interchangeable items are a fixed set of types that we know -when our code is compiled. - -However, sometimes we want our library user to be able to extend the set of -types that are valid in a particular situation. To show how we might achieve -this, we’ll create an example graphical user interface (GUI) tool that iterates -through a list of items, calling a `draw` method on each one to draw it to the -screen—a common technique for GUI tools. We’ll create a library crate called -`gui` that contains the structure of a GUI library. This crate might include -some types for people to use, such as `Button` or `TextField`. In addition, -`gui` users will want to create their own types that can be drawn: for -instance, one programmer might add an `Image` and another might add a -`SelectBox`. - -At the time of writing the library, we can’t know and define all the types -other programmers might want to create. But we do know that `gui` needs to keep -track of many values of different types, and it needs to call a `draw` method -on each of these differently typed values. It doesn’t need to know exactly what -will happen when we call the `draw` method, just that the value will have that -method available for us to call. - -To do this in a language with inheritance, we might define a class named -`Component` that has a method named `draw` on it. The other classes, such as -`Button`, `Image`, and `SelectBox`, would inherit from `Component` and thus -inherit the `draw` method. They could each override the `draw` method to define -their custom behavior, but the framework could treat all of the types as if -they were `Component` instances and call `draw` on them. But because Rust -doesn’t have inheritance, we need another way to structure the `gui` library to -allow users to create new types compatible with the library. - -### Defining a Trait for Common Behavior - -To implement the behavior we want `gui` to have, we’ll define a trait named -`Draw` that will have one method named `draw`. Then we can define a vector that -takes a trait object. A _trait object_ points to both an instance of a type -implementing our specified trait and a table used to look up trait methods on -that type at runtime. We create a trait object by specifying some sort of -pointer, such as an `&` reference or a `Box<T>` smart pointer, then the `dyn` -keyword, and then specifying the relevant trait. (We’ll talk about the reason -trait objects must use a pointer in [“Dynamically Sized Types and the `Sized` -Trait”][dynamically-sized]<!-- ignore --> in Chapter 20.) We can use trait -objects in place of a generic or concrete type. Wherever we use a trait object, -Rust’s type system will ensure at compile time that any value used in that -context will implement the trait object’s trait. Consequently, we don’t need to -know all the possible types at compile time. - -We’ve mentioned that, in Rust, we refrain from calling structs and enums -“objects” to distinguish them from other languages’ objects. In a struct or -enum, the data in the struct fields and the behavior in `impl` blocks are -separated, whereas in other languages, the data and behavior combined into one -concept is often labeled an object. Trait objects differ from objects in other -languages in that we can’t add data to a trait object. Trait objects aren’t as -generally useful as objects in other languages: their specific purpose is to -allow abstraction across common behavior. - -Listing 18-3 shows how to define a trait named `Draw` with one method named -`draw`. +در فصل 8، اشاره کردیم که یکی از محدودیت‌های وکتورها این است که می‌توانند فقط عناصر یک نوع را ذخیره کنند. در لیستینگ 8-9، راه‌حلی ایجاد کردیم که در آن یک enum به نام `SpreadsheetCell` تعریف کردیم که انواع مختلفی مانند اعداد صحیح، اعداد اعشاری و متن را در خود جای می‌داد. این به ما اجازه می‌داد داده‌های مختلفی را در هر سلول ذخیره کنیم و همچنان یک وکتور داشته باشیم که نمایانگر یک ردیف از سلول‌ها باشد. این راه‌حل زمانی مناسب است که آیتم‌های قابل تعویض ما مجموعه‌ای ثابت از انواع باشد که هنگام کامپایل کد می‌دانیم. + +با این حال، گاهی اوقات می‌خواهیم کاربران کتابخانه ما بتوانند مجموعه‌ای از انواع معتبر در یک وضعیت خاص را گسترش دهند. برای نشان دادن نحوه انجام این کار، یک ابزار رابط کاربری گرافیکی (GUI) نمونه ایجاد می‌کنیم که از طریق یک لیست از آیتم‌ها تکرار می‌کند و متدی به نام `draw` را برای هر آیتم فراخوانی می‌کند تا آن را روی صفحه رسم کند—یک تکنیک رایج برای ابزارهای GUI. یک crate کتابخانه‌ای به نام `gui` ایجاد می‌کنیم که ساختار یک کتابخانه GUI را شامل می‌شود. این crate ممکن است شامل برخی انواع باشد که افراد از آن‌ها استفاده کنند، مانند `Button` یا `TextField`. علاوه بر این، کاربران `gui` می‌خواهند انواع خود را که می‌توانند رسم شوند ایجاد کنند: برای مثال، یک برنامه‌نویس ممکن است یک `Image` اضافه کند و دیگری ممکن است یک `SelectBox` اضافه کند. + +ما برای این مثال یک کتابخانه GUI کامل پیاده‌سازی نخواهیم کرد، اما نشان خواهیم داد که قطعات چگونه به هم متصل می‌شوند. هنگام نوشتن این کتابخانه، نمی‌توانیم تمام انواعی که برنامه‌نویسان دیگر ممکن است بخواهند ایجاد کنند را بدانیم و تعریف کنیم. اما می‌دانیم که `gui` باید مقادیر زیادی از انواع مختلف را پیگیری کند و باید متدی به نام `draw` را برای هر یک از این مقادیر با نوع متفاوت فراخوانی کند. نیازی به دانستن دقیق آنچه هنگام فراخوانی متد `draw` اتفاق می‌افتد نداریم، فقط اینکه مقدار باید این متد را داشته باشد. + +برای انجام این کار در یک زبان با وراثت، ممکن است یک کلاس به نام `Component` تعریف کنیم که یک متد به نام `draw` داشته باشد. سایر کلاس‌ها، مانند `Button`، `Image` و `SelectBox`، از `Component` ارث می‌برند و به این ترتیب متد `draw` را به ارث می‌برند. آن‌ها می‌توانند متد `draw` را بازنویسی کنند تا رفتار سفارشی خود را تعریف کنند، اما فریم‌ورک می‌تواند تمام این انواع را به گونه‌ای مدیریت کند که گویی نمونه‌هایی از `Component` هستند و متد `draw` را روی آن‌ها فراخوانی کند. اما چون Rust وراثت ندارد، باید راه دیگری برای ساختاردهی کتابخانه `gui` پیدا کنیم تا به کاربران اجازه دهد آن را با انواع جدید گسترش دهند. + +### تعریف یک صفت برای رفتار مشترک + +برای پیاده‌سازی رفتاری که می‌خواهیم `gui` داشته باشد، یک صفت به نام `Draw` تعریف می‌کنیم که یک متد به نام `draw` خواهد داشت. سپس می‌توانیم یک وکتور تعریف کنیم که یک _شیء صفت_ را بگیرد. یک شیء صفت به یک نمونه از یک نوع که صفت مشخصی را پیاده‌سازی کرده اشاره می‌کند و همچنین یک جدول برای جستجوی متدهای صفت روی آن نوع در زمان اجرا را شامل می‌شود. برای ایجاد یک شیء صفت، باید نوع اشاره‌گر (Pointer) (مانند یک ارجاع `&` یا یک اشاره‌گر (Pointer) هوشمند `Box<T>`)، کلمه کلیدی `dyn` و سپس صفت مربوطه را مشخص کنیم. (در فصل 20، بخش [“انواع با اندازه پویا و صفت `Sized`”][dynamically-sized]<!-- ignore --> دلیل اینکه اشیاء صفت باید از یک اشاره‌گر (Pointer) استفاده کنند را توضیح خواهیم داد.) می‌توانیم از اشیاء صفت به جای یک نوع جنریک یا نوع مشخص استفاده کنیم. هر جا که از یک شیء صفت استفاده کنیم، سیستم نوع Rust در زمان کامپایل تضمین می‌کند که هر مقداری که در آن زمینه استفاده شود، صفت شیء صفت را پیاده‌سازی می‌کند. بنابراین نیازی به دانستن تمام انواع ممکن در زمان کامپایل نداریم. + +اشاره کردیم که در Rust از استفاده از اصطلاح "اشیاء" برای structها و enumها اجتناب می‌کنیم تا آن‌ها را از اشیاء سایر زبان‌ها متمایز کنیم. در یک struct یا enum، داده‌ها در فیلدهای struct و رفتار در بلوک‌های `impl` جدا شده‌اند، در حالی که در سایر زبان‌ها داده‌ها و رفتار معمولاً در یک مفهوم واحد به نام شیء ترکیب می‌شوند. اما اشیاء صفت در Rust بیشتر شبیه اشیاء در سایر زبان‌ها هستند، زیرا داده‌ها و رفتار را ترکیب می‌کنند. با این حال، اشیاء صفت از اشیاء سنتی متفاوت هستند زیرا نمی‌توان داده‌ای به یک شیء صفت اضافه کرد. اشیاء صفت به اندازه اشیاء در سایر زبان‌ها عمومی نیستند: هدف خاص آن‌ها فراهم کردن انتزاع در رفتار مشترک است. + +لیستینگ 18-3 نشان می‌دهد چگونه می‌توان یک صفت به نام `Draw` با یک متد به نام `draw` تعریف کرد: <Listing number="18-3" file-name="src/lib.rs" caption="Definition of the `Draw` trait"> @@ -75,13 +28,9 @@ Listing 18-3 shows how to define a trait named `Draw` with one method named </Listing> -This syntax should look familiar from our discussions on how to define traits -in Chapter 10. Next comes some new syntax: Listing 18-4 defines a struct named -`Screen` that holds a vector named `components`. This vector is of type -`Box<dyn Draw>`, which is a trait object; it’s a stand-in for any type inside a -`Box` that implements the `Draw` trait. +این نحو باید از بحث‌های ما در فصل 10 در مورد نحوه تعریف صفات آشنا باشد. حالا به نحو جدیدی می‌رسیم: لیستینگ 18-4 یک ساختار به نام `Screen` را تعریف می‌کند که یک بردار به نام `components` دارد. این بردار از نوع `Box<dyn Draw>` است، که یک شیء صفت است؛ این به‌عنوان جایگزینی برای هر نوع داخل یک `Box` که صفت `Draw` را پیاده‌سازی کرده عمل می‌کند. -<Listing number="18-4" file-name="src/lib.rs" caption="Definition of the `Screen` struct with a `components` field holding a vector of trait objects that implement the `Draw` trait"> +<Listing number="18-4" file-name="src/lib.rs" caption="تعریف ساختار `Screen` با یک فیلد `components` که یک بردار از اشیاء صفت را نگه می‌دارد که صفت `Draw` را پیاده‌سازی کرده‌اند"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-04/src/lib.rs:here}} @@ -89,10 +38,9 @@ in Chapter 10. Next comes some new syntax: Listing 18-4 defines a struct named </Listing> -On the `Screen` struct, we’ll define a method named `run` that will call the -`draw` method on each of its `components`, as shown in Listing 18-5. +روی ساختار `Screen`، متدی به نام `run` تعریف می‌کنیم که متد `draw` را روی هر یک از `components` خود فراخوانی می‌کند، همان‌طور که در لیستینگ 18-5 نشان داده شده است: -<Listing number="18-5" file-name="src/lib.rs" caption="A `run` method on `Screen` that calls the `draw` method on each component"> +<Listing number="18-5" file-name="src/lib.rs" caption="متد `run` روی `Screen` که متد `draw` را روی هر کامپوننت فراخوانی می‌کند"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-05/src/lib.rs:here}} @@ -100,14 +48,9 @@ On the `Screen` struct, we’ll define a method named `run` that will call the </Listing> -This works differently from defining a struct that uses a generic type -parameter with trait bounds. A generic type parameter can be substituted with -only one concrete type at a time, whereas trait objects allow for multiple -concrete types to fill in for the trait object at runtime. For example, we -could have defined the `Screen` struct using a generic type and a trait bound, -as in Listing 18-6. +این روش متفاوت از تعریف ساختاری است که از یک پارامتر نوع جنریک با محدودیت‌های صفت استفاده می‌کند. یک پارامتر نوع جنریک فقط می‌تواند یک نوع مشخص را در هر زمان جایگزین کند، در حالی که اشیاء صفت به ما اجازه می‌دهند چندین نوع مشخص را در زمان اجرا به جای اشیاء صفت قرار دهیم. برای مثال، می‌توانستیم ساختار `Screen` را با استفاده از یک نوع جنریک و یک محدودیت صفت به صورت لیستینگ 18-6 تعریف کنیم: -<Listing number="18-6" file-name="src/lib.rs" caption="An alternate implementation of the `Screen` struct and its `run` method using generics and trait bounds"> +<Listing number="18-6" file-name="src/lib.rs" caption="یک پیاده‌سازی جایگزین برای ساختار `Screen` و متد `run` آن با استفاده از جنریک‌ها و محدودیت‌های صفت"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-06/src/lib.rs:here}} @@ -115,25 +58,15 @@ as in Listing 18-6. </Listing> -This restricts us to a `Screen` instance that has a list of components all of -type `Button` or all of type `TextField`. If you’ll only ever have homogeneous -collections, using generics and trait bounds is preferable because the -definitions will be monomorphized at compile time to use the concrete types. +این روش ما را محدود به یک نمونه `Screen` می‌کند که لیستی از کامپوننت‌ها همه از نوع `Button` یا همه از نوع `TextField` داشته باشد. اگر فقط مجموعه‌های همگن داشته باشید، استفاده از جنریک‌ها و محدودیت‌های صفت ترجیح داده می‌شود زیرا این تعاریف در زمان کامپایل با استفاده از انواع مشخص مونومورفیزه می‌شوند. -On the other hand, with the method using trait objects, one `Screen` instance -can hold a `Vec<T>` that contains a `Box<Button>` as well as a -`Box<TextField>`. Let’s look at how this works, and then we’ll talk about the -runtime performance implications. +از طرف دیگر، با استفاده از روش مبتنی بر اشیاء صفت، یک نمونه `Screen` می‌تواند یک `Vec<T>` داشته باشد که شامل یک `Box<Button>` و همچنین یک `Box<TextField>` باشد. بیایید ببینیم که چگونه این کار می‌کند، سپس درباره پیامدهای عملکرد در زمان اجرا صحبت کنیم. -### Implementing the Trait +### پیاده‌سازی صفت -Now we’ll add some types that implement the `Draw` trait. We’ll provide the -`Button` type. Again, actually implementing a GUI library is beyond the scope -of this book, so the `draw` method won’t have any useful implementation in its -body. To imagine what the implementation might look like, a `Button` struct -might have fields for `width`, `height`, and `label`, as shown in Listing 18-7. +حالا برخی از انواعی که صفت `Draw` را پیاده‌سازی می‌کنند اضافه می‌کنیم. نوع `Button` را ارائه می‌دهیم. دوباره، پیاده‌سازی یک کتابخانه GUI کامل فراتر از محدوده این کتاب است، بنابراین متد `draw` هیچ پیاده‌سازی مفیدی در بدنه خود نخواهد داشت. برای تصور اینکه پیاده‌سازی ممکن است چگونه باشد، یک ساختار `Button` ممکن است فیلدهایی برای `width`، `height` و `label` داشته باشد، همان‌طور که در لیستینگ 18-7 نشان داده شده است: -<Listing number="18-7" file-name="src/lib.rs" caption="A `Button` struct that implements the `Draw` trait"> +<Listing number="18-7" file-name="src/lib.rs" caption="یک ساختار `Button` که صفت `Draw` را پیاده‌سازی می‌کند"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-07/src/lib.rs:here}} @@ -141,21 +74,11 @@ might have fields for `width`, `height`, and `label`, as shown in Listing 18-7. </Listing> -The `width`, `height`, and `label` fields on `Button` will differ from the -fields on other components; for example, a `TextField` type might have those -same fields plus a `placeholder` field. Each of the types we want to draw on -the screen will implement the `Draw` trait but will use different code in the -`draw` method to define how to draw that particular type, as `Button` has here -(without the actual GUI code, as mentioned). The `Button` type, for instance, -might have an additional `impl` block containing methods related to what -happens when a user clicks the button. These kinds of methods won’t apply to -types like `TextField`. +فیلدهای `width`، `height` و `label` در `Button` با فیلدهای کامپوننت‌های دیگر متفاوت خواهند بود. برای مثال، یک نوع `TextField` ممکن است همان فیلدها به‌علاوه یک فیلد `placeholder` داشته باشد. هر یک از انواعی که می‌خواهیم روی صفحه رسم شوند، صفت `Draw` را پیاده‌سازی می‌کنند اما از کد متفاوتی در متد `draw` برای تعریف نحوه رسم آن نوع خاص استفاده می‌کنند، همان‌طور که در اینجا برای `Button` آمده است (بدون کد GUI واقعی، همان‌طور که ذکر شد). نوع `Button`، برای مثال، ممکن است یک بلوک `impl` اضافی شامل متدهایی مرتبط با آنچه هنگام کلیک کاربر روی دکمه اتفاق می‌افتد داشته باشد. این نوع متدها برای انواعی مانند `TextField` اعمال نمی‌شوند. -If someone using our library decides to implement a `SelectBox` struct that has -`width`, `height`, and `options` fields, they would implement the `Draw` trait -on the `SelectBox` type as well, as shown in Listing 18-8. +اگر کسی که از کتابخانه‌ی ما استفاده می‌کند بخواهد یک ساختار `SelectBox` تعریف کند که شامل فیلدهای `width`، `height` و `options` باشد، او همچنین باید `trait` `Draw` را برای نوع `SelectBox` پیاده‌سازی کند، همان‌طور که در لیستینگ 18-8 نشان داده شده است. -<Listing number="18-8" file-name="src/main.rs" caption="Another crate using `gui` and implementing the `Draw` trait on a `SelectBox` struct"> +<Listing number="18-8" file-name="src/main.rs" caption="یک crate دیگر که از `gui` استفاده می‌کند و صفت `Draw` را روی یک ساختار `SelectBox` پیاده‌سازی می‌کند"> ```rust,ignore {{#rustdoc_include ../listings/ch18-oop/listing-18-08/src/main.rs:here}} @@ -163,13 +86,9 @@ on the `SelectBox` type as well, as shown in Listing 18-8. </Listing> -Our library’s user can now write their `main` function to create a `Screen` -instance. To the `Screen` instance, they can add a `SelectBox` and a `Button` -by putting each in a `Box<T>` to become a trait object. They can then call the -`run` method on the `Screen` instance, which will call `draw` on each of the -components. Listing 18-9 shows this implementation. +اکنون کاربر کتابخانه ما می‌تواند تابع `main` خود را بنویسد تا یک نمونه `Screen` ایجاد کند. به نمونه `Screen`، آن‌ها می‌توانند یک `SelectBox` و یک `Button` اضافه کنند، با قرار دادن هر یک در یک `Box<T>` تا به یک شیء صفت تبدیل شوند. سپس می‌توانند متد `run` را روی نمونه `Screen` فراخوانی کنند، که متد `draw` را روی هر یک از کامپوننت‌ها فراخوانی می‌کند. لیستینگ 18-9 این پیاده‌سازی را نشان می‌دهد: -<Listing number="18-9" file-name="src/main.rs" caption="Using trait objects to store values of different types that implement the same trait"> +<Listing number="18-9" file-name="src/main.rs" caption="استفاده از اشیاء صفت برای ذخیره مقادیری با انواع مختلف که یک صفت یکسان را پیاده‌سازی می‌کنند"> ```rust,ignore {{#rustdoc_include ../listings/ch18-oop/listing-18-09/src/main.rs:here}} @@ -177,32 +96,15 @@ components. Listing 18-9 shows this implementation. </Listing> -When we wrote the library, we didn’t know that someone might add the -`SelectBox` type, but our `Screen` implementation was able to operate on the -new type and draw it because `SelectBox` implements the `Draw` trait, which -means it implements the `draw` method. - -This concept—of being concerned only with the messages a value responds to -rather than the value’s concrete type—is similar to the concept of _duck -typing_ in dynamically typed languages: if it walks like a duck and quacks like -a duck, then it must be a duck! In the implementation of `run` on `Screen` in -Listing 18-5, `run` doesn’t need to know what the concrete type of each -component is. It doesn’t check whether a component is an instance of a `Button` -or a `SelectBox`, it just calls the `draw` method on the component. By -specifying `Box<dyn Draw>` as the type of the values in the `components` -vector, we’ve defined `Screen` to need values that we can call the `draw` -method on. - -The advantage of using trait objects and Rust’s type system to write code -similar to code using duck typing is that we never have to check whether a -value implements a particular method at runtime or worry about getting errors -if a value doesn’t implement a method but we call it anyway. Rust won’t compile -our code if the values don’t implement the traits that the trait objects need. - -For example, Listing 18-10 shows what happens if we try to create a `Screen` -with a `String` as a component. - -<Listing number="18-10" file-name="src/main.rs" caption="Attempting to use a type that doesn’t implement the trait object’s trait"> +وقتی کتابخانه را نوشتیم، نمی‌دانستیم که کسی ممکن است نوع `SelectBox` را اضافه کند، اما پیاده‌سازی `Screen` ما توانست روی نوع جدید عمل کند و آن را رسم کند زیرا `SelectBox` صفت `Draw` را پیاده‌سازی کرده است، که به این معناست که متد `draw` را پیاده‌سازی کرده است. + +این مفهوم—فقط به پیام‌هایی که یک مقدار به آن‌ها پاسخ می‌دهد اهمیت داده می‌شود، نه نوع دقیق مقدار—مشابه مفهوم _duck typing_ در زبان‌های با نوع‌دهی پویا است: اگر مانند اردک حرکت می‌کند و مانند اردک صدا می‌کند، پس حتماً یک اردک است! در پیاده‌سازی متد `run` روی `Screen` در لیستینگ 18-5، `run` نیازی ندارد بداند نوع دقیق هر کامپوننت چیست. نیازی ندارد بررسی کند که آیا یک کامپوننت نمونه‌ای از `Button` یا `SelectBox` است؛ فقط متد `draw` را روی کامپوننت فراخوانی می‌کند. با مشخص کردن `Box<dyn Draw>` به‌عنوان نوع مقادیر در بردار `components`، ما تعریف کرده‌ایم که `Screen` به مقادیری نیاز دارد که بتوانیم متد `draw` را روی آن‌ها فراخوانی کنیم. + +مزیت استفاده از اشیاء صفت و سیستم نوع Rust برای نوشتن کدی مشابه با duck typing این است که هرگز نیازی به بررسی نداریم که آیا یک مقدار متدی خاص را در زمان اجرا پیاده‌سازی کرده است یا خیر، یا نگران خطاهایی باشیم اگر یک مقدار متدی را پیاده‌سازی نکرده اما ما آن را فراخوانی کنیم. Rust کد ما را کامپایل نمی‌کند اگر مقادیر صفاتی را که اشیاء صفت نیاز دارند پیاده‌سازی نکنند. + +برای مثال، لیستینگ 18-10 نشان می‌دهد چه اتفاقی می‌افتد اگر بخواهیم یک `Screen` با یک `String` به‌عنوان یک کامپوننت ایجاد کنیم: + +<Listing number="18-10" file-name="src/main.rs" caption="تلاش برای استفاده از نوعی که صفت شیء صفت را پیاده‌سازی نکرده است"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch18-oop/listing-18-10/src/main.rs}} @@ -210,41 +112,21 @@ with a `String` as a component. </Listing> -We’ll get this error because `String` doesn’t implement the `Draw` trait: +ما این خطا را دریافت خواهیم کرد زیرا `String` صفت `Draw` را پیاده‌سازی نکرده است: ```console {{#include ../listings/ch18-oop/listing-18-10/output.txt}} ``` -This error lets us know that either we’re passing something to `Screen` that we -didn’t mean to pass and so should pass a different type, or we should implement -`Draw` on `String` so that `Screen` is able to call `draw` on it. - -### Trait Objects Perform Dynamic Dispatch - -Recall in [“Performance of Code Using -Generics”][performance-of-code-using-generics]<!-- ignore --> in Chapter 10 our -discussion on the monomorphization process performed on generics by the -compiler: the compiler generates nongeneric implementations of functions and -methods for each concrete type that we use in place of a generic type -parameter. The code that results from monomorphization is doing _static -dispatch_, which is when the compiler knows what method you’re calling at -compile time. This is opposed to _dynamic dispatch_, which is when the compiler -can’t tell at compile time which method you’re calling. In dynamic dispatch -cases, the compiler emits code that at runtime will know which method to call. - -When we use trait objects, Rust must use dynamic dispatch. The compiler doesn’t -know all the types that might be used with the code that’s using trait objects, -so it doesn’t know which method implemented on which type to call. Instead, at -runtime, Rust uses the pointers inside the trait object to know which method to -call. This lookup incurs a runtime cost that doesn’t occur with static dispatch. -Dynamic dispatch also prevents the compiler from choosing to inline a method’s -code, which in turn prevents some optimizations, and Rust has some rules about -where you can and cannot use dynamic dispatch, called _dyn compatibility_. Those -rules are beyond the scope of this discussion, but you can read more about them -[in the reference][dyn-compatibility]<!-- ignore -->. However, we did get extra -flexibility in the code that we wrote in Listing 18-5 and were able to support -in Listing 18-9, so it’s a trade-off to consider. +این خطا به ما اطلاع می‌دهد که یا چیزی را به `Screen` پاس داده‌ایم که منظورمان نبوده و باید نوع متفاوتی پاس دهیم، یا باید `trait` `Draw` را برای نوع `String` پیاده‌سازی کنیم تا `Screen` بتواند متد `draw` را روی آن فراخوانی کند. + +### اشیاء صفت اجرای Dispatch پویا را انجام می‌دهند + +در بخش [«کارایی کدی که از Genericها استفاده می‌کند»][performance-of-code-using-generics]<!-- ignore --> در فصل ۱۰، درباره‌ی فرآیند مونومورفیزاسیون (monomorphization) که توسط کامپایلر روی genericها انجام می‌شود صحبت کردیم: کامپایلر پیاده‌سازی‌های غیر generic از توابع و متدها را برای هر نوع مشخصی که به‌جای پارامتر generic استفاده می‌کنیم تولید می‌کند. کدی که در نتیجه‌ی مونومورفیزاسیون به‌دست می‌آید از *ارسال ایستا* (static dispatch) استفاده می‌کند، به این معنا که کامپایلر در زمان کامپایل می‌داند کدام متد را فراخوانی می‌کنید. این در مقابل *ارسال پویا* (dynamic dispatch) است، که در آن کامپایلر نمی‌تواند در زمان کامپایل تشخیص دهد کدام متد فراخوانی خواهد شد. در حالت ارسال پویا، کامپایلر کدی تولید می‌کند که در زمان اجرا تشخیص می‌دهد کدام متد را باید فراخوانی کند. + +زمانی که از `trait object`ها استفاده می‌کنیم، Rust مجبور است از ارسال پویا استفاده کند. کامپایلر نمی‌داند همه‌ی نوع‌هایی که ممکن است با کدی که از `trait object` استفاده می‌کند به کار روند، کدامند؛ بنابراین نمی‌تواند مشخص کند کدام متد روی کدام نوع باید فراخوانی شود. در عوض، Rust در زمان اجرا از اشاره‌گرهای درون `trait object` استفاده می‌کند تا بداند کدام متد را باید فراخوانی کند. این جستجو هزینه‌ای در زمان اجرا دارد که در ارسال ایستا رخ نمی‌دهد. همچنین ارسال پویا مانع از این می‌شود که کامپایلر کد متد را inline کند که این موضوع باعث جلوگیری از برخی بهینه‌سازی‌ها می‌شود. Rust قوانینی در مورد محل‌هایی که می‌توان و نمی‌توان از ارسال پویا استفاده کرد دارد که به آن‌ها *هماهنگی dyn* (dyn compatibility) گفته می‌شود. این قوانین فراتر از محدوده‌ی این بحث هستند، اما می‌توانید درباره‌ی آن‌ها بیشتر در [مستندات مرجع][dyn-compatibility]<!-- ignore --> بخوانید. + +با این حال، کدی که در لیستینگ 18-5 نوشتیم و در لیستینگ 18-9 پشتیبانی کردیم، انعطاف‌پذیری بیشتری داشت؛ بنابراین این یک معامله‌ی قابل توجه است که باید در نظر گرفته شود. [performance-of-code-using-generics]: ch10-01-syntax.html#performance-of-code-using-generics [dynamically-sized]: ch20-03-advanced-types.html#dynamically-sized-types-and-the-sized-trait diff --git a/src/ch18-03-oo-design-patterns.md b/src/ch18-03-oo-design-patterns.md index 3c4e12d963..7784f2ee00 100644 --- a/src/ch18-03-oo-design-patterns.md +++ b/src/ch18-03-oo-design-patterns.md @@ -1,57 +1,41 @@ -## Implementing an Object-Oriented Design Pattern - -The _state pattern_ is an object-oriented design pattern. The crux of the -pattern is that we define a set of states a value can have internally. The -states are represented by a set of _state objects_, and the value’s behavior -changes based on its state. We’re going to work through an example of a blog -post struct that has a field to hold its state, which will be a state object -from the set “draft,” “review,” or “published.” - -The state objects share functionality: in Rust, of course, we use structs and -traits rather than objects and inheritance. Each state object is responsible -for its own behavior and for governing when it should change into another -state. The value that holds a state object knows nothing about the different -behavior of the states or when to transition between states. - -The advantage of using the state pattern is that, when the business -requirements of the program change, we won’t need to change the code of the -value holding the state or the code that uses the value. We’ll only need to -update the code inside one of the state objects to change its rules or perhaps -add more state objects. - -First we’re going to implement the state pattern in a more traditional -object-oriented way, then we’ll use an approach that’s a bit more natural in -Rust. Let’s dig in to incrementally implement a blog post workflow using the -state pattern. - -The final functionality will look like this: - -1. A blog post starts as an empty draft. -1. When the draft is done, a review of the post is requested. -1. When the post is approved, it gets published. -1. Only published blog posts return content to print, so unapproved posts can’t - accidentally be published. - -Any other changes attempted on a post should have no effect. For example, if we -try to approve a draft blog post before we’ve requested a review, the post -should remain an unpublished draft. - -### A Traditional Object-oriented Attempt - -There are infinite ways to structure code to solve the same problem, each with -different trade-offs. This section’s implementation is more of a traditional -object-oriented style, which is possible to write in Rust, but doesn’t take -advantage of some of Rust’s strengths. Later, we’ll demonstrate a different -solution that still uses the object-oriented design pattern but is structured -in a way that might look less familiar to programmers with object-oriented -experience. We’ll compare the two solutions to experience the trade-offs of -designing Rust code differently than code in other languages. - -Listing 18-11 shows this workflow in code form: this is an example usage of the -API we’ll implement in a library crate named `blog`. This won’t compile yet -because we haven’t implemented the `blog` crate. - -<Listing number="18-11" file-name="src/main.rs" caption="Code that demonstrates the desired behavior we want our `blog` crate to have"> +## پیاده‌سازی یک الگوی طراحی شی‌گرا + +_الگوی وضعیت_ یک الگوی طراحی شی‌گرا است. هسته این الگو این است که مجموعه‌ای از وضعیت‌ها را که یک مقدار می‌تواند +به‌طور داخلی داشته باشد، تعریف کنیم. این وضعیت‌ها با مجموعه‌ای از _اشیای وضعیت_ نمایش داده می‌شوند و رفتار مقدار +بر اساس وضعیت آن تغییر می‌کند. قصد داریم مثالی از یک ساختار blog post (پست وبلاگ) را بررسی کنیم که یک فیلد برای +نگه‌داشتن وضعیت دارد. این وضعیت یک شیء از مجموعه "پیش‌نویس" (draft)، "در حال بررسی" (review)، یا "منتشرشده" +(published) خواهد بود. + +اشیای وضعیت قابلیت‌هایی را به اشتراک می‌گذارند: در Rust، البته، ما از ساختارها (structs) و صفت‌ها (traits) به جای +اشیا و ارث‌بری استفاده می‌کنیم. هر شیء وضعیت مسئول رفتار خود و مدیریت زمانی است که باید به وضعیت دیگری تغییر کند. +مقداری که یک شیء وضعیت را نگه می‌دارد، هیچ اطلاعی از رفتارهای مختلف وضعیت‌ها یا زمان تغییر وضعیت ندارد. + +مزیت استفاده از الگوی وضعیت این است که وقتی نیازهای تجاری برنامه تغییر می‌کنند، نیازی به تغییر کد مقداری که وضعیت +را نگه می‌دارد یا کدی که از آن مقدار استفاده می‌کند، نداریم. تنها لازم است کد داخل یکی از اشیای وضعیت را برای تغییر +قوانین آن یا شاید اضافه کردن اشیای وضعیت جدید به‌روزرسانی کنیم. + +ابتدا الگوی وضعیت را به روش سنتی شی‌گرایی پیاده‌سازی می‌کنیم، سپس از رویکردی که در Rust طبیعی‌تر است استفاده خواهیم +کرد. بیایید به‌صورت مرحله‌به‌مرحله پیاده‌سازی یک فرآیند کاری پست وبلاگ با استفاده از الگوی وضعیت را بررسی کنیم. + +قابلیت نهایی به این شکل خواهد بود: + +1. یک پست وبلاگ به‌صورت یک پیش‌نویس خالی شروع می‌شود. +2. وقتی پیش‌نویس تمام شد، بررسی پست درخواست می‌شود. +3. وقتی پست تأیید شد، منتشر می‌شود. +4. تنها پست‌های وبلاگی که منتشر شده‌اند متن را برای چاپ بازمی‌گردانند، بنابراین پست‌های تأییدنشده نمی‌توانند به‌طور + تصادفی منتشر شوند. + +هر تغییر دیگری که روی یک پست تلاش شود نباید تأثیری داشته باشد. برای مثال، اگر بخواهیم یک پست وبلاگ پیش‌نویس را +قبل از درخواست بررسی تأیید کنیم، پست باید به‌عنوان پیش‌نویس منتشرنشده باقی بماند. + +### یک تلاش سنتی شیء‌گرایانه + +راه‌های بی‌شماری برای ساختاردهی کد به‌منظور حل یک مسئله وجود دارد که هرکدام با معایب و مزایای متفاوتی همراه‌اند. پیاده‌سازی این بخش بیشتر به سبک سنتی شیءگرایانه نزدیک است، که در Rust قابل نوشتن است، اما از برخی نقاط قوت Rust بهره نمی‌برد. در ادامه، راه‌حل متفاوتی را نشان خواهیم داد که هنوز از الگوی طراحی شیءگرایانه استفاده می‌کند اما به‌گونه‌ای ساختار یافته که ممکن است برای برنامه‌نویسان با تجربه‌ی شیءگرایی کمتر آشنا باشد. این دو راه‌حل را با هم مقایسه خواهیم کرد تا تفاوت‌ها و معایب و مزایای طراحی کد Rust به شکلی متفاوت نسبت به زبان‌های دیگر را تجربه کنیم. + +لیستینگ 18-11 این روند کاری را به‌صورت کد نشان می‌دهد: این یک نمونه استفاده از API است که در کتابخانه‌ای به نام `blog` پیاده‌سازی خواهیم کرد. این کد هنوز کامپایل نمی‌شود، زیرا `crate` مربوط به `blog` را پیاده‌سازی نکرده‌ایم. + + +<Listing number="18-11" file-name="src/main.rs" caption="کدی که رفتار مورد نظر ما برای crate `blog` را نشان می‌دهد"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch18-oop/listing-18-11/src/main.rs:all}} @@ -59,42 +43,28 @@ because we haven’t implemented the `blog` crate. </Listing> -We want to allow the user to create a new draft blog post with `Post::new`. We -want to allow text to be added to the blog post. If we try to get the post’s -content immediately, before approval, we shouldn’t get any text because the -post is still a draft. We’ve added `assert_eq!` in the code for demonstration -purposes. An excellent unit test for this would be to assert that a draft blog -post returns an empty string from the `content` method, but we’re not going to -write tests for this example. - -Next, we want to enable a request for a review of the post, and we want -`content` to return an empty string while waiting for the review. When the post -receives approval, it should get published, meaning the text of the post will -be returned when `content` is called. - -Notice that the only type we’re interacting with from the crate is the `Post` -type. This type will use the state pattern and will hold a value that will be -one of three state objects representing the various states a post can be -in—draft, review, or published. Changing from one state to another will be -managed internally within the `Post` type. The states change in response to the -methods called by our library’s users on the `Post` instance, but they don’t -have to manage the state changes directly. Also, users can’t make a mistake -with the states, such as publishing a post before it’s reviewed. - -#### Defining `Post` and Creating a New Instance in the Draft State - -Let’s get started on the implementation of the library! We know we need a -public `Post` struct that holds some content, so we’ll start with the -definition of the struct and an associated public `new` function to create an -instance of `Post`, as shown in Listing 18-12. We’ll also make a private -`State` trait that will define the behavior that all state objects for a `Post` -must have. - -Then `Post` will hold a trait object of `Box<dyn State>` inside an `Option<T>` -in a private field named `state` to hold the state object. You’ll see why the -`Option<T>` is necessary in a bit. - -<Listing number="18-12" file-name="src/lib.rs" caption="Definition of a `Post` struct and a `new` function that creates a new `Post` instance, a `State` trait, and a `Draft` struct"> +ما می‌خواهیم به کاربر اجازه دهیم یک پست وبلاگ پیش‌نویس جدید با `Post::new` ایجاد کند. می‌خواهیم امکان اضافه کردن +متن به پست وبلاگ را فراهم کنیم. اگر فوراً بخواهیم محتوای پست را دریافت کنیم، قبل از تأیید، نباید هیچ متنی دریافت +کنیم، زیرا پست هنوز یک پیش‌نویس است. ما از `assert_eq!` در کد برای اهداف نمایشی استفاده کرده‌ایم. یک تست واحد عالی +برای این مورد این است که تأیید کنیم یک پست وبلاگ پیش‌نویس یک رشته خالی از متد `content` بازمی‌گرداند، اما قصد نداریم +برای این مثال تست بنویسیم. + +سپس می‌خواهیم امکان درخواست بررسی برای پست فراهم شود و می‌خواهیم `content` در حین انتظار برای بررسی یک رشته خالی +بازگرداند. وقتی پست تأیید شود، باید منتشر شود، به این معنی که متن پست هنگام فراخوانی `content` بازگردانده خواهد شد. + +توجه کنید که تنها نوعی که از crate با آن تعامل داریم، نوع `Post` است. این نوع از الگوی حالت (state pattern) استفاده خواهد کرد و یک مقدار نگه می‌دارد که یکی از سه شیء حالت مختلف را نمایندگی می‌کند—حالت‌های پیش‌نویس (draft)، بازبینی (review) یا منتشر شده (published). تغییر از یک حالت به حالت دیگر به‌صورت داخلی و درون نوع `Post` مدیریت می‌شود. این حالت‌ها در پاسخ به متدهایی که کاربران کتابخانه روی نمونه‌ی `Post` فراخوانی می‌کنند تغییر می‌کنند، اما کاربران نیازی به مدیریت مستقیم تغییرات حالت ندارند. همچنین، کاربران نمی‌توانند در مدیریت حالت‌ها اشتباه کنند، مثلاً ارسال پستی قبل از بازبینی آن. + +#### تعریف `Post` و ایجاد یک نمونه‌ی جدید در حالت پیش‌نویس (Draft) + +بیایید پیاده‌سازی کتابخانه را شروع کنیم! می‌دانیم که به یک ساختار `Post` عمومی نیاز داریم که مقداری محتوا را نگه +می‌دارد، بنابراین با تعریف این ساختار و یک تابع مرتبط `new` عمومی برای ایجاد یک نمونه از `Post` شروع می‌کنیم. این +تعاریف در لیستینگ 18-12 آمده‌اند. همچنین، یک صفت خصوصی `State` ایجاد خواهیم کرد که رفتاری را که تمام اشیای وضعیت +برای `Post` باید داشته باشند تعریف می‌کند. + +سپس، `Post` یک شیء صفت `Box<dyn State>` را درون یک `Option<T>` در یک فیلد خصوصی به نام `state` نگه خواهد داشت تا +شیء وضعیت را مدیریت کند. در ادامه خواهید دید که چرا `Option<T>` ضروری است. + +<Listing number="18-12" file-name="src/lib.rs" caption="تعریف یک ساختار `Post` و یک تابع `new` که یک نمونه جدید از `Post` ایجاد می‌کند، یک صفت `State`، و یک ساختار `Draft`"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-12/src/lib.rs}} @@ -102,30 +72,25 @@ in a private field named `state` to hold the state object. You’ll see why the </Listing> -The `State` trait defines the behavior shared by different post states. The -state objects are `Draft`, `PendingReview`, and `Published`, and they will all -implement the `State` trait. For now, the trait doesn’t have any methods, and -we’ll start by defining just the `Draft` state because that is the state we -want a post to start in. +صفت `State` رفتاری را که وضعیت‌های مختلف پست‌ها به اشتراک می‌گذارند تعریف می‌کند. اشیای وضعیت شامل `Draft`, +`PendingReview` و `Published` هستند و همه آن‌ها صفت `State` را پیاده‌سازی خواهند کرد. فعلاً صفت هیچ متدی ندارد و +ما با تعریف تنها وضعیت `Draft` شروع می‌کنیم، زیرا این وضعیت است که می‌خواهیم پست در آن شروع شود. -When we create a new `Post`, we set its `state` field to a `Some` value that -holds a `Box`. This `Box` points to a new instance of the `Draft` struct. This -ensures that whenever we create a new instance of `Post`, it will start out as -a draft. Because the `state` field of `Post` is private, there is no way to -create a `Post` in any other state! In the `Post::new` function, we set the -`content` field to a new, empty `String`. +وقتی یک `Post` جدید ایجاد می‌کنیم، فیلد `state` آن را به یک مقدار `Some` تنظیم می‌کنیم که یک `Box` را نگه می‌دارد. +این `Box` به یک نمونه جدید از ساختار `Draft` اشاره می‌کند. این کار تضمین می‌کند که هرگاه یک نمونه جدید از `Post` +ایجاد شود، به‌عنوان یک پیش‌نویس شروع شود. از آنجا که فیلد `state` در `Post` خصوصی است، هیچ راهی برای ایجاد یک +`Post` در وضعیت دیگری وجود ندارد! در تابع `Post::new`، فیلد `content` را به یک `String` جدید و خالی تنظیم +می‌کنیم. -#### Storing the Text of the Post Content +### ذخیره متن محتوای پست -We saw in Listing 18-11 that we want to be able to call a method named -`add_text` and pass it a `&str` that is then added as the text content of the -blog post. We implement this as a method, rather than exposing the `content` -field as `pub`, so that later we can implement a method that will control how -the `content` field’s data is read. The `add_text` method is pretty -straightforward, so let’s add the implementation in Listing 18-13 to the `impl -Post` block. +در لیستینگ 18-11 دیدیم که می‌خواهیم بتوانیم یک متد به نام `add_text` فراخوانی کنیم و یک `&str` به آن بدهیم که به +عنوان محتوای متنی پست وبلاگ اضافه شود. این کار را به‌صورت یک متد پیاده‌سازی می‌کنیم تا فیلد `content` را به‌جای +تعریف آن به‌صورت `pub` کنترل کنیم و بتوانیم در آینده متدی برای کنترل چگونگی خواندن داده فیلد `content` +پیاده‌سازی کنیم. متد `add_text` نسبتاً ساده است، بنابراین بیایید پیاده‌سازی آن را به بلوک `impl Post` در لیستینگ +18-13 اضافه کنیم: -<Listing number="18-13" file-name="src/lib.rs" caption="Implementing the `add_text` method to add text to a post’s `content`"> +<Listing number="18-13" file-name="src/lib.rs" caption="پیاده‌سازی متد `add_text` برای افزودن متن به `content` یک پست"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-13/src/lib.rs:here}} @@ -133,26 +98,14 @@ Post` block. </Listing> -The `add_text` method takes a mutable reference to `self` because we’re -changing the `Post` instance that we’re calling `add_text` on. We then call -`push_str` on the `String` in `content` and pass the `text` argument to add to -the saved `content`. This behavior doesn’t depend on the state the post is in, -so it’s not part of the state pattern. The `add_text` method doesn’t interact -with the `state` field at all, but it is part of the behavior we want to -support. +متد `add_text` یک رفرنس قابل‌تغییر به `self` می‌گیرد، زیرا ما در حال تغییر نمونه‌ی `Post` هستیم که روی آن این متد را فراخوانی می‌کنیم. سپس متد `push_str` را روی رشته‌ی درون `content` فراخوانی می‌کنیم و آرگومان `text` را به آن می‌دهیم تا به محتوای ذخیره‌شده اضافه شود. این رفتار به وضعیت فعلی پست بستگی ندارد، بنابراین بخشی از الگوی حالت نیست. متد `add_text` اصلاً با فیلد `state` تعامل ندارد، اما بخشی از رفتار کلی است که می‌خواهیم پشتیبانی کنیم. + +#### اطمینان از اینکه محتوای یک پست پیش‌نویس خالی است -#### Ensuring the Content of a Draft Post Is Empty +حتی پس از آن‌که `add_text` را فراخوانی کردیم و مقداری محتوا به پست اضافه نمودیم، همچنان می‌خواهیم متد `content` یک رشته‌ی خالی برگرداند، زیرا پست هنوز در حالت پیش‌نویس (draft) قرار دارد، همان‌طور که در خط ۷ لیستینگ 18-11 نشان داده شده است. فعلاً، متد `content` را به ساده‌ترین شکل ممکن پیاده‌سازی می‌کنیم که این نیاز را برآورده کند: همیشه یک برش رشته‌ی خالی بازگرداند. بعداً، زمانی که توانایی تغییر حالت پست برای منتشر شدن را پیاده‌سازی کنیم، این متد را تغییر خواهیم داد. تا اینجا، پست‌ها تنها می‌توانند در حالت پیش‌نویس باشند، پس محتوای پست همیشه باید خالی باشد. لیستینگ 18-14 این پیاده‌سازی جایگزین را نشان می‌دهد. -Even after we’ve called `add_text` and added some content to our post, we still -want the `content` method to return an empty string slice because the post is -still in the draft state, as shown on line 7 of Listing 18-11. For now, let’s -implement the `content` method with the simplest thing that will fulfill this -requirement: always returning an empty string slice. We’ll change this later -once we implement the ability to change a post’s state so it can be published. -So far, posts can only be in the draft state, so the post content should always -be empty. Listing 18-14 shows this placeholder implementation. -<Listing number="18-14" file-name="src/lib.rs" caption="Adding a placeholder implementation for the `content` method on `Post` that always returns an empty string slice"> +<Listing number="18-14" file-name="src/lib.rs" caption="افزودن یک پیاده‌سازی موقت برای متد `content` در `Post` که همیشه یک برش رشته خالی بازمی‌گرداند"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-14/src/lib.rs:here}} @@ -160,19 +113,17 @@ be empty. Listing 18-14 shows this placeholder implementation. </Listing> -With this added `content` method, everything in Listing 18-11 up to line 7 -works as intended. +با افزودن این متد `content`، تمام موارد تا خط 7 لیستینگ 18-11 به درستی کار می‌کنند. <!-- Old headings. Do not remove or links may break. --> <a id="requesting-a-review-of-the-post-changes-its-state"></a> -#### Requesting a Review Changes the Post’s State +#### درخواست بازبینی، وضعیت پست را تغییر می‌دهد -Next, we need to add functionality to request a review of a post, which should -change its state from `Draft` to `PendingReview`. Listing 18-15 shows this code. +در مرحله‌ی بعد، باید قابلیت درخواست بازبینی برای یک پست را اضافه کنیم، که باید وضعیت آن را از `Draft` به `PendingReview` تغییر دهد. لیستینگ 18-15 این کد را نشان می‌دهد. -<Listing number="18-15" file-name="src/lib.rs" caption="Implementing `request_review` methods on `Post` and the `State` trait"> +<Listing number="18-15" file-name="src/lib.rs" caption="پیاده‌سازی متدهای `request_review` برای `Post` و صفت `State`"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-15/src/lib.rs:here}} @@ -180,31 +131,21 @@ change its state from `Draft` to `PendingReview`. Listing 18-15 shows this code. </Listing> -We give `Post` a public method named `request_review` that will take a mutable -reference to `self`. Then we call an internal `request_review` method on the -current state of `Post`, and this second `request_review` method consumes the -current state and returns a new state. - -We add the `request_review` method to the `State` trait; all types that -implement the trait will now need to implement the `request_review` method. -Note that rather than having `self`, `&self`, or `&mut self` as the first -parameter of the method, we have `self: Box<Self>`. This syntax means the -method is only valid when called on a `Box` holding the type. This syntax takes -ownership of `Box<Self>`, invalidating the old state so the state value of the -`Post` can transform into a new state. - -To consume the old state, the `request_review` method needs to take ownership -of the state value. This is where the `Option` in the `state` field of `Post` -comes in: we call the `take` method to take the `Some` value out of the `state` -field and leave a `None` in its place because Rust doesn’t let us have -unpopulated fields in structs. This lets us move the `state` value out of -`Post` rather than borrowing it. Then we’ll set the post’s `state` value to the -result of this operation. - -We need to set `state` to `None` temporarily rather than setting it directly -with code like `self.state = self.state.request_review();` to get ownership of -the `state` value. This ensures `Post` can’t use the old `state` value after -we’ve transformed it into a new state. +ما یک متد عمومی به نام `request_review` به `Post` اضافه می‌کنیم که یک ارجاع متغیر به `self` می‌گیرد. سپس یک متد +داخلی `request_review` را روی وضعیت فعلی `Post` فراخوانی می‌کنیم، و این متد دوم وضعیت فعلی را مصرف کرده و یک وضعیت +جدید بازمی‌گرداند. + +ما متد `request_review` را به صفت `State` اضافه می‌کنیم؛ تمام انواعی که این صفت را پیاده‌سازی می‌کنند اکنون باید +متد `request_review` را پیاده‌سازی کنند. توجه داشته باشید که به جای `self`، `&self` یا `&mut self` به‌عنوان اولین +پارامتر متد، از `self: Box<Self>` استفاده کرده‌ایم. این نحو به این معنی است که متد فقط زمانی معتبر است که روی +یک `Box` نگه‌دارنده نوع فراخوانی شود. این نحو مالکیت `Box<Self>` را می‌گیرد و وضعیت قدیمی را باطل می‌کند تا مقدار +وضعیت `Post` بتواند به یک وضعیت جدید تبدیل شود. + +برای مصرف وضعیت قدیمی، متد `request_review` باید مالکیت مقدار وضعیت (`state`) را بگیرد. اینجا است که استفاده از `Option` در فیلد `state` ساختار `Post` اهمیت پیدا می‌کند: ما متد `take` را فراخوانی می‌کنیم تا مقدار `Some` را از فیلد `state` بیرون بکشیم و در عوض آن `None` قرار دهیم، زیرا Rust اجازه نمی‌دهد فیلدهای بدون مقدار (unpopulated) در ساختارها وجود داشته باشد. این کار به ما اجازه می‌دهد مقدار `state` را به‌جای قرض‌گرفتن، به بیرون منتقل کنیم (move). سپس مقدار `state` پست را به نتیجه‌ی این عملیات اختصاص می‌دهیم. + +باید به‌طور موقت `state` را به `None` تنظیم کنیم، نه اینکه مستقیماً آن را با کدی مانند +`self.state = self.state.request_review();` تنظیم کنیم، تا مالکیت مقدار `state` را بدست آوریم. این کار اطمینان +می‌دهد که `Post` نمی‌تواند از مقدار قدیمی `state` پس از تبدیل آن به یک وضعیت جدید استفاده کند. The `request_review` method on `Draft` returns a new, boxed instance of a new `PendingReview` struct, which represents the state when a post is waiting for a @@ -213,26 +154,22 @@ but doesn’t do any transformations. Rather, it returns itself because when we request a review on a post already in the `PendingReview` state, it should stay in the `PendingReview` state. -Now we can start seeing the advantages of the state pattern: the -`request_review` method on `Post` is the same no matter its `state` value. Each -state is responsible for its own rules. +اکنون می‌توانیم مزایای الگوی وضعیت را مشاهده کنیم: متد `request_review` در `Post` بدون توجه به مقدار `state` آن +یکسان است. هر وضعیت مسئول قوانین خاص خود است. -We’ll leave the `content` method on `Post` as is, returning an empty string -slice. We can now have a `Post` in the `PendingReview` state as well as in the -`Draft` state, but we want the same behavior in the `PendingReview` state. -Listing 18-11 now works up to line 10! +ما متد `content` در `Post` را به همان صورت باقی می‌گذاریم که یک برش رشته خالی بازمی‌گرداند. اکنون می‌توانیم +یک `Post` در وضعیت `PendingReview` و همچنین در وضعیت `Draft` داشته باشیم، اما می‌خواهیم همان رفتار در وضعیت +`PendingReview` نیز باشد. لیستینگ 18-11 اکنون تا خط 10 کار می‌کند! <!-- Old headings. Do not remove or links may break. --> <a id="adding-the-approve-method-that-changes-the-behavior-of-content"></a> -#### Adding `approve` to Change the Behavior of `content` +#### افزودن متد `approve` برای تغییر رفتار `content` -The `approve` method will be similar to the `request_review` method: it will -set `state` to the value that the current state says it should have when that -state is approved, as shown in Listing 18-16. +متد `approve` شبیه به متد `request_review` خواهد بود: این متد فیلد `state` را به مقداری تغییر می‌دهد که وضعیت فعلی (`state` فعلی) تعیین می‌کند که هنگام تأیید آن وضعیت، باید به آن تغییر یابد؛ همان‌طور که در لیستینگ 18-16 نشان داده شده است. -<Listing number="18-16" file-name="src/lib.rs" caption="Implementing the `approve` method on `Post` and the `State` trait"> +<Listing number="18-16" file-name="src/lib.rs" caption="پیاده‌سازی متد `approve` در `Post` و صفت `State`"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-16/src/lib.rs:here}} @@ -240,23 +177,14 @@ state is approved, as shown in Listing 18-16. </Listing> -We add the `approve` method to the `State` trait and add a new struct that -implements `State`, the `Published` state. +ما متد `approve` را به صفت `State` اضافه می‌کنیم و یک ساختار جدید که صفت `State` را پیاده‌سازی می‌کند، یعنی وضعیت +`Published`، اضافه می‌کنیم. -Similar to the way `request_review` on `PendingReview` works, if we call the -`approve` method on a `Draft`, it will have no effect because `approve` will -return `self`. When we call `approve` on `PendingReview`, it returns a new, -boxed instance of the `Published` struct. The `Published` struct implements the -`State` trait, and for both the `request_review` method and the `approve` -method, it returns itself because the post should stay in the `Published` state -in those cases. +مشابه روشی که متد `request_review` در حالت `PendingReview` کار می‌کند، اگر متد `approve` را روی حالت `Draft` فراخوانی کنیم، هیچ تأثیری نخواهد داشت زیرا `approve` در این حالت خودِ `self` را باز می‌گرداند. زمانی که متد `approve` را روی حالت `PendingReview` فراخوانی کنیم، یک نمونه‌ی جدید از ساختار `Published` را به‌صورت جعبه‌شده (boxed) باز می‌گرداند. ساختار `Published` نیز `trait` مربوط به `State` را پیاده‌سازی می‌کند، و برای هر دو متد `request_review` و `approve`، خودِ `self` را برمی‌گرداند، زیرا در این موارد پست باید در وضعیت `Published` باقی بماند. -Now we need to update the `content` method on `Post`. We want the value -returned from `content` to depend on the current state of the `Post`, so we’re -going to have the `Post` delegate to a `content` method defined on its `state`, -as shown in Listing 18-17. +اکنون باید متد `content` را در ساختار `Post` به‌روز کنیم. ما می‌خواهیم مقداری که از `content` بازگردانده می‌شود، به وضعیت فعلیِ (`state`) پست بستگی داشته باشد. بنابراین از متد `content` تعریف‌شده در وضعیت (`state`) فعلیِ پست استفاده خواهیم کرد، همان‌طور که در لیستینگ 18-17 نشان داده شده است. -<Listing number="18-17" file-name="src/lib.rs" caption="Updating the `content` method on `Post` to delegate to a `content` method on `State`"> +<Listing number="18-17" file-name="src/lib.rs" caption="به‌روزرسانی متد `content` در `Post` برای ارجاع به متد `content` در `State`"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch18-oop/listing-18-17/src/lib.rs:here}} @@ -264,33 +192,34 @@ as shown in Listing 18-17. </Listing> -Because the goal is to keep all of these rules inside the structs that -implement `State`, we call a `content` method on the value in `state` and pass -the post instance (that is, `self`) as an argument. Then we return the value -that’s returned from using the `content` method on the `state` value. - -We call the `as_ref` method on the `Option` because we want a reference to the -value inside the `Option` rather than ownership of the value. Because `state` is -an `Option<Box<dyn State>>`, when we call `as_ref`, an `Option<&Box<dyn -State>>` is returned. If we didn’t call `as_ref`, we would get an error because -we can’t move `state` out of the borrowed `&self` of the function parameter. - -We then call the `unwrap` method, which we know will never panic because we -know the methods on `Post` ensure that `state` will always contain a `Some` -value when those methods are done. This is one of the cases we talked about in -[“Cases in Which You Have More Information Than the -Compiler”][more-info-than-rustc]<!-- ignore --> in Chapter 9 when we know that -a `None` value is never possible, even though the compiler isn’t able to -understand that. - -At this point, when we call `content` on the `&Box<dyn State>`, deref coercion -will take effect on the `&` and the `Box` so the `content` method will -ultimately be called on the type that implements the `State` trait. That means -we need to add `content` to the `State` trait definition, and that is where -we’ll put the logic for what content to return depending on which state we -have, as shown in Listing 18-18. - -<Listing number="18-18" file-name="src/lib.rs" caption="Adding the `content` method to the `State` trait"> +چون هدف این است که تمام این قوانین در داخل ساختارهایی که صفت `State` را پیاده‌سازی می‌کنند باقی بماند، ما یک متد +`content` را روی مقدار `state` فراخوانی می‌کنیم و نمونه پست (یعنی `self`) را به‌عنوان آرگومان به آن می‌دهیم. سپس +مقداری که از متد `content` روی مقدار `state` بازمی‌گردد را بازمی‌گردانیم. + +ما متد `as_ref` را روی `Option` فراخوانی می‌کنیم زیرا می‌خواهیم یک ارجاع به مقدار داخل `Option` داشته باشیم، نه +مالکیت مقدار. چون `state` یک `Option<Box<dyn State>>` است، وقتی `as_ref` را فراخوانی می‌کنیم، یک +`Option<&Box<dyn State>>` بازمی‌گردد. اگر `as_ref` را فراخوانی نکنیم، با یک خطا مواجه می‌شویم زیرا نمی‌توانیم +`state` را از `&self` که به‌عنوان پارامتر به تابع داده شده است خارج کنیم. + +سپس متد `unwrap` را فراخوانی می‌کنیم که می‌دانیم هرگز وحشت (panic) نخواهد کرد، زیرا می‌دانیم متدهای `Post` +تضمین می‌کنند که `state` همیشه یک مقدار `Some` دارد وقتی این متدها کارشان را تمام می‌کنند. این یکی از مواردی است +که در بخش [“مواردی که شما اطلاعات بیشتری نسبت به کامپایلر دارید”][more-info-than-rustc]<!-- ignore --> در فصل 9 +در مورد آن صحبت کردیم، زمانی که می‌دانیم یک مقدار `None` هرگز ممکن نیست، حتی اگر کامپایلر نتواند این موضوع را +درک کند. + +در این مرحله، وقتی `content` را روی `&Box<dyn State>` فراخوانی می‌کنیم، تبدیل خودکار به نوع ارجاع (`deref coercion`) +روی `&` و `Box` اعمال می‌شود تا متد `content` در نهایت روی نوعی که صفت `State` را پیاده‌سازی می‌کند، فراخوانی شود. +این بدان معناست که باید `content` را به تعریف صفت `State` اضافه کنیم، و اینجا جایی است که منطق مربوط به بازگرداندن +محتوا بر اساس وضعیت فعلی قرار خواهد گرفت، همان‌طور که در لیستینگ 18-18 نشان داده شده است: +چون هدف این است که تمام این قوانین را درون ساختارهایی که `State` را پیاده‌سازی می‌کنند نگه داریم، متد `content` را روی مقداری که در فیلد `state` است فراخوانی می‌کنیم و نمونه‌ی فعلی پست (یعنی `self`) را به‌عنوان آرگومان ارسال می‌کنیم. سپس مقدار بازگشتیِ متد `content` روی مقدارِ `state` را برمی‌گردانیم. + +ما متد `as_ref` را روی `Option` فراخوانی می‌کنیم، زیرا می‌خواهیم یک رفرنس به مقداری که درون `Option` قرار دارد بگیریم و نه مالکیت آن مقدار را. به‌دلیل اینکه `state` از نوع `Option<Box<dyn State>>` است، وقتی `as_ref` را فراخوانی می‌کنیم، نوع بازگشتی ما `Option<&Box<dyn State>>` خواهد بود. اگر `as_ref` را فراخوانی نمی‌کردیم، دچار خطا می‌شدیم زیرا نمی‌توانیم مقدار `state` را از رفرنس قرضیِ (`&self`) که به تابع داده شده بیرون بکشیم. + +سپس متد `unwrap` را فراخوانی می‌کنیم که می‌دانیم هرگز باعث panic نمی‌شود، زیرا متدهایی که روی `Post` تعریف کرده‌ایم تضمین می‌کنند که پس از اجرای این متدها، مقدار `state` همیشه `Some` باشد. این مورد یکی از همان حالت‌هایی است که در بخش [«حالتی که شما اطلاعات بیشتری نسبت به کامپایلر دارید»][more-info-than-rustc]<!-- ignore --> در فصل ۹ اشاره کردیم، یعنی زمانی که می‌دانیم مقدار `None` هرگز رخ نمی‌دهد ولی کامپایلر قادر به تشخیص آن نیست. + +در این مرحله، وقتی متد `content` را روی `&Box<dyn State>` فراخوانی می‌کنیم، عمل deref coercion روی `&` و `Box` اتفاق می‌افتد و در نهایت متد `content` روی نوعی که `trait` مربوط به `State` را پیاده‌سازی کرده است فراخوانی خواهد شد. این بدان معناست که باید متد `content` را به تعریف `trait` مربوط به `State` اضافه کنیم؛ و این همان جایی است که منطق بازگرداندن محتوا را، بر اساس وضعیت فعلیِ پست، قرار می‌دهیم. این موضوع در لیستینگ 18-18 نشان داده شده است. + +<Listing number="18-18" file-name="src/lib.rs" caption="افزودن متد `content` به `trait` مربوط به `State`"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-18/src/lib.rs:here}} @@ -298,100 +227,59 @@ have, as shown in Listing 18-18. </Listing> -We add a default implementation for the `content` method that returns an empty -string slice. That means we don’t need to implement `content` on the `Draft` -and `PendingReview` structs. The `Published` struct will override the `content` -method and return the value in `post.content`. While convenient, having the -`content` method on `State` determine the `content` of the `Post` is blurring -the lines between the responsibility of `State` and the responsibility of -`Post`. +ما یک پیاده‌سازی پیش‌فرض (default implementation) برای متد `content` اضافه می‌کنیم که یک برش رشته‌ی خالی (`""`) برمی‌گرداند. این یعنی دیگر نیازی نداریم متد `content` را برای ساختارهای `Draft` و `PendingReview` پیاده‌سازی کنیم. ساختار `Published` متد `content` را override می‌کند و مقداری که در `post.content` وجود دارد را برمی‌گرداند. گرچه این روش راحت است، اما باعث می‌شود مرز بین مسئولیت‌های `State` و مسئولیت‌های `Post` کمی مبهم شود. -Note that we need lifetime annotations on this method, as we discussed in -Chapter 10. We’re taking a reference to a `post` as an argument and returning a -reference to part of that `post`, so the lifetime of the returned reference is -related to the lifetime of the `post` argument. +توجه داشته باشید که برای این متد نیاز به حاشیه‌نویسی طول عمر داریم، همان‌طور که در فصل 10 توضیح داده شد. چون یک +ارجاع به یک `post` را به‌عنوان آرگومان می‌گیریم و یک ارجاع به بخشی از آن `post` را بازمی‌گردانیم، طول عمر ارجاع +بازگشتی به طول عمر آرگومان `post` مرتبط است. -And we’re done—all of Listing 18-11 now works! We’ve implemented the state -pattern with the rules of the blog post workflow. The logic related to the -rules lives in the state objects rather than being scattered throughout `Post`. +و تمام—اکنون تمام لیستینگ 18-11 کار می‌کند! ما الگوی وضعیت را با قوانین مربوط به فرآیند کاری پست وبلاگ پیاده‌سازی +کرده‌ایم. منطق مربوط به قوانین در اشیای وضعیت قرار دارد، نه اینکه در سراسر `Post` پراکنده باشد. -> ### Why Not An Enum? +> ### چرا از enum استفاده نکردیم؟ > -> You may have been wondering why we didn’t use an `enum` with the different -> possible post states as variants. That’s certainly a possible solution; try it -> and compare the end results to see which you prefer! One disadvantage of using -> an enum is that every place that checks the value of the enum will need a -> `match` expression or similar to handle every possible variant. This could get -> more repetitive than this trait object solution. - -#### Trade-offs of the State Pattern - -We’ve shown that Rust is capable of implementing the object-oriented state -pattern to encapsulate the different kinds of behavior a post should have in -each state. The methods on `Post` know nothing about the various behaviors. The -way we organized the code, we have to look in only one place to know the -different ways a published post can behave: the implementation of the `State` -trait on the `Published` struct. - -If we were to create an alternative implementation that didn’t use the state -pattern, we might instead use `match` expressions in the methods on `Post` or -even in the `main` code that checks the state of the post and changes behavior -in those places. That would mean we would have to look in several places to -understand all the implications of a post being in the published state. - -With the state pattern, the `Post` methods and the places we use `Post` don’t -need `match` expressions, and to add a new state, we would only need to add a -new struct and implement the trait methods on that one struct in one location. - -The implementation using the state pattern is easy to extend to add more -functionality. To see the simplicity of maintaining code that uses the state -pattern, try a few of these suggestions: - -- Add a `reject` method that changes the post’s state from `PendingReview` back - to `Draft`. -- Require two calls to `approve` before the state can be changed to `Published`. -- Allow users to add text content only when a post is in the `Draft` state. - Hint: have the state object responsible for what might change about the - content but not responsible for modifying the `Post`. - -One downside of the state pattern is that, because the states implement the -transitions between states, some of the states are coupled to each other. If we -add another state between `PendingReview` and `Published`, such as `Scheduled`, -we would have to change the code in `PendingReview` to transition to -`Scheduled` instead. It would be less work if `PendingReview` didn’t need to -change with the addition of a new state, but that would mean switching to -another design pattern. - -Another downside is that we’ve duplicated some logic. To eliminate some of the -duplication, we might try to make default implementations for the -`request_review` and `approve` methods on the `State` trait that return `self`. -However, this wouldn’t work: when using `State` as a trait object, the trait -doesn’t know what the concrete `self` will be exactly, so the return type isn’t -known at compile time. (This is one of the `dyn` compatibility rules mentioned -earlier.) - -Other duplication includes the similar implementations of the `request_review` -and `approve` methods on `Post`. Both methods use `Option::take` with the -`state` field of `Post`, and if `state` is `Some`, they delegate to the wrapped -value’s implementation of the same method and set the new value of the `state` -field to the result. If we had a lot of methods on `Post` that followed this -pattern, we might consider defining a macro to eliminate the repetition (see -[“Macros”][macros]<!-- ignore --> in Chapter 20). - -By implementing the state pattern exactly as it’s defined for object-oriented -languages, we’re not taking as full advantage of Rust’s strengths as we could. -Let’s look at some changes we can make to the `blog` crate that can make -invalid states and transitions into compile-time errors. - -### Encoding States and Behavior as Types - -We’ll show you how to rethink the state pattern to get a different set of -trade-offs. Rather than encapsulating the states and transitions completely so -outside code has no knowledge of them, we’ll encode the states into different -types. Consequently, Rust’s type checking system will prevent attempts to use -draft posts where only published posts are allowed by issuing a compiler error. - -Let’s consider the first part of `main` in Listing 18-11: +> شاید برایتان این سؤال پیش آمده باشد که چرا از یک `enum` با حالت‌های ممکن مختلف پست به‌عنوان واریانت‌ها استفاده نکردیم. +> قطعاً این راه‌حل نیز قابل استفاده است؛ آن را امتحان کنید و نتایج نهایی را مقایسه کنید تا ببینید کدام روش را ترجیح می‌دهید! یکی از معایب استفاده از `enum` این است که در هر جایی که نیاز دارید مقدار آن enum را بررسی کنید، مجبورید از یک عبارت `match` (یا چیزی مشابه آن) استفاده کنید تا تمام حالت‌های ممکن را مدیریت کنید. این موضوع می‌تواند در مقایسه با راه‌حل trait object که اینجا استفاده کردیم، منجر به تکرار کد بیشتری شود. + +#### معایب و مزایای الگوی حالت (State Pattern) + +ما نشان داده‌ایم که Rust قادر است الگوی وضعیت شی‌گرا را برای کپسوله کردن رفتارهای مختلف یک پست در هر حالت +پیاده‌سازی کند. متدهای `Post` هیچ اطلاعی از رفتارهای مختلف ندارند. با روشی که کد را سازمان‌دهی کرده‌ایم، تنها +باید در یک مکان به‌دنبال راه‌های مختلف رفتار یک پست منتشرشده بگردیم: پیاده‌سازی صفت `State` روی ساختار +`Published`. + +اگر می‌خواستیم یک پیاده‌سازی جایگزین ایجاد کنیم که از الگوی حالت (state pattern) استفاده نکند، احتمالاً مجبور بودیم در متدهای ساختار `Post` و یا حتی در کد تابع `main`، از عبارات `match` استفاده کنیم تا وضعیت فعلی پست را بررسی کرده و رفتار مناسب را در آن‌جا انتخاب کنیم. در این حالت، برای درک تمام اثرات ناشی از قرار گرفتن پست در وضعیت منتشرشده (`published`)، باید چندین نقطه‌ی مختلف از کد را بررسی می‌کردیم. + +اما با استفاده از الگوی حالت، متدهای ساختار `Post` و مکان‌هایی که از `Post` استفاده می‌کنیم، نیازی به عبارات `match` ندارند. همچنین، برای افزودن یک حالت جدید تنها کافی است یک ساختار جدید ایجاد کرده و متدهای `trait` را فقط برای همان ساختار جدید و در یک محل واحد پیاده‌سازی کنیم. + +پیاده‌سازی با استفاده از الگوی وضعیت به‌راحتی قابلیت گسترش برای اضافه کردن عملکردهای بیشتر را دارد. برای دیدن +سادگی نگهداری کدی که از الگوی وضعیت استفاده می‌کند، چند پیشنهاد زیر را امتحان کنید: + +- یک متد `reject` اضافه کنید که وضعیت پست را از `PendingReview` به `Draft` تغییر دهد. +- دو فراخوانی به `approve` نیاز داشته باشید تا وضعیت به `Published` تغییر کند. +- اجازه دهید کاربران فقط زمانی که یک پست در حالت `Draft` است متن محتوا اضافه کنند. نکته: بگذارید شیء وضعیت + مسئول تغییراتی باشد که ممکن است در محتوا ایجاد شود، اما مسئول اصلاح مستقیم `Post` نباشد. + +یکی از معایب الگوی وضعیت این است که به دلیل اینکه وضعیت‌ها انتقال بین حالت‌ها را پیاده‌سازی می‌کنند، برخی از +وضعیت‌ها به یکدیگر وابسته هستند. اگر یک حالت دیگر بین `PendingReview` و `Published` اضافه کنیم، مانند +`Scheduled`، باید کد در `PendingReview` را تغییر دهیم تا به `Scheduled` منتقل شود. اگر نیازی نبود که +`PendingReview` با اضافه شدن یک حالت جدید تغییر کند، کار کمتری می‌داشتیم، اما این به معنای تغییر به یک الگوی +طراحی دیگر خواهد بود. + +نکته‌ی منفی دیگر این است که ما در اینجا منطق‌هایی را تکرار کرده‌ایم. برای حذف بخشی از این تکرارها، ممکن است تلاش کنیم پیاده‌سازی پیش‌فرضی برای متدهای `request_review` و `approve` در `trait` مربوط به `State` ایجاد کنیم که مقدار `self` را بازگردانند. اما این رویکرد جواب نمی‌دهد: وقتی از `State` به عنوان یک trait object استفاده می‌کنیم، آن trait دقیقاً نمی‌داند که نوع مشخص `self` چیست، بنابراین نوع بازگشتی در زمان کامپایل قابل تشخیص نیست. (این یکی از همان قواعد مربوط به سازگاری با `dyn` است که پیش‌تر به آن اشاره شد.) + +سایر موارد تکرار شامل پیاده‌سازی‌های مشابه متدهای `request_review` و `approve` در `Post` است. هر دو متد از `Option::take` با فیلد `state` از ساختار `Post` استفاده می‌کنند و اگر مقدار `state` برابر با `Some` باشد، عملیات به پیاده‌سازی همان متد در مقدار درون آن منتقل می‌شود و نتیجه‌ی آن را به فیلد `state` اختصاص می‌دهد. اگر متدهای زیادی در `Post` داشته باشیم که از این الگو تبعیت می‌کنند، ممکن است بخواهیم برای حذف تکرار، یک ماکرو تعریف کنیم (به بخش [«ماکروها»][macros]<!-- ignore --> در فصل ۲۰ مراجعه کنید). + +با پیاده‌سازی الگوی حالت دقیقاً همان‌طور که برای زبان‌های شیءگرای سنتی تعریف شده است، ما به‌طور کامل از نقاط قوت زبان Rust استفاده نمی‌کنیم. بیایید تغییراتی را بررسی کنیم که می‌توانیم در `crate` مربوط به `blog` ایجاد کنیم تا حالت‌ها و انتقال‌های نادرست را به خطاهای زمان کامپایل تبدیل کنیم. + +### کدگذاری حالت‌ها و رفتارها به عنوان نوع‌ها (Encoding States and Behavior as Types) + +به شما نشان خواهیم داد که چگونه الگوی وضعیت را دوباره طراحی کنید تا مجموعه‌ای متفاوت از مزایا و معایب به دست +آورید. به‌جای اینکه وضعیت‌ها و انتقالات را کاملاً کپسوله کنیم تا کد خارجی از آن‌ها اطلاعی نداشته باشد، وضعیت‌ها را +به انواع مختلف کدگذاری می‌کنیم. در نتیجه، سیستم بررسی نوع Rust تلاش برای استفاده از پست‌های پیش‌نویس در جاهایی که +فقط پست‌های منتشرشده مجاز هستند را با صدور یک خطای کامپایلر متوقف می‌کند. + +ابتدا قسمت اول `main` در لیستینگ 18-11 را در نظر بگیرید: <Listing file-name="src/main.rs"> @@ -401,17 +289,10 @@ Let’s consider the first part of `main` in Listing 18-11: </Listing> -We still enable the creation of new posts in the draft state using `Post::new` -and the ability to add text to the post’s content. But instead of having a -`content` method on a draft post that returns an empty string, we’ll make it so -draft posts don’t have the `content` method at all. That way, if we try to get -a draft post’s content, we’ll get a compiler error telling us the method -doesn’t exist. As a result, it will be impossible for us to accidentally -display draft post content in production because that code won’t even compile. -Listing 18-19 shows the definition of a `Post` struct and a `DraftPost` struct, -as well as methods on each. +ما همچنان اجازه می‌دهیم که پست‌های جدید در حالت پیش‌نویس (draft) توسط متد `Post::new` ایجاد شوند و امکان اضافه کردن متن به محتوای پست را نیز خواهیم داشت. اما به جای این‌که یک متد `content` روی پست پیش‌نویس تعریف کنیم که یک رشته‌ی خالی برمی‌گرداند، کاری می‌کنیم که پست‌های پیش‌نویس اصلاً متد `content` نداشته باشند. به این ترتیب، اگر تلاش کنیم محتوای یک پست پیش‌نویس را بخوانیم، کامپایلر به ما خطا می‌دهد و اعلام می‌کند که چنین متدی وجود ندارد. در نتیجه، غیرممکن خواهد شد که به‌صورت تصادفی محتوای یک پست پیش‌نویس را در محیط نهایی (production) نمایش دهیم، زیرا اساساً چنین کدی کامپایل نمی‌شود. +لیستینگ 18-19 تعریف ساختارهای `Post` و `DraftPost` و همچنین متدهای مربوط به هرکدام را نشان می‌دهد. -<Listing number="18-19" file-name="src/lib.rs" caption="A `Post` with a `content` method and `DraftPost` without a `content` method"> +<Listing number="18-19" file-name="src/lib.rs" caption="یک `Post` با یک متد `content` و یک `DraftPost` بدون متد `content`"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-19/src/lib.rs}} @@ -419,36 +300,26 @@ as well as methods on each. </Listing> -Both the `Post` and `DraftPost` structs have a private `content` field that -stores the blog post text. The structs no longer have the `state` field because -we’re moving the encoding of the state to the types of the structs. The `Post` -struct will represent a published post, and it has a `content` method that -returns the `content`. +هر دو ساختار `Post` و `DraftPost` دارای یک فیلد خصوصی به نام `content` هستند که متن پست وبلاگ را ذخیره می‌کند. این +ساختارها دیگر فیلد `state` ندارند زیرا کدگذاری وضعیت را به انواع ساختارها منتقل کرده‌ایم. ساختار `Post` نماینده +یک پست منتشرشده است و دارای متد `content` است که مقدار `content` را بازمی‌گرداند. -We still have a `Post::new` function, but instead of returning an instance of -`Post`, it returns an instance of `DraftPost`. Because `content` is private and -there aren’t any functions that return `Post`, it’s not possible to create an -instance of `Post` right now. +ما همچنان یک تابع `Post::new` داریم، اما به‌جای بازگرداندن نمونه‌ای از `Post`، یک نمونه از `DraftPost` بازمی‌گرداند. +از آنجا که `content` خصوصی است و هیچ تابعی وجود ندارد که `Post` را بازگرداند، در حال حاضر امکان ایجاد نمونه‌ای از +`Post` وجود ندارد. -The `DraftPost` struct has an `add_text` method, so we can add text to -`content` as before, but note that `DraftPost` does not have a `content` method -defined! So now the program ensures all posts start as draft posts, and draft -posts don’t have their content available for display. Any attempt to get around -these constraints will result in a compiler error. +ساختار `DraftPost` یک متد `add_text` دارد، بنابراین می‌توانیم همانند قبل متن را به `content` اضافه کنیم، اما +توجه کنید که `DraftPost` متد `content` تعریف‌شده ندارد! بنابراین اکنون برنامه تضمین می‌کند که تمام پست‌ها به‌صورت +پست‌های پیش‌نویس شروع می‌شوند و پست‌های پیش‌نویس محتوای خود را برای نمایش در دسترس ندارند. هر تلاشی برای دور زدن +این محدودیت‌ها منجر به خطای کامپایلر خواهد شد. <!-- Old headings. Do not remove or links may break. --> <a id="implementing-transitions-as-transformations-into-different-types"></a> -So how do we get a published post? We want to enforce the rule that a draft -post has to be reviewed and approved before it can be published. A post in the -pending review state should still not display any content. Let’s implement -these constraints by adding another struct, `PendingReviewPost`, defining the -`request_review` method on `DraftPost` to return a `PendingReviewPost` and -defining an `approve` method on `PendingReviewPost` to return a `Post`, as -shown in Listing 18-20. +حال چگونه یک پست منتشرشده خواهیم داشت؟ می‌خواهیم این قانون را اجباری کنیم که یک پستِ پیش‌نویس حتماً باید پیش از انتشار، بازبینی و تأیید شود. همچنین پستی که در وضعیت انتظار بازبینی (PendingReview) است، همچنان نباید هیچ محتوایی را نمایش دهد. بیایید این محدودیت‌ها را با اضافه کردن یک ساختار دیگر به نام `PendingReviewPost` پیاده‌سازی کنیم. سپس متد `request_review` را در ساختار `DraftPost` طوری تعریف می‌کنیم که یک `PendingReviewPost` بازگرداند و همچنین متد `approve` را در ساختار `PendingReviewPost` تعریف می‌کنیم تا یک `Post` بازگرداند. این روند در لیستینگ 18-20 نشان داده شده است. -<Listing number="18-20" file-name="src/lib.rs" caption="A `PendingReviewPost` that gets created by calling `request_review` on `DraftPost` and an `approve` method that turns a `PendingReviewPost` into a published `Post`"> +<Listing number="18-20" file-name="src/lib.rs" caption="یک `PendingReviewPost` که با فراخوانی `request_review` روی `DraftPost` ایجاد می‌شود و یک متد `approve` که یک `PendingReviewPost` را به یک `Post` منتشرشده تبدیل می‌کند"> ```rust,noplayground {{#rustdoc_include ../listings/ch18-oop/listing-18-20/src/lib.rs:here}} @@ -456,27 +327,22 @@ shown in Listing 18-20. </Listing> -The `request_review` and `approve` methods take ownership of `self`, thus -consuming the `DraftPost` and `PendingReviewPost` instances and transforming -them into a `PendingReviewPost` and a published `Post`, respectively. This way, -we won’t have any lingering `DraftPost` instances after we’ve called -`request_review` on them, and so forth. The `PendingReviewPost` struct doesn’t -have a `content` method defined on it, so attempting to read its content -results in a compiler error, as with `DraftPost`. Because the only way to get a -published `Post` instance that does have a `content` method defined is to call -the `approve` method on a `PendingReviewPost`, and the only way to get a -`PendingReviewPost` is to call the `request_review` method on a `DraftPost`, -we’ve now encoded the blog post workflow into the type system. - -But we also have to make some small changes to `main`. The `request_review` and -`approve` methods return new instances rather than modifying the struct they’re -called on, so we need to add more `let post =` shadowing assignments to save -the returned instances. We also can’t have the assertions about the draft and -pending review posts’ contents be empty strings, nor do we need them: we can’t -compile code that tries to use the content of posts in those states any longer. -The updated code in `main` is shown in Listing 18-21. - -<Listing number="18-21" file-name="src/main.rs" caption="Modifications to `main` to use the new implementation of the blog post workflow"> +متدهای `request_review` و `approve` مالکیت `self` را می‌گیرند، بنابراین نمونه‌های `DraftPost` و `PendingReviewPost` +را مصرف کرده و آن‌ها را به‌ترتیب به یک `PendingReviewPost` و یک `Post` منتشرشده تبدیل می‌کنند. به این ترتیب، +پس از فراخوانی `request_review` روی یک `DraftPost` و به همین ترتیب، هیچ نمونه‌ای از `DraftPost` باقی نمی‌ماند. +ساختار `PendingReviewPost` متد `content` تعریف‌شده‌ای ندارد، بنابراین تلاش برای خواندن محتوای آن منجر به خطای +کامپایلر می‌شود، همان‌طور که در مورد `DraftPost` اتفاق می‌افتد. چون تنها راه برای گرفتن یک نمونه از `Post` منتشرشده +که متد `content` تعریف‌شده‌ای دارد، فراخوانی متد `approve` روی یک `PendingReviewPost` است، و تنها راه برای گرفتن +یک `PendingReviewPost` فراخوانی متد `request_review` روی یک `DraftPost` است، ما اکنون فرآیند کاری پست وبلاگ را به +سیستم نوع کدگذاری کرده‌ایم. + +اما همچنین باید تغییرات کوچکی در `main` ایجاد کنیم. متدهای `request_review` و `approve` نمونه‌های جدیدی بازمی‌گردانند +به‌جای اینکه ساختاری که روی آن فراخوانی شده‌اند را تغییر دهند، بنابراین باید تخصیص‌های مجدد با `let post =` اضافه کنیم +تا نمونه‌های بازگشتی را ذخیره کنیم. همچنین نمی‌توانیم تأییدیه‌های مربوط به خالی بودن محتوای پست‌های پیش‌نویس و در +انتظار بررسی را داشته باشیم، و نیازی به آن‌ها نیست: دیگر نمی‌توانیم کدی که سعی می‌کند محتوای پست‌های در این حالت‌ها را +استفاده کند، کامپایل کنیم. کد به‌روزشده در `main` در لیستینگ 18-21 نشان داده شده است: + +<Listing number="18-21" file-name="src/main.rs" caption="تغییرات در `main` برای استفاده از پیاده‌سازی جدید فرآیند کاری پست وبلاگ"> ```rust,ignore {{#rustdoc_include ../listings/ch18-oop/listing-18-21/src/main.rs}} @@ -484,43 +350,33 @@ The updated code in `main` is shown in Listing 18-21. </Listing> -The changes we needed to make to `main` to reassign `post` mean that this -implementation doesn’t quite follow the object-oriented state pattern anymore: -the transformations between the states are no longer encapsulated entirely -within the `Post` implementation. However, our gain is that invalid states are -now impossible because of the type system and the type checking that happens at -compile time! This ensures that certain bugs, such as display of the content of -an unpublished post, will be discovered before they make it to production. - -Try the tasks suggested at the start of this section on the `blog` crate as it -is after Listing 18-21 to see what you think about the design of this version -of the code. Note that some of the tasks might be completed already in this -design. - -We’ve seen that even though Rust is capable of implementing object-oriented -design patterns, other patterns, such as encoding state into the type system, -are also available in Rust. These patterns have different trade-offs. Although -you might be very familiar with object-oriented patterns, rethinking the -problem to take advantage of Rust’s features can provide benefits, such as -preventing some bugs at compile time. Object-oriented patterns won’t always be -the best solution in Rust due to certain features, like ownership, that -object-oriented languages don’t have. - -## Summary - -Regardless of whether you think Rust is an object-oriented language after -reading this chapter, you now know that you can use trait objects to get some -object-oriented features in Rust. Dynamic dispatch can give your code some -flexibility in exchange for a bit of runtime performance. You can use this -flexibility to implement object-oriented patterns that can help your code’s -maintainability. Rust also has other features, like ownership, that -object-oriented languages don’t have. An object-oriented pattern won’t always -be the best way to take advantage of Rust’s strengths, but it is an available -option. - -Next, we’ll look at patterns, which are another of Rust’s features that enable -lots of flexibility. We’ve looked at them briefly throughout the book but -haven’t seen their full capability yet. Let’s go! +تغییراتی که باید در `main` برای تخصیص مجدد `post` انجام می‌دادیم، به این معناست که این پیاده‌سازی دیگر کاملاً از +الگوی وضعیت شی‌گرا پیروی نمی‌کند: انتقالات بین حالت‌ها دیگر به‌طور کامل در پیاده‌سازی `Post` کپسوله نشده‌اند. +اما، مزیت ما این است که اکنون وضعیت‌های نامعتبر به دلیل سیستم نوع و بررسی نوعی که در زمان کامپایل انجام می‌شود، +غیرممکن هستند! این تضمین می‌کند که برخی از باگ‌ها، مانند نمایش محتوای یک پست منتشرنشده، قبل از رسیدن به تولید +کشف شوند. + +تکالیف پیشنهادی در ابتدای این بخش را روی crate `blog` همان‌طور که پس از لیستینگ 18-21 است امتحان کنید تا ببینید +درباره طراحی این نسخه از کد چه نظری دارید. توجه داشته باشید که برخی از تکالیف ممکن است در این طراحی از پیش +انجام شده باشند. + +دیدیم که حتی با وجود اینکه Rust قادر به پیاده‌سازی الگوهای طراحی شی‌گرا است، الگوهای دیگر، مانند کدگذاری حالت +در سیستم نوع، نیز در Rust در دسترس هستند. این الگوها مزایا و معایب متفاوتی دارند. اگرچه ممکن است با الگوهای شی‌گرا +بسیار آشنا باشید، بازاندیشی مسئله برای بهره‌بردن از ویژگی‌های Rust می‌تواند مزایایی مانند جلوگیری از برخی باگ‌ها +در زمان کامپایل را فراهم کند. الگوهای شی‌گرا همیشه بهترین راه‌حل در Rust نخواهند بود، به دلیل ویژگی‌هایی مانند +مالکیت که زبان‌های شی‌گرا ندارند. + +## خلاصه + +فارغ از اینکه پس از خواندن این فصل فکر می‌کنید Rust یک زبان شی‌گرا است یا نه، اکنون می‌دانید که می‌توانید از +اشیای صفت برای دریافت برخی ویژگی‌های شی‌گرایی در Rust استفاده کنید. تخصیص پویا (Dynamic Dispatch) می‌تواند +انعطاف‌پذیری به کد شما بدهد، اما در ازای آن کمی از عملکرد زمان اجرا را قربانی می‌کند. می‌توانید از این انعطاف‌پذیری +برای پیاده‌سازی الگوهای شی‌گرا که می‌توانند به نگه‌داری کد شما کمک کنند، استفاده کنید. Rust همچنین دارای ویژگی‌های +دیگری مانند مالکیت است که زبان‌های شی‌گرا ندارند. یک الگوی شی‌گرا همیشه بهترین راه برای بهره‌بردن از نقاط قوت +Rust نخواهد بود، اما به‌عنوان یک گزینه در دسترس است. + +در ادامه، به بررسی الگوها خواهیم پرداخت که یکی دیگر از ویژگی‌های Rust است که انعطاف‌پذیری زیادی را فراهم +می‌کنند. در طول کتاب به‌طور مختصر به آن‌ها اشاره کرده‌ایم، اما هنوز به‌طور کامل توانایی آن‌ها را ندیده‌ایم. برویم! [more-info-than-rustc]: ch09-03-to-panic-or-not-to-panic.html#cases-in-which-you-have-more-information-than-the-compiler [macros]: ch20-05-macros.html#macros diff --git a/src/ch19-00-patterns.md b/src/ch19-00-patterns.md index 4574ac0b8e..1a60a33ca1 100644 --- a/src/ch19-00-patterns.md +++ b/src/ch19-00-patterns.md @@ -1,29 +1,20 @@ -# Patterns and Matching +# الگوها و Match -_Patterns_ are a special syntax in Rust for matching against the structure of -types, both complex and simple. Using patterns in conjunction with `match` -expressions and other constructs gives you more control over a program’s -control flow. A pattern consists of some combination of the following: +_الگوها_ یک نحو خاص در Rust هستند که برای مطابقت با ساختار انواع، چه پیچیده و چه ساده، استفاده می‌شوند. استفاده از الگوها همراه با عبارات `match` و سایر سازه‌ها به شما کنترل بیشتری بر جریان کنترل برنامه می‌دهد. یک الگو از ترکیبی از موارد زیر تشکیل شده است: -- Literals -- Destructured arrays, enums, structs, or tuples -- Variables -- Wildcards -- Placeholders +<div dir="rtl"> + <ul> + <li>مقادیر ثابت (Literals)</li> + <li>آرایه‌ها، enumها، structها یا tupleهای تخریب‌شده</li> + <li>متغیرها</li> + <li>کاراکترهای عمومی (Wildcards)</li> + <li>جای‌نگهدارها (Placeholders)</li> + </ul> +</div> -Some example patterns include `x`, `(a, 3)`, and `Some(Color::Red)`. In the -contexts in which patterns are valid, these components describe the shape of -data. Our program then matches values against the patterns to determine whether -it has the correct shape of data to continue running a particular piece of code. +برخی از نمونه الگوها عبارتند از `x`، `(a, 3)` و `Some(Color::Red)`. در زمینه‌هایی که الگوها معتبر هستند، این مؤلفه‌ها شکل داده‌ها را توصیف می‌کنند. سپس برنامه ما مقادیر را با الگوها مقایسه می‌کند تا مشخص شود آیا داده‌ها شکل درستی دارند تا یک قطعه خاص از کد اجرا شود یا خیر. -To use a pattern, we compare it to some value. If the pattern matches the -value, we use the value parts in our code. Recall the `match` expressions in -Chapter 6 that used patterns, such as the coin-sorting machine example. If the -value fits the shape of the pattern, we can use the named pieces. If it -doesn’t, the code associated with the pattern won’t run. +برای استفاده از یک الگو، آن را با یک مقدار مقایسه می‌کنیم. اگر الگو با مقدار مطابقت داشته باشد، از قطعات مقدار در کد خود استفاده می‌کنیم. به یاد آورید که در فصل 6 از عبارات `match` که از الگوها استفاده می‌کردند، مانند مثال ماشین مرتب‌سازی سکه، بحث کردیم. اگر مقدار با شکل الگو تطابق داشته باشد، می‌توانیم از قسمت‌های نام‌گذاری‌شده استفاده کنیم. اگر مطابقت نداشته باشد، کد مرتبط با آن الگو اجرا نخواهد شد. + +این فصل یک مرجع جامع در مورد هر چیزی است که به الگوها مربوط می‌شود. ما مکان‌های معتبری که می‌توان از الگوها استفاده کرد، تفاوت بین الگوهای قابل‌رد (refutable) و غیرقابل‌رد (irrefutable)، و انواع مختلف نحوی الگو که ممکن است ببینید را پوشش خواهیم داد. تا پایان فصل، خواهید دانست که چگونه از الگوها برای بیان بسیاری از مفاهیم به روشی واضح استفاده کنید. -This chapter is a reference on all things related to patterns. We’ll cover the -valid places to use patterns, the difference between refutable and irrefutable -patterns, and the different kinds of pattern syntax that you might see. By the -end of the chapter, you’ll know how to use patterns to express many concepts in -a clear way. diff --git a/src/ch19-01-all-the-places-for-patterns.md b/src/ch19-01-all-the-places-for-patterns.md index b5704707fa..6627aa9460 100644 --- a/src/ch19-01-all-the-places-for-patterns.md +++ b/src/ch19-01-all-the-places-for-patterns.md @@ -1,29 +1,25 @@ ## All the Places Patterns Can Be Used -Patterns pop up in a number of places in Rust, and you’ve been using them a lot -without realizing it! This section discusses all the places where patterns are -valid. +الگوها در بسیاری از جاها در راست ظاهر می‌شوند، و شما از آن‌ها زیاد استفاده کرده‌اید بدون اینکه متوجه شوید! این بخش تمام جاهایی که الگوها معتبر هستند را بررسی می‌کند. ### `match` Arms -As discussed in Chapter 6, we use patterns in the arms of `match` expressions. -Formally, `match` expressions are defined as the keyword `match`, a value to -match on, and one or more match arms that consist of a pattern and an -expression to run if the value matches that arm’s pattern, like this: +همان‌طور که در فصل 6 بحث شد، ما از الگوها در بازوهای (arms) عبارات `match` استفاده می‌کنیم. به‌طور رسمی، عبارات `match` به‌صورت کلمه کلیدی `match`، یک مقدار برای مطابقت، و یک یا چند بازوی match که از یک الگو و یک عبارت برای اجرا در صورت مطابقت مقدار با الگوی آن بازو تشکیل شده‌اند، تعریف می‌شوند، مانند این: <!-- Manually formatted rather than using Markdown intentionally: Markdown does not support italicizing code in the body of a block like this! --> -<pre><code>match <em>VALUE</em> { +```rust +match <em>VALUE</em> { <em>PATTERN</em> => <em>EXPRESSION</em>, <em>PATTERN</em> => <em>EXPRESSION</em>, <em>PATTERN</em> => <em>EXPRESSION</em>, -}</code></pre> +} +``` -For example, here’s the `match` expression from Listing 6-5 that matches on an -`Option<i32>` value in the variable `x`: +برای مثال، در این‌جا عبارت `match` از لیستینگ 6-5 را داریم که روی یک مقدار از نوع `Option<i32>` در متغیر `x` تطبیق انجام می‌دهد: ```rust,ignore match x { @@ -32,57 +28,31 @@ match x { } ``` -The patterns in this `match` expression are the `None` and `Some(i)` on the -left of each arrow. +الگوها در این عبارت `match` شامل `None` و `Some(i)` هستند که در سمت چپ هر پیکان قرار دارند. -One requirement for `match` expressions is that they need to be _exhaustive_ in -the sense that all possibilities for the value in the `match` expression must -be accounted for. One way to ensure you’ve covered every possibility is to have -a catch-all pattern for the last arm: for example, a variable name matching any -value can never fail and thus covers every remaining case. +یکی از نیازمندی‌های عبارات `match` این است که باید به‌صورت _کامل_ باشند، به این معنا که تمام حالات ممکن برای مقدار در عبارت `match` باید پوشش داده شوند. یکی از راه‌های اطمینان از اینکه همه حالات را پوشش داده‌اید این است که یک الگوی عمومی (catchall) برای بازوی آخر داشته باشید: برای مثال، یک نام متغیر که هر مقداری را مطابقت می‌دهد هرگز شکست نمی‌خورد و بنابراین تمام موارد باقی‌مانده را پوشش می‌دهد. -The particular pattern `_` will match anything, but it never binds to a -variable, so it’s often used in the last match arm. The `_` pattern can be -useful when you want to ignore any value not specified, for example. We’ll cover -the `_` pattern in more detail in [“Ignoring Values in a -Pattern”][ignoring-values-in-a-pattern]<!-- ignore --> later in this chapter. +الگوی خاص `_` هر چیزی را مطابقت می‌دهد، اما هرگز به یک متغیر متصل نمی‌شود، بنابراین اغلب در بازوی آخر match استفاده می‌شود. الگوی `_` می‌تواند زمانی مفید باشد که بخواهید هر مقداری که مشخص نشده است را نادیده بگیرید، برای مثال. ما الگوی `_` را در بخش [“Ignoring Values in a Pattern”][ignoring-values-in-a-pattern]<!-- ignore --> بعداً در این فصل به‌طور مفصل بررسی خواهیم کرد. ### let Statements -Prior to this chapter, we had only explicitly discussed using patterns with -`match` and `if let`, but in fact, we’ve used patterns in other places as well, -including in `let` statements. For example, consider this straightforward -variable assignment with `let`: +پیش از این فصل، تنها به‌طور صریح از الگوها در `match` و `if let` استفاده کرده بودیم، اما در واقع در بخش‌های دیگری نیز از الگوها استفاده کرده‌ایم، از جمله در دستورات `let`. برای مثال، این انتساب ساده متغیر با استفاده از `let` را در نظر بگیرید: ```rust let x = 5; ``` -Every time you’ve used a `let` statement like this you’ve been using patterns, -although you might not have realized it! More formally, a `let` statement looks -like this: - -<!-- - Manually formatted rather than using Markdown intentionally: Markdown does not - support italicizing code in the body of a block like this! ---> +هر بار که از یک دستور `let` مانند این استفاده کرده‌اید، در حال استفاده از یک الگو بوده‌اید، حتی اگر متوجه آن نشده باشید! به‌طور رسمی‌تر، یک دستور `let` به این شکل است: <pre> <code>let <em>PATTERN</em> = <em>EXPRESSION</em>;</code> </pre> -In statements like `let x = 5;` with a variable name in the PATTERN slot, the -variable name is just a particularly simple form of a pattern. Rust compares -the expression against the pattern and assigns any names it finds. So, in the -`let x = 5;` example, `x` is a pattern that means “bind what matches here to -the variable `x`.” Because the name `x` is the whole pattern, this pattern -effectively means “bind everything to the variable `x`, whatever the value is.” - -To see the pattern-matching aspect of `let` more clearly, consider Listing -19-1, which uses a pattern with `let` to destructure a tuple. +در دستوراتی مانند `let x = 5;` که یک نام متغیر در جایگاه PATTERN قرار دارد، آن نام متغیر در واقع شکل ساده‌ای از یک الگو است. Rust عبارت را با الگو مقایسه می‌کند و هر نامی را که پیدا کند اختصاص می‌دهد. بنابراین، در مثال `let x = 5;`، `x` الگویی است که به این معناست: «هر چیزی که این‌جا تطابق دارد را به متغیر `x` متصل کن.» از آن‌جا که نام `x` کل الگو را تشکیل می‌دهد، این الگو عملاً به این معناست: «هر چیزی که باشد، آن را به متغیر `x` متصل کن.» +برای دیدن جنبه‌ی تطبیق الگو در `let` به‌طور واضح‌تر، به کد موجود در لیست ۱۹-۱ نگاه کنید که از یک الگو با `let` برای تجزیه‌ی یک tuple استفاده می‌کند. -<Listing number="19-1" caption="Using a pattern to destructure a tuple and create three variables at once"> +<Listing number="19-1" caption="استفاده از یک الگو برای تجزیه‌ی یک tuple و ساختن سه متغیر به‌صورت هم‌زمان"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-01/src/main.rs:here}} @@ -90,18 +60,11 @@ To see the pattern-matching aspect of `let` more clearly, consider Listing </Listing> -Here, we match a tuple against a pattern. Rust compares the value `(1, 2, 3)` -to the pattern `(x, y, z)` and sees that the value matches the pattern, in that -it sees that the number of elements is the same in both, so Rust binds `1` to -`x`, `2` to `y`, and `3` to `z`. You can think of this tuple pattern as nesting -three individual variable patterns inside it. +در این‌جا، ما یک tuple را با یک الگو تطبیق می‌دهیم. Rust مقدار `(1, 2, 3)` را با الگوی `(x, y, z)` مقایسه می‌کند و می‌بیند که این مقدار با الگو تطابق دارد، از این جهت که تعداد عناصر در هر دو یکی است. بنابراین Rust مقدار `1` را به `x`، مقدار `2` را به `y`، و مقدار `3` را به `z` اختصاص می‌دهد. می‌توانید این الگوی tuple را به‌عنوان تو در تویی از سه الگوی متغیر مجزا در نظر بگیرید. -If the number of elements in the pattern doesn’t match the number of elements -in the tuple, the overall type won’t match and we’ll get a compiler error. For -example, Listing 19-2 shows an attempt to destructure a tuple with three -elements into two variables, which won’t work. +اگر تعداد عناصر در الگو با تعداد عناصر در tuple تطابق نداشته باشد، نوع کلی تطابق نخواهد داشت و با خطای کامپایل مواجه خواهیم شد. برای مثال، لیست ۱۹-۲ تلاشی برای تجزیه‌ی یک tuple با سه عنصر به دو متغیر را نشان می‌دهد، که کار نخواهد کرد. -<Listing number="19-2" caption="Incorrectly constructing a pattern whose variables don’t match the number of elements in the tuple"> +<Listing number="19-2" caption="ساخت نادرست الگویی که تعداد متغیرهای آن با تعداد عناصر در tuple هم‌خوانی ندارد"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-02/src/main.rs:here}} @@ -109,38 +72,23 @@ elements into two variables, which won’t work. </Listing> -Attempting to compile this code results in this type error: +تلاش برای کامپایل این کد منجر به این خطای نوع می‌شود: ```console {{#include ../listings/ch19-patterns-and-matching/listing-19-02/output.txt}} ``` -To fix the error, we could ignore one or more of the values in the tuple using -`_` or `..`, as you’ll see in the [“Ignoring Values in a -Pattern”][ignoring-values-in-a-pattern]<!-- ignore --> section. If the problem -is that we have too many variables in the pattern, the solution is to make the -types match by removing variables so the number of variables equals the number -of elements in the tuple. +برای رفع این خطا، می‌توانیم یک یا چند مقدار را با استفاده از `_` یا `..` نادیده بگیریم، همان‌طور که در بخش \[«نادیده گرفتن مقادیر در یک الگو»]\[ignoring-values-in-a-pattern]<!-- ignore --> خواهید دید. اگر مشکل این باشد که متغیرهای زیادی در الگو داریم، راه‌حل این است که برخی متغیرها را حذف کنیم تا تعداد متغیرها با تعداد عناصر در tuple برابر شود. -### Conditional if let Expressions +### عبارات شرطی if let -In Chapter 6, we discussed how to use `if let` expressions mainly as a shorter -way to write the equivalent of a `match` that only matches one case. -Optionally, `if let` can have a corresponding `else` containing code to run if -the pattern in the `if let` doesn’t match. +در فصل ۶، در مورد نحوه‌ی استفاده از عبارات `if let` صحبت کردیم، عمدتاً به‌عنوان راهی کوتاه‌تر برای نوشتن معادل `match` که تنها یک حالت را تطبیق می‌دهد. به‌صورت اختیاری، `if let` می‌تواند یک بخش `else` نیز داشته باشد که در صورت عدم تطابق الگو، کدی را اجرا کند. -Listing 19-3 shows that it’s also possible to mix and match `if let`, `else -if`, and `else if let` expressions. Doing so gives us more flexibility than a -`match` expression in which we can express only one value to compare with the -patterns. Also, Rust doesn’t require that the conditions in a series of `if -let`, `else if`, and `else if let` arms relate to each other. +لیست ۱۹-۳ نشان می‌دهد که همچنین می‌توانیم `if let`، `else if`، و `else if let` را ترکیب کنیم. این کار انعطاف‌پذیری بیشتری نسبت به یک عبارت `match` به ما می‌دهد، که در آن تنها می‌توانیم یک مقدار را با الگوها مقایسه کنیم. همچنین، Rust الزام نمی‌کند که شرایط در زنجیره‌ی `if let`، `else if`، و `else if let` به یکدیگر مرتبط باشند. -The code in Listing 19-3 determines what color to make your background based on -a series of checks for several conditions. For this example, we’ve created -variables with hardcoded values that a real program might receive from user -input. +کد موجود در لیست ۱۹-۳ تعیین می‌کند که پس‌زمینه‌ی شما بر اساس مجموعه‌ای از بررسی‌ها چه رنگی باشد. برای این مثال، متغیرهایی با مقادیر hardcoded ایجاد کرده‌ایم که یک برنامه واقعی ممکن است از ورودی کاربر دریافت کند. -<Listing number="19-3" file-name="src/main.rs" caption="Mixing `if let`, `else if`, `else if let`, and `else`"> +<Listing number="19-3" file-name="src/main.rs" caption="ترکیب `if let`، `else if`، `else if let` و `else`"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-03/src/main.rs}} @@ -148,38 +96,19 @@ input. </Listing> -If the user specifies a favorite color, that color is used as the background. -If no favorite color is specified and today is Tuesday, the background color is -green. Otherwise, if the user specifies their age as a string and we can parse -it as a number successfully, the color is either purple or orange depending on -the value of the number. If none of these conditions apply, the background -color is blue. - -This conditional structure lets us support complex requirements. With the -hardcoded values we have here, this example will print `Using purple as the -background color`. - -You can see that `if let` can also introduce new variables that shadow existing -variables in the same way that `match` arms can: the line `if let Ok(age) = age` -introduces a new `age` variable that contains the value inside the `Ok` variant, -shadowing the existing `age` variable. This means we need to place the `if age > -30` condition within that block: we can’t combine these two conditions into `if -let Ok(age) = age && age > 30`. The new `age` we want to compare to 30 isn’t -valid until the new scope starts with the curly bracket. - -The downside of using `if let` expressions is that the compiler doesn’t check -for exhaustiveness, whereas with `match` expressions it does. If we omitted the -last `else` block and therefore missed handling some cases, the compiler would -not alert us to the possible logic bug. +اگر کاربر یک رنگ مورد علاقه مشخص کند، از آن رنگ به‌عنوان پس‌زمینه استفاده می‌شود. اگر هیچ رنگ مورد علاقه‌ای مشخص نشده باشد و امروز سه‌شنبه باشد، رنگ پس‌زمینه سبز است. در غیر این صورت، اگر کاربر سن خود را به‌عنوان یک رشته مشخص کند و بتوانیم آن را با موفقیت به یک عدد تبدیل کنیم، رنگ یا بنفش یا نارنجی است، بسته به مقدار عدد. اگر هیچ‌کدام از این شرایط صدق نکند، رنگ پس‌زمینه آبی خواهد بود. + +این ساختار شرطی به ما امکان پشتیبانی از نیازهای پیچیده را می‌دهد. با مقادیر سخت‌کدشده‌ای که در اینجا داریم، این مثال پیام `Using purple as the background color` را چاپ خواهد کرد. + +می‌توانید ببینید که `if let` نیز می‌تواند متغیرهای جدیدی را معرفی کند که متغیرهای موجود را به همان روشی که بازوهای `match` انجام می‌دهند، پوشش می‌دهند: خط `if let Ok(age) = age` یک متغیر جدید به نام `age` معرفی می‌کند که حاوی مقدار داخل حالت `Ok` است و متغیر موجود `age` را پوشش می‌دهد. این بدان معناست که باید شرط `if age > 30` را در داخل آن بلوک قرار دهیم: نمی‌توانیم این دو شرط را به‌صورت `if let Ok(age) = age && age > 30` ترکیب کنیم. متغیر جدید `age` که می‌خواهیم با 30 مقایسه کنیم تا شروع محدوده جدید با آکولاد معتبر نیست. + +نقطه ضعف استفاده از عبارات `if let` این است که کامپایلر بررسی نمی‌کند که آیا همه حالات پوشش داده شده‌اند یا خیر، در حالی که با عبارات `match` این کار را انجام می‌دهد. اگر بلوک آخر `else` را حذف کنیم و بنابراین برخی موارد را پوشش ندهیم، کامپایلر به ما در مورد باگ احتمالی منطقی هشدار نمی‌دهد. ### `while let` Conditional Loops -Similar in construction to `if let`, the `while let` conditional loop allows a -`while` loop to run for as long as a pattern continues to match. In Listing -19-4 we show a `while let` loop that waits on messages sent between threads, -but in this case checking a `Result` instead of an `Option`. +ساختار `while let` مشابه `if let` است و به ما این امکان را می‌دهد که یک حلقه‌ی `while` تا زمانی که یک الگو با موفقیت تطبیق یابد، اجرا شود. در لیست ۱۹-۴، یک حلقه‌ی `while let` را نشان می‌دهیم که منتظر دریافت پیام‌هایی است که بین نخ‌ها (threads) ارسال می‌شوند، اما در این مورد به جای بررسی یک مقدار از نوع `Option`، یک مقدار از نوع `Result` را بررسی می‌کنیم. -<Listing number="19-4" caption="Using a `while let` loop to print values for as long as `rx.recv()` returns `Ok`"> +<Listing number="19-4" caption="استفاده از یک حلقه‌ی `while let` برای چاپ مقادیر تا زمانی که `rx.recv()` مقدار `Ok` برگرداند"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-04/src/main.rs:here}} @@ -187,15 +116,9 @@ but in this case checking a `Result` instead of an `Option`. </Listing> -This example prints `1`, `2`, and then `3`. The `recv` method takes the first -message out of the receiver side of the channel and returns an `Ok(value)`. When -we first saw `recv` back in Chapter 16, we unwrapped the error directly, or -interacted with it as an iterator using a `for` loop. As Listing 19-4 shows, -though, we can also use while let, because the `recv` method returns an `Ok` -each time a message arrives, as long as the sender exists, and then produces an -`Err `once the sender side disconnects. +این مثال مقادیر `1`، `2`، و سپس `3` را چاپ می‌کند. متد `recv` اولین پیام را از سمت گیرنده‌ی کانال دریافت کرده و یک `Ok(value)` برمی‌گرداند. زمانی که در فصل ۱۶ با `recv` آشنا شدیم، یا مستقیماً خطا را `unwrap` می‌کردیم، یا از آن به‌عنوان یک پیمایشگر (iterator) در یک حلقه‌ی `for` استفاده می‌کردیم. اما همان‌طور که در لیست ۱۹-۴ نشان داده شده است، می‌توانیم از `while let` نیز استفاده کنیم، زیرا متد `recv` هر بار که پیامی دریافت شود، یک `Ok` برمی‌گرداند، تا زمانی که فرستنده هنوز وجود داشته باشد، و زمانی که سمت فرستنده قطع شود، یک `Err` تولید می‌کند. -### `for` Loops +### حلقه‌های `for` In a `for` loop, the value that directly follows the keyword `for` is a pattern. For example, in `for x in y`, the `x` is the pattern. Listing 19-5 @@ -211,26 +134,19 @@ apart, a tuple as part of the `for` loop. </Listing> -The code in Listing 19-5 will print the following: - +کدی که در لیست ۱۹-۵ آمده است، خروجی زیر را چاپ خواهد کرد: ```console {{#include ../listings/ch19-patterns-and-matching/listing-19-05/output.txt}} ``` -We adapt an iterator using the `enumerate` method so it produces a value and -the index for that value, placed into a tuple. The first value produced is the -tuple `(0, 'a')`. When this value is matched to the pattern `(index, value)`, -`index` will be `0` and `value` will be `'a'`, printing the first line of the -output. +ما با استفاده از متد `enumerate` یک پیمایشگر (iterator) را تطبیق می‌دهیم تا همراه با هر مقدار، اندیس آن مقدار را نیز تولید کند؛ این دو مقدار در قالب یک tuple قرار می‌گیرند. اولین مقداری که تولید می‌شود tupleای به شکل `(0, 'a')` است. هنگامی که این مقدار با الگوی `(index, value)` تطبیق داده می‌شود، `index` برابر با `0` و `value` برابر با `'a'` خواهد بود، و خط اول از خروجی چاپ می‌شود. ### Function Parameters -Function parameters can also be patterns. The code in Listing 19-6, which -declares a function named `foo` that takes one parameter named `x` of type -`i32`, should by now look familiar. +پارامترهای تابع نیز می‌توانند الگو باشند. کد در فهرست 19-6، که تابعی به نام `foo` را تعریف می‌کند که یک پارامتر به نام `x` از نوع `i32` می‌گیرد، باید تا الان آشنا به نظر برسد. -<Listing number="19-6" caption="A function signature uses patterns in the parameters"> +<Listing number="19-6" caption="یک امضای تابع از الگوها در پارامترها استفاده می‌کند"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-06/src/main.rs:here}} @@ -238,11 +154,9 @@ declares a function named `foo` that takes one parameter named `x` of type </Listing> -The `x` part is a pattern! As we did with `let`, we could match a tuple in a -function’s arguments to the pattern. Listing 19-7 splits the values in a tuple -as we pass it to a function. +قسمت `x` یک الگو است! همان‌طور که با `let` انجام دادیم، می‌توانیم یک tuple را در آرگومان‌های یک تابع با الگو مطابقت دهیم. فهرست 19-7 مقادیر یک tuple را هنگام ارسال به یک تابع تجزیه می‌کند. -<Listing number="19-7" file-name="src/main.rs" caption="A function with parameters that destructure a tuple"> +<Listing number="19-7" file-name="src/main.rs" caption="یک تابع با پارامترهایی که یک tuple را تخریب می‌کنند"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-07/src/main.rs}} @@ -250,16 +164,10 @@ as we pass it to a function. </Listing> -This code prints `Current location: (3, 5)`. The values `&(3, 5)` match the -pattern `&(x, y)`, so `x` is the value `3` and `y` is the value `5`. +این کد پیام `Current location: (3, 5)` را چاپ می‌کند. مقادیر `&(3, 5)` با الگوی `&(x, y)` مطابقت دارند، بنابراین `x` مقدار `3` و `y` مقدار `5` است. -We can also use patterns in closure parameter lists in the same way as in -function parameter lists because closures are similar to functions, as -discussed in Chapter 13. +ما همچنین می‌توانیم از الگوها در لیست پارامترهای closureها به همان روشی که در لیست پارامترهای تابع استفاده می‌کنیم، استفاده کنیم، زیرا closureها شبیه به توابع هستند، همان‌طور که در فصل 13 بحث شد. -At this point, you’ve seen several ways to use patterns, but patterns don’t -work the same in every place we can use them. In some places, the patterns must -be irrefutable; in other circumstances, they can be refutable. We’ll discuss -these two concepts next. +تا اینجا، چندین روش برای استفاده از الگوها را دیده‌اید، اما الگوها در هر جایی که از آن‌ها استفاده کنیم به یک شکل کار نمی‌کنند. در برخی مکان‌ها، الگوها باید غیرقابل‌رد (irrefutable) باشند؛ در شرایط دیگر، می‌توانند قابل‌رد (refutable) باشند. در بخش بعدی این دو مفهوم را بررسی خواهیم کرد. [ignoring-values-in-a-pattern]: ch19-03-pattern-syntax.html#ignoring-values-in-a-pattern diff --git a/src/ch19-02-refutability.md b/src/ch19-02-refutability.md index c886efad33..6346fdf1ee 100644 --- a/src/ch19-02-refutability.md +++ b/src/ch19-02-refutability.md @@ -1,33 +1,14 @@ ## Refutability: Whether a Pattern Might Fail to Match -Patterns come in two forms: refutable and irrefutable. Patterns that will match -for any possible value passed are _irrefutable_. An example would be `x` in the -statement `let x = 5;` because `x` matches anything and therefore cannot fail -to match. Patterns that can fail to match for some possible value are -_refutable_. An example would be `Some(x)` in the expression `if let Some(x) = -a_value` because if the value in the `a_value` variable is `None` rather than -`Some`, the `Some(x)` pattern will not match. - -Function parameters, `let` statements, and `for` loops can only accept -irrefutable patterns because the program cannot do anything meaningful when -values don’t match. The `if let` and `while let` expressions and the -`let...else` statement accept refutable and irrefutable patterns, but the -compiler warns against irrefutable patterns because, by definition, they’re -intended to handle possible failure: the functionality of a conditional is in -its ability to perform differently depending on success or failure. - -In general, you shouldn’t have to worry about the distinction between refutable -and irrefutable patterns; however, you do need to be familiar with the concept -of refutability so you can respond when you see it in an error message. In -those cases, you’ll need to change either the pattern or the construct you’re -using the pattern with, depending on the intended behavior of the code. - -Let’s look at an example of what happens when we try to use a refutable pattern -where Rust requires an irrefutable pattern and vice versa. Listing 19-8 shows a -`let` statement, but for the pattern, we’ve specified `Some(x)`, a refutable -pattern. As you might expect, this code will not compile. - -<Listing number="19-8" caption="Attempting to use a refutable pattern with `let`"> +الگوها به دو شکل هستند: قابل‌رد (refutable) و غیرقابل‌رد (irrefutable). الگوهایی که برای هر مقدار ممکن مطابقت دارند _غیرقابل‌رد_ هستند. به‌عنوان مثال، `x` در عبارت `let x = 5;`، زیرا `x` با هر چیزی مطابقت دارد و بنابراین نمی‌تواند از تطابق باز بماند. الگوهایی که ممکن است برای برخی مقادیر ممکن مطابقت نداشته باشند _قابل‌رد_ هستند. به‌عنوان مثال، `Some(x)` در عبارت `if let Some(x) = a_value`، زیرا اگر مقدار در متغیر `a_value` `None` باشد به‌جای `Some`، الگوی `Some(x)` مطابقت نخواهد داشت. + +پارامترهای تابع، عبارات `let`، و حلقه‌های `for` فقط می‌توانند الگوهای غیرقابل‌رد بپذیرند، زیرا برنامه نمی‌تواند کاری معنادار انجام دهد وقتی مقادیر مطابقت ندارند. عبارات `if let` و `while let` و عبارت `let`-`else` الگوهای قابل‌رد و غیرقابل‌رد را می‌پذیرند، اما کامپایلر درباره الگوهای غیرقابل‌رد هشدار می‌دهد زیرا به‌طور تعریف‌شده برای مدیریت شکست احتمالی طراحی شده‌اند: عملکرد شرطی در توانایی آن است که بسته به موفقیت یا شکست به‌طور متفاوت عمل کند. + +به‌طور کلی، نباید نیازی به نگرانی در مورد تمایز بین الگوهای قابل‌رد و غیرقابل‌رد داشته باشید؛ با این حال، باید با مفهوم قابل‌رد بودن آشنا باشید تا بتوانید زمانی که آن را در یک پیام خطا می‌بینید، واکنش نشان دهید. در این موارد، باید یا الگو را تغییر دهید یا ساختاری که الگو را با آن استفاده می‌کنید، بسته به رفتار موردنظر کد تغییر دهید. + +بیایید به مثالی نگاه کنیم که وقتی سعی می‌کنیم از یک الگوی قابل‌رد جایی که راست نیاز به یک الگوی غیرقابل‌رد دارد استفاده کنیم، و برعکس، چه اتفاقی می‌افتد. فهرست 19-8 یک عبارت `let` را نشان می‌دهد، اما برای الگو ما `Some(x)`، یک الگوی قابل‌رد مشخص کرده‌ایم. همان‌طور که ممکن است انتظار داشته باشید، این کد کامپایل نخواهد شد. + +<Listing number="19-8" caption="تلاش برای استفاده از یک الگوی قابل‌رد با `let`"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-08/src/main.rs:here}} @@ -35,26 +16,17 @@ pattern. As you might expect, this code will not compile. </Listing> -If `some_option_value` were a `None` value, it would fail to match the pattern -`Some(x)`, meaning the pattern is refutable. However, the `let` statement can -only accept an irrefutable pattern because there is nothing valid the code can -do with a `None` value. At compile time, Rust will complain that we’ve tried to -use a refutable pattern where an irrefutable pattern is required: +اگر مقدار `some_option_value` `None` باشد، مطابقت با الگوی `Some(x)` شکست خواهد خورد، به این معنا که الگو قابل‌رد است. با این حال، عبارت `let` فقط می‌تواند یک الگوی غیرقابل‌رد بپذیرد زیرا چیزی معتبر وجود ندارد که کد بتواند با مقدار `None` انجام دهد. در زمان کامپایل، راست شکایت می‌کند که ما سعی کرده‌ایم از یک الگوی قابل‌رد جایی که یک الگوی غیرقابل‌رد نیاز است استفاده کنیم: ```console {{#include ../listings/ch19-patterns-and-matching/listing-19-08/output.txt}} ``` -Because we didn’t cover (and couldn’t cover!) every valid value with the -pattern `Some(x)`, Rust rightfully produces a compiler error. +زیرا ما هر مقدار معتبری را با الگوی `Some(x)` پوشش ندادیم (و نمی‌توانستیم پوشش دهیم!)، راست به‌درستی یک خطای کامپایلر تولید می‌کند. -If we have a refutable pattern where an irrefutable pattern is needed, we can -fix it by changing the code that uses the pattern: instead of using `let`, we -can use `let else`. Then, if the pattern doesn’t match, the code will just skip -the code in the curly brackets, giving it a way to continue validly. Listing -19-9 shows how to fix the code in Listing 19-8. +اگر یک الگوی قابل‌رد داشته باشیم جایی که یک الگوی غیرقابل‌رد نیاز است، می‌توانیم با تغییر کدی که از الگو استفاده می‌کند آن را رفع کنیم: به‌جای استفاده از `let`، می‌توانیم از `if let` استفاده کنیم. سپس اگر الگو مطابقت نداشته باشد، کد به‌سادگی از اجرای کد داخل آکولادها صرف‌نظر می‌کند و راهی برای ادامه معتبر فراهم می‌کند. فهرست 19-9 نشان می‌دهد که چگونه کد در فهرست 19-8 را رفع کنیم. -<Listing number="19-9" caption="Using `let...else` and a block with refutable patterns instead of `let`"> +<Listing number="19-9" caption="استفاده از `if let` و یک بلوک با الگوهای قابل‌رد به‌جای `let`"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-09/src/main.rs:here}} @@ -62,12 +34,9 @@ the code in the curly brackets, giving it a way to continue validly. Listing </Listing> -We’ve given the code an out! This code is perfectly valid, although it means we -cannot use an irrefutable pattern without receiving a warning. If we give -`let...else` a pattern that will always match, such as `x`, as shown in Listing -19-10, the compiler will give a warning. +ما به کد یک مسیر خروجی دادیم! این کد اکنون کاملاً معتبر است. با این حال، اگر به `if let` یک الگوی غیرقابل‌رد (الگویی که همیشه مطابقت دارد)، مانند `x`، بدهیم، همان‌طور که در فهرست 19-10 نشان داده شده است، کامپایلر یک هشدار خواهد داد. -<Listing number="19-10" caption="Attempting to use an irrefutable pattern with `let...else`"> +<Listing number="19-10" caption="تلاش برای استفاده از یک الگوی غیرقابل‌رد با `if let`"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-10/src/main.rs:here}} @@ -75,19 +44,12 @@ cannot use an irrefutable pattern without receiving a warning. If we give </Listing> -Rust complains that it doesn’t make sense to use `let...else` with an -irrefutable pattern: +راست شکایت می‌کند که استفاده از `if let` با یک الگوی غیرقابل‌رد منطقی نیست: ```console {{#include ../listings/ch19-patterns-and-matching/listing-19-10/output.txt}} ``` -For this reason, match arms must use refutable patterns, except for the last -arm, which should match any remaining values with an irrefutable pattern. Rust -allows us to use an irrefutable pattern in a `match` with only one arm, but -this syntax isn’t particularly useful and could be replaced with a simpler -`let` statement. +به این دلیل، بازوهای match باید از الگوهای قابل‌رد استفاده کنند، به‌جز بازوی آخر که باید با یک الگوی غیرقابل‌رد هر مقدار باقی‌مانده را مطابقت دهد. راست به ما اجازه می‌دهد از یک الگوی غیرقابل‌رد در یک `match` با تنها یک بازو استفاده کنیم، اما این نحو به‌ویژه مفید نیست و می‌تواند با یک عبارت ساده‌تر `let` جایگزین شود. -Now that you know where to use patterns and the difference between refutable -and irrefutable patterns, let’s cover all the syntax we can use to create -patterns. +اکنون که می‌دانید کجا می‌توان از الگوها استفاده کرد و تفاوت بین الگوهای قابل‌رد و غیرقابل‌رد چیست، بیایید تمام نحوهایی که می‌توانیم برای ایجاد الگوها استفاده کنیم را بررسی کنیم. \ No newline at end of file diff --git a/src/ch19-03-pattern-syntax.md b/src/ch19-03-pattern-syntax.md index 57a3bb7431..030f21bd47 100644 --- a/src/ch19-03-pattern-syntax.md +++ b/src/ch19-03-pattern-syntax.md @@ -5,31 +5,19 @@ why and when you might want to use each one. ### Matching Literals -As you saw in Chapter 6, you can match patterns against literals directly. The -following code gives some examples: +همان‌طور که در فصل 6 دیدید، می‌توانید الگوها را مستقیماً با مقادیر ثابت (literals) تطبیق دهید. کد زیر برخی از مثال‌ها را نشان می‌دهد: ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/no-listing-01-literals/src/main.rs:here}} ``` -This code prints `one` because the value in `x` is `1`. This syntax is useful -when you want your code to take an action if it gets a particular concrete -value. +این کد `one` را چاپ می‌کند زیرا مقدار در `x` برابر با 1 است. این نحو زمانی مفید است که بخواهید کد شما در صورت دریافت یک مقدار مشخص خاص اقدامی انجام دهد. ### Matching Named Variables -Named variables are irrefutable patterns that match any value, and we’ve used -them many times in this book. However, there is a complication when you use -named variables in `match`, `if let`, or `while let` expressions. Because each -of these kinds of expressions starts a new scope, variables declared as part of -a pattern inside these expressions will shadow those with the same name outside -the constructs, as is the case with all variables. In Listing 19-11, we declare -a variable named `x` with the value `Some(5)` and a variable `y` with the value -`10`. We then create a `match` expression on the value `x`. Look at the -patterns in the match arms and `println!` at the end, and try to figure out -what the code will print before running this code or reading further. +متغیرهای نام‌گذاری‌شده الگوهای غیرقابل‌رد هستند که با هر مقداری مطابقت دارند، و ما بارها در این کتاب از آن‌ها استفاده کرده‌ایم. با این حال، زمانی که از متغیرهای نام‌گذاری‌شده در عبارات `match`، `if let`، یا `while let` استفاده می‌کنید، یک پیچیدگی وجود دارد. زیرا هر یک از این نوع عبارات یک دامنه جدید را شروع می‌کنند، متغیرهایی که به‌عنوان بخشی از یک الگو در داخل عبارت تعریف می‌شوند، متغیرهایی با همان نام در خارج را پوشش می‌دهند، همان‌طور که برای همه متغیرها صدق می‌کند. در فهرست 19-11، یک متغیر به نام `x` با مقدار `Some(5)` و یک متغیر `y` با مقدار `10` تعریف می‌کنیم. سپس یک عبارت `match` روی مقدار `x` ایجاد می‌کنیم. به الگوها در بازوهای match و دستور `println!` در انتها نگاه کنید و سعی کنید قبل از اجرای این کد یا خواندن بیشتر، حدس بزنید که کد چه چیزی را چاپ خواهد کرد. -<Listing number="19-11" file-name="src/main.rs" caption="A `match` expression with an arm that introduces a new variable which shadows an existing variable `y`"> +<Listing number="19-11" file-name="src/main.rs" caption="یک عبارت `match` با بازویی که یک متغیر جدید معرفی می‌کند که متغیر موجود `y` را پوشش می‌دهد"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-11/src/main.rs:here}} @@ -37,89 +25,55 @@ what the code will print before running this code or reading further. </Listing> -Let’s walk through what happens when the `match` expression runs. The pattern -in the first match arm doesn’t match the defined value of `x`, so the code -continues. +بیایید بررسی کنیم که وقتی عبارت `match` اجرا می‌شود چه اتفاقی می‌افتد. الگوی موجود در بازوی اول match با مقدار تعریف‌شده `x` مطابقت ندارد، بنابراین کد ادامه می‌یابد. -The pattern in the second match arm introduces a new variable named `y` that -will match any value inside a `Some` value. Because we’re in a new scope inside -the `match` expression, this is a new `y` variable, not the `y` we declared at -the beginning with the value `10`. This new `y` binding will match any value -inside a `Some`, which is what we have in `x`. Therefore, this new `y` binds to -the inner value of the `Some` in `x`. That value is `5`, so the expression for -that arm executes and prints `Matched, y = 5`. +الگوی موجود در بازوی دوم match یک متغیر جدید به نام `y` معرفی می‌کند که با هر مقداری درون یک `Some` مطابقت خواهد داشت. از آنجا که ما در یک دامنه جدید داخل عبارت `match` هستیم، این یک متغیر جدید `y` است، نه متغیری که در ابتدا با مقدار 10 تعریف کردیم. این binding جدید `y` با هر مقداری درون یک `Some` مطابقت دارد، که همان چیزی است که ما در `x` داریم. بنابراین، این `y` جدید به مقدار داخلی `Some` در `x` متصل می‌شود. آن مقدار `5` است، بنابراین عبارت برای آن بازو اجرا می‌شود و `Matched, y = 5` را چاپ می‌کند. -If `x` had been a `None` value instead of `Some(5)`, the patterns in the first -two arms wouldn’t have matched, so the value would have matched to the -underscore. We didn’t introduce the `x` variable in the pattern of the -underscore arm, so the `x` in the expression is still the outer `x` that hasn’t -been shadowed. In this hypothetical case, the `match` would print `Default case, -x = None`. +اگر `x` به جای `Some(5)` یک مقدار `None` بود، الگوهای موجود در دو بازوی اول مطابقت نداشتند، بنابراین مقدار به علامت زیرخط (`_`) مطابقت داده می‌شد. ما متغیر `x` را در الگوی بازوی زیرخط معرفی نکردیم، بنابراین `x` در عبارت همچنان همان `x` خارجی است که پوشش داده نشده است. در این حالت فرضی، عبارت `match` پیام `Default case, x = None` را چاپ می‌کرد. -When the `match` expression is done, its scope ends, and so does the scope of -the inner `y`. The last `println!` produces `at the end: x = Some(5), y = 10`. +وقتی عبارت `match` تمام می‌شود، دامنه آن نیز پایان می‌یابد، و همین‌طور دامنه `y` داخلی. دستور `println!` آخر پیام `at the end: x = Some(5), y = 10` را تولید می‌کند. -To create a `match` expression that compares the values of the outer `x` and -`y`, rather than introducing a new variable that shadows the existing `y` -variable, we would need to use a match guard conditional instead. We’ll talk -about match guards later in [“Extra Conditionals with Match -Guards”](#extra-conditionals-with-match-guards)<!-- ignore -->. +برای ایجاد یک عبارت `match` که مقادیر `x` و `y` بیرونی را مقایسه کند، به‌جای معرفی یک متغیر جدید که متغیر `y` موجود را سایه‌بان (shadow) کند، باید از یک شرط نگهبان `match` (match guard) استفاده کنیم. درباره‌ی نگهبان‌های `match` بعداً در بخش [«شرط‌های اضافی با Match Guards»](#extra-conditionals-with-match-guards)<!-- ignore --> صحبت خواهیم کرد. -### Multiple Patterns - -In `match` expressions, you can match multiple patterns using the `|` syntax, -which is the pattern _or_ operator. For example, in the following code we match -the value of `x` against the match arms, the first of which has an _or_ option, -meaning if the value of `x` matches either of the values in that arm, that -arm’s code will run: +### چند الگو (Multiple Patterns) +در عبارات `match` می‌توانید چندین الگو را با استفاده از سینتکس `|` که عملگر _یا_ (or) برای الگوها است، با هم مقایسه کنید. برای مثال، در کد زیر مقدار `x` را با شاخه‌های `match` مقایسه می‌کنیم که اولین شاخه شامل گزینه‌ی _یا_ است، یعنی اگر مقدار `x` با هر یک از مقادیر موجود در آن شاخه مطابقت داشته باشد، کد آن شاخه اجرا خواهد شد: ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/no-listing-02-multiple-patterns/src/main.rs:here}} ``` -This code prints `one or two`. +این کد `one or two` را چاپ می‌کند. ### Matching Ranges of Values with `..=` -The `..=` syntax allows us to match to an inclusive range of values. In the -following code, when a pattern matches any of the values within the given -range, that arm will execute: +نحو `..=` به ما اجازه می‌دهد یک بازه شامل مقادیر را مطابقت دهیم. در کد زیر، وقتی یک الگو با هر کدام از مقادیر در بازه داده‌شده مطابقت داشته باشد، آن بازو اجرا خواهد شد: ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/no-listing-03-ranges/src/main.rs:here}} ``` -If `x` is `1`, `2`, `3`, `4`, or `5`, the first arm will match. This syntax is -more convenient for multiple match values than using the `|` operator to -express the same idea; if we were to use `|`, we would have to specify `1 | 2 | -3 | 4 | 5`. Specifying a range is much shorter, especially if we want to match, -say, any number between 1 and 1,000! +اگر مقدار `x` برابر با `1`، `2`، `3`، `4` یا `5` باشد، شاخه‌ی اول در `match` تطابق خواهد داشت. این نحو برای مقادیر متعدد در `match` بسیار راحت‌تر از استفاده‌ی مکرر از عملگر `|` است؛ زیرا اگر از `|` استفاده کنیم، باید به صورت `1 | 2 | 3 | 4 | 5` آن‌ها را مشخص کنیم. استفاده از بازه (range) بسیار کوتاه‌تر است، به‌ویژه اگر بخواهیم مثلاً هر عددی بین ۱ تا ۱۰۰۰ را تطبیق دهیم! -The compiler checks that the range isn’t empty at compile time, and because the -only types for which Rust can tell if a range is empty or not are `char` and -numeric values, ranges are only allowed with numeric or `char` values. +کامپایلر بررسی می‌کند که بازه در زمان کامپایل خالی نیست، و چون تنها نوع‌هایی که راست می‌تواند تشخیص دهد که آیا یک بازه خالی است یا نه `char` و مقادیر عددی هستند، بازه‌ها فقط برای مقادیر عددی یا `char` مجاز هستند. -Here is an example using ranges of `char` values: +در اینجا یک مثال با استفاده از بازه‌هایی از مقادیر `char` آمده است: ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/no-listing-04-ranges-of-char/src/main.rs:here}} ``` -Rust can tell that `'c'` is within the first pattern’s range and prints `early -ASCII letter`. +راست می‌تواند تشخیص دهد که `'c'` در بازه الگوی اول است و پیام `early ASCII letter` را چاپ می‌کند. ### Destructuring to Break Apart Values -We can also use patterns to destructure structs, enums, and tuples to use -different parts of these values. Let’s walk through each value. +ما همچنین می‌توانیم از الگوها برای تخریب (destructure) ساختارها (structs)، enums، و tuple‌ها استفاده کنیم تا از بخش‌های مختلف این مقادیر استفاده کنیم. بیایید به هر نوع مقدار نگاهی بیندازیم. #### Destructuring Structs -Listing 19-12 shows a `Point` struct with two fields, `x` and `y`, that we can -break apart using a pattern with a `let` statement. +فهرست 19-12 یک struct به نام `Point` را با دو فیلد، `x` و `y` نشان می‌دهد که می‌توانیم با استفاده از یک الگو در یک عبارت `let` آن را تخریب کنیم. -<Listing number="19-12" file-name="src/main.rs" caption="Destructuring a struct’s fields into separate variables"> +<Listing number="19-12" file-name="src/main.rs" caption="تخریب فیلدهای یک struct به متغیرهای جداگانه"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-12/src/main.rs}} @@ -127,19 +81,9 @@ break apart using a pattern with a `let` statement. </Listing> -This code creates the variables `a` and `b` that match the values of the `x` -and `y` fields of the `p` struct. This example shows that the names of the -variables in the pattern don’t have to match the field names of the struct. -However, it’s common to match the variable names to the field names to make it -easier to remember which variables came from which fields. Because of this -common usage, and because writing `let Point { x: x, y: y } = p;` contains a -lot of duplication, Rust has a shorthand for patterns that match struct fields: -you only need to list the name of the struct field, and the variables created -from the pattern will have the same names. Listing 19-13 behaves in the same -way as the code in Listing 19-12, but the variables created in the `let` -pattern are `x` and `y` instead of `a` and `b`. +این کد متغیرهای `a` و `b` را ایجاد می‌کند که با مقادیر فیلدهای `x` و `y` از struct `p` مطابقت دارند. این مثال نشان می‌دهد که نام متغیرها در الگو نیازی به مطابقت با نام فیلدهای struct ندارند. با این حال، معمولاً نام متغیرها با نام فیلدها مطابقت داده می‌شوند تا یادآوری اینکه کدام متغیرها از کدام فیلدها آمده‌اند آسان‌تر شود. به‌دلیل این استفاده معمول و به‌دلیل اینکه نوشتن `let Point { x: x, y: y } = p;` تکرار زیادی دارد، راست یک نحو کوتاه برای الگوهایی که فیلدهای struct را مطابقت می‌دهند فراهم می‌کند: فقط کافی است نام فیلد struct را لیست کنید و متغیرهایی که از الگو ایجاد می‌شوند همان نام‌ها را خواهند داشت. فهرست 19-13 به همان روشی که کد در فهرست 19-12 عمل می‌کند، اما متغیرهای ایجادشده در الگوی `let` به‌جای `a` و `b`، `x` و `y` هستند. -<Listing number="19-13" file-name="src/main.rs" caption="Destructuring struct fields using struct field shorthand"> +<Listing number="19-13" file-name="src/main.rs" caption="تخریب فیلدهای struct با استفاده از نحو کوتاه فیلد struct"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-13/src/main.rs}} @@ -147,20 +91,13 @@ pattern are `x` and `y` instead of `a` and `b`. </Listing> -This code creates the variables `x` and `y` that match the `x` and `y` fields -of the `p` variable. The outcome is that the variables `x` and `y` contain the -values from the `p` struct. +این کد متغیرهای `x` و `y` را ایجاد می‌کند که با فیلدهای `x` و `y` از متغیر `p` مطابقت دارند. نتیجه این است که متغیرهای `x` و `y` مقادیر از ساختار `p` را شامل می‌شوند. -We can also destructure with literal values as part of the struct pattern -rather than creating variables for all the fields. Doing so allows us to test -some of the fields for particular values while creating variables to -destructure the other fields. +ما همچنین می‌توانیم با مقادیر ثابت (literals) به‌عنوان بخشی از الگوی struct تخریب کنیم، به‌جای ایجاد متغیرهایی برای همه فیلدها. انجام این کار به ما اجازه می‌دهد برخی از فیلدها را برای مقادیر خاصی تست کنیم، در حالی که متغیرهایی برای تخریب فیلدهای دیگر ایجاد می‌کنیم. -In Listing 19-14, we have a `match` expression that separates `Point` values -into three cases: points that lie directly on the `x` axis (which is true when -`y = 0`), on the `y` axis (`x = 0`), or on neither axis. +در فهرست 19-14، یک عبارت `match` داریم که مقادیر `Point` را به سه حالت تقسیم می‌کند: نقاطی که مستقیماً روی محور `x` قرار دارند (که در صورتی درست است که `y = 0`)، روی محور `y` (`x = 0`)، یا هیچ‌کدام. -<Listing number="19-14" file-name="src/main.rs" caption="Destructuring and matching literal values in one pattern"> +<Listing number="19-14" file-name="src/main.rs" caption="تخریب و تطبیق مقادیر ثابت در یک الگو"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-14/src/main.rs:here}} @@ -168,31 +105,19 @@ into three cases: points that lie directly on the `x` axis (which is true when </Listing> -The first arm will match any point that lies on the `x` axis by specifying that -the `y` field matches if its value matches the literal `0`. The pattern still -creates an `x` variable that we can use in the code for this arm. +بازوی اول هر نقطه‌ای که روی محور `x` قرار دارد را با مشخص کردن اینکه فیلد `y` در صورتی مطابقت دارد که مقدار آن با مقدار ثابت `0` مطابقت داشته باشد، تطبیق می‌دهد. الگو همچنان یک متغیر `x` ایجاد می‌کند که می‌توانیم در کد این بازو از آن استفاده کنیم. -Similarly, the second arm matches any point on the `y` axis by specifying that -the `x` field matches if its value is `0` and creates a variable `y` for the -value of the `y` field. The third arm doesn’t specify any literals, so it -matches any other `Point` and creates variables for both the `x` and `y` fields. +به‌طور مشابه، بازوی دوم هر نقطه روی محور `y` را با مشخص کردن اینکه فیلد `x` در صورتی که مقدار آن `0` باشد مطابقت دارد و یک متغیر `y` برای مقدار فیلد `y` ایجاد می‌کند. بازوی سوم هیچ مقدار ثابتی را مشخص نمی‌کند، بنابراین هر `Point` دیگری را مطابقت می‌دهد و متغیرهایی برای هر دو فیلد `x` و `y` ایجاد می‌کند. -In this example, the value `p` matches the second arm by virtue of `x` -containing a `0`, so this code will print `On the y axis at 7`. +در این مثال، مقدار `p` به لطف `x` که مقدار `0` دارد، با بازوی دوم مطابقت دارد، بنابراین این کد پیام `On the y axis at 7` را چاپ می‌کند. -Remember that a `match` expression stops checking arms once it has found the -first matching pattern, so even though `Point { x: 0, y: 0}` is on the `x` axis -and the `y` axis, this code would only print `On the x axis at 0`. +به یاد داشته باشید که یک عبارت `match` پس از یافتن اولین الگوی مطابقت متوقف می‌شود، بنابراین حتی اگر `Point { x: 0, y: 0 }` روی محور `x` و محور `y` باشد، این کد فقط پیام `On the x axis at 0` را چاپ خواهد کرد. #### Destructuring Enums -We’ve destructured enums in this book (for example, Listing 6-5 in Chapter 6), -but haven’t yet explicitly discussed that the pattern to destructure an enum -corresponds to the way the data stored within the enum is defined. As an -example, in Listing 19-15 we use the `Message` enum from Listing 6-2 and write -a `match` with patterns that will destructure each inner value. +ما در این کتاب enums را تخریب کرده‌ایم (برای مثال، فهرست 6-5 در فصل 6)، اما هنوز به‌طور خاص بحث نکرده‌ایم که الگوی تخریب یک enum مطابق با نحوه تعریف داده‌های ذخیره‌شده درون enum است. به‌عنوان مثال، در فهرست 19-15 از enum `Message` از فهرست 6-2 استفاده می‌کنیم و یک `match` با الگوهایی می‌نویسیم که هر مقدار داخلی را تخریب می‌کنند. -<Listing number="19-15" file-name="src/main.rs" caption="Destructuring enum variants that hold different kinds of values"> +<Listing number="19-15" file-name="src/main.rs" caption="تخریب متغیرهای enum که مقادیر مختلفی دارند"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-15/src/main.rs}} @@ -200,33 +125,19 @@ a `match` with patterns that will destructure each inner value. </Listing> -This code will print `Change color to red 0, green 160, and blue 255`. Try -changing the value of `msg` to see the code from the other arms run. +این کد پیام `Change the color to red 0, green 160, and blue 255` را چاپ می‌کند. مقدار `msg` را تغییر دهید تا کد از بازوهای دیگر اجرا شود. -For enum variants without any data, like `Message::Quit`, we can’t destructure -the value any further. We can only match on the literal `Message::Quit` value, -and no variables are in that pattern. +برای متغیرهای enum بدون هیچ داده‌ای، مانند `Message::Quit`، نمی‌توان مقدار را بیشتر تخریب کرد. فقط می‌توان روی مقدار ثابت `Message::Quit` مطابقت داد، و هیچ متغیری در آن الگو وجود ندارد. -For struct-like enum variants, such as `Message::Move`, we can use a pattern -similar to the pattern we specify to match structs. After the variant name, we -place curly brackets and then list the fields with variables so we break apart -the pieces to use in the code for this arm. Here we use the shorthand form as -we did in Listing 19-13. +برای متغیرهای enum شبیه به struct، مانند `Message::Move`، می‌توانیم از الگویی مشابه الگوی مشخص‌شده برای تطبیق structs استفاده کنیم. پس از نام متغیر، آکولاد باز می‌کنیم و سپس فیلدها را با متغیرها لیست می‌کنیم تا بخش‌ها را برای استفاده در کد این بازو تجزیه کنیم. در اینجا از فرم کوتاه همان‌طور که در فهرست 19-13 استفاده کردیم استفاده می‌کنیم. -For tuple-like enum variants, like `Message::Write` that holds a tuple with one -element and `Message::ChangeColor` that holds a tuple with three elements, the -pattern is similar to the pattern we specify to match tuples. The number of -variables in the pattern must match the number of elements in the variant we’re -matching. +برای متغیرهای enum شبیه به tuple، مانند `Message::Write` که یک tuple با یک عنصر دارد و `Message::ChangeColor` که یک tuple با سه عنصر دارد، الگو مشابه الگویی است که برای تطبیق tuple‌ها مشخص می‌کنیم. تعداد متغیرها در الگو باید با تعداد عناصر در متغیر که تطبیق می‌دهیم مطابقت داشته باشد. #### Destructuring Nested Structs and Enums -So far, our examples have all been matching structs or enums one level deep, -but matching can work on nested items too! For example, we can refactor the -code in Listing 19-15 to support RGB and HSV colors in the `ChangeColor` -message, as shown in Listing 19-16. +تاکنون، مثال‌های ما همه تطبیق ساختارها یا enums در یک سطح عمیق بوده‌اند، اما تطبیق می‌تواند روی آیتم‌های تو در تو نیز کار کند! برای مثال، می‌توانیم کد در فهرست 19-15 را بازسازی کنیم تا از رنگ‌های RGB و HSV در پیام `ChangeColor` پشتیبانی کند، همان‌طور که در فهرست 19-16 نشان داده شده است. -<Listing number="19-16" caption="Matching on nested enums"> +<Listing number="19-16" caption="تطبیق روی enums تو در تو"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-16/src/main.rs}} @@ -234,38 +145,23 @@ message, as shown in Listing 19-16. </Listing> -The pattern of the first arm in the `match` expression matches a -`Message::ChangeColor` enum variant that contains a `Color::Rgb` variant; then -the pattern binds to the three inner `i32` values. The pattern of the second -arm also matches a `Message::ChangeColor` enum variant, but the inner enum -matches `Color::Hsv` instead. We can specify these complex conditions in one -`match` expression, even though two enums are involved. +الگوی بازوی اول در عبارت `match` یک متغیر enum به نام `Message::ChangeColor` را تطبیق می‌دهد که شامل یک متغیر `Color::Rgb` است؛ سپس الگو به سه مقدار داخلی `i32` متصل می‌شود. الگوی بازوی دوم نیز یک متغیر enum به نام `Message::ChangeColor` را تطبیق می‌دهد، اما enum داخلی به جای آن `Color::Hsv` را مطابقت می‌دهد. ما می‌توانیم این شرایط پیچیده را در یک عبارت `match` مشخص کنیم، حتی اگر دو enum درگیر باشند. #### Destructuring Structs and Tuples -We can mix, match, and nest destructuring patterns in even more complex ways. -The following example shows a complicated destructure where we nest structs and -tuples inside a tuple and destructure all the primitive values out: +ما می‌توانیم الگوهای تخریب را به روش‌های پیچیده‌تر ترکیب، تطبیق و تو در تو کنیم. مثال زیر یک تخریب پیچیده را نشان می‌دهد که در آن ساختارها و tuple‌ها را داخل یک tuple تو در تو می‌کنیم و تمام مقادیر اولیه را تخریب می‌کنیم: ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/no-listing-05-destructuring-structs-and-tuples/src/main.rs:here}} ``` -This code lets us break complex types into their component parts so we can use -the values we’re interested in separately. +این کد به ما اجازه می‌دهد انواع پیچیده را به اجزای سازنده آن‌ها بشکنیم تا بتوانیم مقادیری که به آن‌ها علاقه داریم را جداگانه استفاده کنیم. -Destructuring with patterns is a convenient way to use pieces of values, such -as the value from each field in a struct, separately from each other. +تخریب با الگوها یک روش راحت برای استفاده از بخش‌هایی از مقادیر، مانند مقدار هر فیلد در یک ساختار، به‌صورت جداگانه است. ### Ignoring Values in a Pattern -You’ve seen that it’s sometimes useful to ignore values in a pattern, such as -in the last arm of a `match`, to get a catch-all that doesn’t actually do -anything but does account for all remaining possible values. There are a few -ways to ignore entire values or parts of values in a pattern: using the `_` -pattern (which you’ve seen), using the `_` pattern within another pattern, -using a name that starts with an underscore, or using `..` to ignore remaining -parts of a value. Let’s explore how and why to use each of these patterns. +گاهی اوقات مفید است که مقادیر را در یک الگو نادیده بگیرید، مانند بازوی آخر یک `match`، برای دریافت یک catchall که هیچ کاری انجام نمی‌دهد اما تمام مقادیر باقی‌مانده ممکن را در نظر می‌گیرد. چندین روش برای نادیده گرفتن مقادیر کامل یا بخش‌هایی از مقادیر در یک الگو وجود دارد: استفاده از الگوی `_` (که دیده‌اید)، استفاده از الگوی `_` درون یک الگوی دیگر، استفاده از نامی که با یک زیرخط شروع می‌شود، یا استفاده از `..` برای نادیده گرفتن بخش‌های باقی‌مانده یک مقدار. بیایید بررسی کنیم چگونه و چرا از هر یک از این الگوها استفاده کنیم. <!-- Old link, do not remove --> @@ -273,12 +169,9 @@ parts of a value. Let’s explore how and why to use each of these patterns. #### An Entire Value with `_` -We’ve used the underscore as a wildcard pattern that will match any value but -not bind to the value. This is especially useful as the last arm in a `match` -expression, but we can also use it in any pattern, including function -parameters, as shown in Listing 19-17. +ما از زیرخط به‌عنوان یک الگوی wildcard استفاده کرده‌ایم که با هر مقداری مطابقت دارد اما به مقدار متصل نمی‌شود. این به‌ویژه به‌عنوان بازوی آخر در یک عبارت `match` مفید است، اما ما همچنین می‌توانیم آن را در هر الگویی استفاده کنیم، از جمله پارامترهای تابع، همان‌طور که در فهرست 19-17 نشان داده شده است. -<Listing number="19-17" file-name="src/main.rs" caption="Using `_` in a function signature"> +<Listing number="19-17" file-name="src/main.rs" caption="استفاده از `_` در یک امضای تابع"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-17/src/main.rs}} @@ -286,29 +179,17 @@ parameters, as shown in Listing 19-17. </Listing> -This code will completely ignore the value `3` passed as the first argument, -and will print `This code only uses the y parameter: 4`. +این کد مقدار `3` را که به‌عنوان آرگومان اول ارسال شده است، کاملاً نادیده می‌گیرد و پیام `This code only uses the y parameter: 4` را چاپ می‌کند. -In most cases when you no longer need a particular function parameter, you -would change the signature so it doesn’t include the unused parameter. Ignoring -a function parameter can be especially useful in cases when, for example, -you’re implementing a trait when you need a certain type signature but the -function body in your implementation doesn’t need one of the parameters. You -then avoid getting a compiler warning about unused function parameters, as you -would if you used a name instead. +در بیشتر موارد، زمانی که دیگر نیازی به یک پارامتر تابع خاص ندارید، امضای تابع را تغییر می‌دهید تا آن پارامتر استفاده‌نشده را شامل نشود. نادیده گرفتن یک پارامتر تابع می‌تواند به‌ویژه در مواردی مفید باشد که، برای مثال، شما در حال پیاده‌سازی یک trait هستید و به یک امضای خاص نیاز دارید، اما بدنه تابع در پیاده‌سازی شما نیازی به یکی از پارامترها ندارد. در این صورت، از دریافت هشدار کامپایلر درباره پارامترهای استفاده‌نشده جلوگیری می‌کنید، همان‌طور که اگر به جای آن از یک نام استفاده می‌کردید، هشدار دریافت می‌کردید. <a id="ignoring-parts-of-a-value-with-a-nested-_"></a> #### Parts of a Value with a Nested `_` -We can also use `_` inside another pattern to ignore just part of a value, for -example, when we want to test for only part of a value but have no use for the -other parts in the corresponding code we want to run. Listing 19-18 shows code -responsible for managing a setting’s value. The business requirements are that -the user should not be allowed to overwrite an existing customization of a -setting but can unset the setting and give it a value if it is currently unset. +ما همچنین می‌توانیم از `_` در داخل یک الگوی دیگر استفاده کنیم تا فقط بخشی از یک مقدار را نادیده بگیریم. برای مثال، وقتی می‌خواهیم فقط بخشی از یک مقدار را تست کنیم اما نیازی به استفاده از بخش‌های دیگر در کدی که می‌خواهیم اجرا کنیم نداریم. فهرست 19-18 کدی را نشان می‌دهد که مسئول مدیریت مقدار یک تنظیم است. نیازمندی‌های تجاری این است که کاربر نباید اجازه داشته باشد یک سفارشی‌سازی موجود برای یک تنظیم را بازنویسی کند، اما می‌تواند تنظیم را لغو کند و به آن یک مقدار بدهد اگر در حال حاضر لغو شده باشد. -<Listing number="19-18" caption="Using an underscore within patterns that match `Some` variants when we don’t need to use the value inside the `Some`"> +<Listing number="19-18" caption="استفاده از یک زیرخط در داخل الگوهایی که با متغیرهای `Some` مطابقت دارند وقتی نیازی به استفاده از مقدار داخل `Some` نداریم"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-18/src/main.rs:here}} @@ -316,22 +197,13 @@ setting but can unset the setting and give it a value if it is currently unset. </Listing> -This code will print `Can't overwrite an existing customized value` and then -`setting is Some(5)`. In the first match arm, we don’t need to match on or use -the values inside either `Some` variant, but we do need to test for the case -when `setting_value` and `new_setting_value` are the `Some` variant. In that -case, we print the reason for not changing `setting_value`, and it doesn’t get -changed. +این کد پیام `Can't overwrite an existing customized value` را چاپ می‌کند و سپس `setting is Some(5)` را چاپ می‌کند. در بازوی اول match، نیازی به مطابقت یا استفاده از مقادیر داخل هر یک از متغیرهای `Some` نداریم، اما باید حالت‌هایی را که در آن‌ها `setting_value` و `new_setting_value` در حالت `Some` هستند، تست کنیم. در این صورت، دلیل تغییر ندادن `setting_value` را چاپ می‌کنیم و این مقدار تغییر نمی‌کند. -In all other cases (if either `setting_value` or `new_setting_value` is `None`) -expressed by the `_` pattern in the second arm, we want to allow -`new_setting_value` to become `setting_value`. +در تمام موارد دیگر (اگر `setting_value` یا `new_setting_value` مقدار `None` داشته باشند) که توسط الگوی `_` در بازوی دوم بیان شده است، می‌خواهیم اجازه دهیم `new_setting_value` به `setting_value` تبدیل شود. -We can also use underscores in multiple places within one pattern to ignore -particular values. Listing 19-19 shows an example of ignoring the second and -fourth values in a tuple of five items. +ما همچنین می‌توانیم از زیرخط‌ها در مکان‌های مختلف در یک الگو برای نادیده گرفتن مقادیر خاص استفاده کنیم. فهرست 19-19 مثالی از نادیده گرفتن مقادیر دوم و چهارم در یک tuple پنج آیتمی را نشان می‌دهد. -<Listing number="19-19" caption="Ignoring multiple parts of a tuple"> +<Listing number="19-19" caption="نادیده گرفتن بخش‌های مختلف یک tuple"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-19/src/main.rs:here}} @@ -339,8 +211,7 @@ fourth values in a tuple of five items. </Listing> -This code will print `Some numbers: 2, 8, 32`, and the values `4` and `16` will -be ignored. +این کد پیام `Some numbers: 2, 8, 32` را چاپ می‌کند و مقادیر 4 و 16 نادیده گرفته می‌شوند. <!-- Old link, do not remove --> @@ -348,15 +219,9 @@ be ignored. #### An Unused Variable by Starting Its Name with `_` -If you create a variable but don’t use it anywhere, Rust will usually issue a -warning because an unused variable could be a bug. However, sometimes it’s -useful to be able to create a variable you won’t use yet, such as when you’re -prototyping or just starting a project. In this situation, you can tell Rust -not to warn you about the unused variable by starting the name of the variable -with an underscore. In Listing 19-20, we create two unused variables, but when -we compile this code, we should only get a warning about one of them. +اگر یک متغیر ایجاد کنید اما از آن در هیچ جایی استفاده نکنید، راست معمولاً یک هشدار صادر می‌کند زیرا یک متغیر استفاده‌نشده ممکن است یک باگ باشد. با این حال، گاهی اوقات مفید است که بتوانید متغیری ایجاد کنید که هنوز از آن استفاده نمی‌کنید، مانند زمانی که در حال نمونه‌سازی یا تازه شروع یک پروژه هستید. در این وضعیت، می‌توانید به راست بگویید که درباره متغیر استفاده‌نشده هشدار ندهد، با شروع نام متغیر با یک زیرخط. در فهرست 19-20، دو متغیر استفاده‌نشده ایجاد می‌کنیم، اما وقتی این کد را کامپایل می‌کنیم، باید فقط یک هشدار درباره یکی از آن‌ها دریافت کنیم. -<Listing number="19-20" file-name="src/main.rs" caption="Starting a variable name with an underscore to avoid getting unused variable warnings"> +<Listing number="19-20" file-name="src/main.rs" caption="شروع نام متغیر با یک زیرخط برای جلوگیری از هشدارهای متغیر استفاده‌نشده"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-20/src/main.rs}} @@ -364,15 +229,11 @@ we compile this code, we should only get a warning about one of them. </Listing> -Here, we get a warning about not using the variable `y`, but we don’t get a -warning about not using `_x`. +اینجا درباره استفاده نکردن از متغیر `y` یک هشدار دریافت می‌کنیم، اما درباره استفاده نکردن از `_x` هشدار نمی‌گیریم. -Note that there is a subtle difference between using only `_` and using a name -that starts with an underscore. The syntax `_x` still binds the value to the -variable, whereas `_` doesn’t bind at all. To show a case where this -distinction matters, Listing 19-21 will provide us with an error. +توجه داشته باشید که تفاوت ظریفی بین استفاده از فقط `_` و استفاده از نامی که با یک زیرخط شروع می‌شود وجود دارد. نحو `_x` همچنان مقدار را به متغیر متصل می‌کند، در حالی که `_` اصلاً متصل نمی‌شود. برای نشان دادن موردی که این تفاوت اهمیت دارد، فهرست 19-21 به ما یک خطا ارائه می‌دهد. -<Listing number="19-21" caption="An unused variable starting with an underscore still binds the value, which might take ownership of the value."> +<Listing number="19-21" caption="یک متغیر استفاده‌نشده که با یک زیرخط شروع می‌شود همچنان مقدار را متصل می‌کند، که ممکن است مالکیت مقدار را بگیرد"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-21/src/main.rs:here}} @@ -380,12 +241,9 @@ distinction matters, Listing 19-21 will provide us with an error. </Listing> -We’ll receive an error because the `s` value will still be moved into `_s`, -which prevents us from using `s` again. However, using the underscore by itself -doesn’t ever bind to the value. Listing 19-22 will compile without any errors -because `s` doesn’t get moved into `_`. +ما یک خطا دریافت خواهیم کرد زیرا مقدار `s` همچنان به `_s` منتقل می‌شود، که مانع از استفاده دوباره از `s` می‌شود. با این حال، استفاده از زیرخط به‌تنهایی هرگز به مقدار متصل نمی‌شود. فهرست 19-22 بدون هیچ خطایی کامپایل خواهد شد زیرا `s` به `_` منتقل نمی‌شود. -<Listing number="19-22" caption="Using an underscore does not bind the value."> +<Listing number="19-22" caption="استفاده از یک زیرخط مقدار را متصل نمی‌کند"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-22/src/main.rs:here}} @@ -393,21 +251,15 @@ because `s` doesn’t get moved into `_`. </Listing> -This code works just fine because we never bind `s` to anything; it isn’t moved. +این کد به‌خوبی کار می‌کند زیرا ما هرگز `s` را به چیزی متصل نمی‌کنیم؛ بنابراین انتقال داده نمی‌شود. <a id="ignoring-remaining-parts-of-a-value-with-"></a> #### Remaining Parts of a Value with `..` -With values that have many parts, we can use the `..` syntax to use specific -parts and ignore the rest, avoiding the need to list underscores for each -ignored value. The `..` pattern ignores any parts of a value that we haven’t -explicitly matched in the rest of the pattern. In Listing 19-23, we have a -`Point` struct that holds a coordinate in three-dimensional space. In the -`match` expression, we want to operate only on the `x` coordinate and ignore -the values in the `y` and `z` fields. +برای مقادیری که بخش‌های زیادی دارند، می‌توانیم از نحو `..` برای استفاده از بخش‌های خاص و نادیده گرفتن باقی بخش‌ها استفاده کنیم، و نیازی به لیست کردن زیرخط‌ها برای هر مقدار نادیده گرفته‌شده نخواهیم داشت. الگوی `..` هر بخشی از یک مقدار را که به‌طور صریح در بقیه الگو مطابقت داده نشده نادیده می‌گیرد. در فهرست 19-23، یک struct به نام `Point` داریم که یک مختصات در فضای سه‌بعدی نگه می‌دارد. در عبارت `match`، می‌خواهیم فقط روی مختصات `x` عمل کنیم و مقادیر موجود در فیلدهای `y` و `z` را نادیده بگیریم. -<Listing number="19-23" caption="Ignoring all fields of a `Point` except for `x` by using `..`"> +<Listing number="19-23" caption="نادیده گرفتن تمام فیلدهای یک `Point` به‌جز `x` با استفاده از `..`"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-23/src/main.rs:here}} @@ -415,15 +267,11 @@ the values in the `y` and `z` fields. </Listing> -We list the `x` value and then just include the `..` pattern. This is quicker -than having to list `y: _` and `z: _`, particularly when we’re working with -structs that have lots of fields in situations where only one or two fields are -relevant. +ما مقدار `x` را فهرست می‌کنیم و سپس فقط الگوی `..` را اضافه می‌کنیم. این سریع‌تر از این است که `y: _` و `z: _` را فهرست کنیم، به‌ویژه زمانی که با ساختارهایی کار می‌کنیم که فیلدهای زیادی دارند و فقط یکی یا دو فیلد مهم هستند. -The syntax `..` will expand to as many values as it needs to be. Listing 19-24 -shows how to use `..` with a tuple. +نحو `..` به هر تعداد مقداری که نیاز باشد گسترش می‌یابد. فهرست 19-24 نشان می‌دهد که چگونه از `..` با یک tuple استفاده کنیم. -<Listing number="19-24" file-name="src/main.rs" caption="Matching only the first and last values in a tuple and ignoring all other values"> +<Listing number="19-24" file-name="src/main.rs" caption="تطبیق فقط اولین و آخرین مقادیر در یک tuple و نادیده گرفتن تمام مقادیر دیگر"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-24/src/main.rs}} @@ -431,15 +279,11 @@ shows how to use `..` with a tuple. </Listing> -In this code, the first and last values are matched with `first` and `last`. -The `..` will match and ignore everything in the middle. +در این کد، مقدار اول و آخر با `first` و `last` مطابقت داده می‌شوند. الگوی `..` تمام مقادیر میانی را مطابقت داده و نادیده می‌گیرد. -However, using `..` must be unambiguous. If it is unclear which values are -intended for matching and which should be ignored, Rust will give us an error. -Listing 19-25 shows an example of using `..` ambiguously, so it will not -compile. +با این حال، استفاده از `..` باید بدون ابهام باشد. اگر مشخص نباشد کدام مقادیر برای تطبیق و کدام برای نادیده گرفتن در نظر گرفته شده‌اند، راست به ما خطا می‌دهد. فهرست 19-25 مثالی از استفاده از `..` به شکلی مبهم را نشان می‌دهد، بنابراین کامپایل نخواهد شد. -<Listing number="19-25" file-name="src/main.rs" caption="An attempt to use `..` in an ambiguous way"> +<Listing number="19-25" file-name="src/main.rs" caption="تلاشی برای استفاده از `..` به شکلی مبهم"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-25/src/main.rs}} @@ -447,33 +291,21 @@ compile. </Listing> -When we compile this example, we get this error: +وقتی این مثال را کامپایل می‌کنیم، این خطا را دریافت می‌کنیم: ```console {{#include ../listings/ch19-patterns-and-matching/listing-19-25/output.txt}} ``` -It’s impossible for Rust to determine how many values in the tuple to ignore -before matching a value with `second` and then how many further values to -ignore thereafter. This code could mean that we want to ignore `2`, bind -`second` to `4`, and then ignore `8`, `16`, and `32`; or that we want to ignore -`2` and `4`, bind `second` to `8`, and then ignore `16` and `32`; and so forth. -The variable name `second` doesn’t mean anything special to Rust, so we get a -compiler error because using `..` in two places like this is ambiguous. +برای راست امکان‌پذیر نیست که تعیین کند چند مقدار در tuple باید نادیده گرفته شود قبل از اینکه یک مقدار را با `second` تطبیق دهد و سپس چند مقدار دیگر را بعد از آن نادیده بگیرد. این کد می‌تواند به این معنا باشد که می‌خواهیم `2` را نادیده بگیریم، `second` را به `4` متصل کنیم، و سپس `8`، `16` و `32` را نادیده بگیریم؛ یا اینکه می‌خواهیم `2` و `4` را نادیده بگیریم، `second` را به `8` متصل کنیم، و سپس `16` و `32` را نادیده بگیریم؛ و غیره. نام متغیر `second` برای راست معنی خاصی ندارد، بنابراین به دلیل استفاده از `..` در دو مکان به این شکل مبهم، خطای کامپایل دریافت می‌کنیم. ### Extra Conditionals with Match Guards -A _match guard_ is an additional `if` condition, specified after the pattern in -a `match` arm, that must also match for that arm to be chosen. Match guards are -useful for expressing more complex ideas than a pattern alone allows. Note, -however, that they are only available in `match` expressions, not `if let` or -`while let` expressions. +یک _match guard_ یک شرط اضافی `if` است که پس از الگو در یک بازوی `match` مشخص می‌شود و باید برای انتخاب آن بازو نیز مطابقت داشته باشد. Match guardها برای بیان ایده‌های پیچیده‌تر از آنچه که یک الگو به‌تنهایی اجازه می‌دهد، مفید هستند. این قابلیت فقط در عبارات `match` در دسترس است، نه در عبارات `if let` یا `while let`. -The condition can use variables created in the pattern. Listing 19-26 shows a -`match` where the first arm has the pattern `Some(x)` and also has a match -guard of `if x % 2 == 0` (which will be `true` if the number is even). +شرط می‌تواند از متغیرهایی که در الگو ایجاد شده‌اند استفاده کند. فهرست 19-26 یک `match` را نشان می‌دهد که بازوی اول آن دارای الگوی `Some(x)` است و همچنین دارای یک match guard `if x % 2 == 0` است (که در صورتی که عدد زوج باشد، true خواهد بود). -<Listing number="19-26" caption="Adding a match guard to a pattern"> +<Listing number="19-26" caption="افزودن یک match guard به یک الگو"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-26/src/main.rs:here}} @@ -481,29 +313,15 @@ guard of `if x % 2 == 0` (which will be `true` if the number is even). </Listing> -This example will print `The number 4 is even`. When `num` is compared to the -pattern in the first arm, it matches because `Some(4)` matches `Some(x)`. Then -the match guard checks whether the remainder of dividing `x` by 2 is equal to -0, and because it is, the first arm is selected. +این مثال پیام `The number 4 is even` را چاپ می‌کند. وقتی `num` با الگوی بازوی اول مقایسه می‌شود، مطابقت دارد، زیرا `Some(4)` با `Some(x)` مطابقت دارد. سپس match guard بررسی می‌کند که آیا باقی‌مانده تقسیم `x` بر 2 برابر با 0 است یا نه، و چون این شرط برقرار است، بازوی اول انتخاب می‌شود. -If `num` had been `Some(5)` instead, the match guard in the first arm would -have been `false` because the remainder of 5 divided by 2 is 1, which is not -equal to 0. Rust would then go to the second arm, which would match because the -second arm doesn’t have a match guard and therefore matches any `Some` variant. +اگر مقدار `num` برابر با `Some(5)` بود، match guard در بازوی اول false می‌شد زیرا باقی‌مانده تقسیم 5 بر 2 برابر با 1 است که برابر با 0 نیست. راست سپس به بازوی دوم می‌رود که مطابقت دارد زیرا بازوی دوم match guard ندارد و بنابراین با هر متغیر `Some` مطابقت دارد. -There is no way to express the `if x % 2 == 0` condition within a pattern, so -the match guard gives us the ability to express this logic. The downside of -this additional expressiveness is that the compiler doesn’t try to check for -exhaustiveness when match guard expressions are involved. +هیچ راهی برای بیان شرط `if x % 2 == 0` در داخل یک الگو وجود ندارد، بنابراین match guard به ما امکان بیان این منطق را می‌دهد. نقطه ضعف این قابلیت اضافی این است که کامپایلر سعی نمی‌کند بررسی کند که آیا تمام موارد پوشش داده شده‌اند یا نه وقتی که match guardها درگیر هستند. -In Listing 19-11, we mentioned that we could use match guards to solve our -pattern-shadowing problem. Recall that we created a new variable inside the -pattern in the `match` expression instead of using the variable outside the -`match`. That new variable meant we couldn’t test against the value of the -outer variable. Listing 19-27 shows how we can use a match guard to fix this -problem. +در فهرست 19-11 اشاره کردیم که می‌توانیم از match guardها برای حل مشکل shadowing الگو استفاده کنیم. به یاد بیاورید که ما یک متغیر جدید در داخل الگو در عبارت `match` ایجاد کردیم به جای استفاده از متغیر بیرون از `match`. آن متغیر جدید به این معنا بود که نمی‌توانستیم مقدار متغیر بیرونی را تست کنیم. فهرست 19-27 نشان می‌دهد که چگونه می‌توانیم از یک match guard برای رفع این مشکل استفاده کنیم. -<Listing number="19-27" file-name="src/main.rs" caption="Using a match guard to test for equality with an outer variable"> +<Listing number="19-27" file-name="src/main.rs" caption="استفاده از یک match guard برای آزمایش برابری با یک متغیر بیرونی"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-27/src/main.rs}} @@ -511,26 +329,13 @@ problem. </Listing> -This code will now print `Default case, x = Some(5)`. The pattern in the second -match arm doesn’t introduce a new variable `y` that would shadow the outer `y`, -meaning we can use the outer `y` in the match guard. Instead of specifying the -pattern as `Some(y)`, which would have shadowed the outer `y`, we specify -`Some(n)`. This creates a new variable `n` that doesn’t shadow anything because -there is no `n` variable outside the `match`. +این کد اکنون پیام `Default case, x = Some(5)` را چاپ می‌کند. الگوی بازوی دوم match یک متغیر جدید `y` که متغیر بیرونی `y` را shadow کند معرفی نمی‌کند، به این معنا که می‌توانیم از متغیر بیرونی `y` در match guard استفاده کنیم. به جای مشخص کردن الگو به‌عنوان `Some(y)` که متغیر بیرونی `y` را shadow می‌کرد، ما `Some(n)` را مشخص می‌کنیم. این یک متغیر جدید `n` ایجاد می‌کند که هیچ چیزی را shadow نمی‌کند زیرا هیچ متغیر `n` در خارج از `match` وجود ندارد. -The match guard `if n == y` is not a pattern and therefore doesn’t introduce new -variables. This `y` _is_ the outer `y` rather than a new `y` shadowing it, and -we can look for a value that has the same value as the outer `y` by comparing -`n` to `y`. +Match guard `if n == y` یک الگو نیست و بنابراین متغیرهای جدیدی را معرفی نمی‌کند. این `y` _همان_ متغیر بیرونی `y` است و یک متغیر جدید که آن را shadow کند نیست، و می‌توانیم با مقایسه `n` با `y` به دنبال مقداری باشیم که با مقدار بیرونی `y` یکسان باشد. -You can also use the _or_ operator `|` in a match guard to specify multiple -patterns; the match guard condition will apply to all the patterns. Listing -19-28 shows the precedence when combining a pattern that uses `|` with a match -guard. The important part of this example is that the `if y` match guard -applies to `4`, `5`, _and_ `6`, even though it might look like `if y` only -applies to `6`. +همچنین می‌توانید از عملگر _یا_ `|` در یک match guard استفاده کنید تا چندین الگو مشخص کنید؛ شرط match guard برای تمام الگوها اعمال خواهد شد. فهرست 19-28 تقدم هنگام ترکیب یک الگو که از `|` استفاده می‌کند با یک match guard را نشان می‌دهد. بخش مهم این مثال این است که match guard `if y` برای `4`، `5`، _و_ `6` اعمال می‌شود، حتی اگر ممکن است به نظر برسد که `if y` فقط برای `6` اعمال می‌شود. -<Listing number="19-28" caption="Combining multiple patterns with a match guard"> +<Listing number="19-28" caption="ترکیب چندین الگو با یک match guard"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-28/src/main.rs:here}} @@ -538,39 +343,25 @@ applies to `6`. </Listing> -The match condition states that the arm only matches if the value of `x` is -equal to `4`, `5`, or `6` _and_ if `y` is `true`. When this code runs, the -pattern of the first arm matches because `x` is `4`, but the match guard `if y` -is `false`, so the first arm is not chosen. The code moves on to the second -arm, which does match, and this program prints `no`. The reason is that the -`if` condition applies to the whole pattern `4 | 5 | 6`, not just to the last -value `6`. In other words, the precedence of a match guard in relation to a -pattern behaves like this: +شرط مطابقت بیان می‌کند که بازو فقط زمانی مطابقت دارد که مقدار `x` برابر با `4`، `5`، یا `6` _و_ مقدار `y` برابر با `true` باشد. وقتی این کد اجرا می‌شود، الگوی بازوی اول مطابقت دارد زیرا `x` برابر با `4` است، اما match guard `if y` برابر با false است، بنابراین بازوی اول انتخاب نمی‌شود. کد به بازوی دوم می‌رود که مطابقت دارد، و این برنامه `no` را چاپ می‌کند. دلیل این است که شرط `if` برای کل الگوی `4 | 5 | 6` اعمال می‌شود، نه فقط برای مقدار آخر `6`. به عبارت دیگر، تقدم یک match guard نسبت به یک الگو به این شکل رفتار می‌کند: ```text (4 | 5 | 6) if y => ... ``` -rather than this: +و نه به این شکل: ```text 4 | 5 | (6 if y) => ... ``` -After running the code, the precedence behavior is evident: if the match guard -were applied only to the final value in the list of values specified using the -`|` operator, the arm would have matched and the program would have printed -`yes`. +بعد از اجرای کد، رفتار تقدم آشکار می‌شود: اگر match guard فقط برای مقدار نهایی در لیست مقادیر مشخص‌شده با استفاده از عملگر `|` اعمال می‌شد، بازو مطابقت می‌داشت و برنامه پیام `yes` را چاپ می‌کرد. ### `@` Bindings -The _at_ operator `@` lets us create a variable that holds a value at the same -time we’re testing that value for a pattern match. In Listing 19-29, we want to -test that a `Message::Hello` `id` field is within the range `3..=7`. We also -want to bind the value to the variable `id` so we can use it in the code -associated with the arm. +عملگر _at_ (`@`) به ما امکان می‌دهد یک متغیر ایجاد کنیم که یک مقدار را نگه می‌دارد و همزمان آن مقدار را برای تطبیق با الگو آزمایش می‌کند. در فهرست 19-29، ما می‌خواهیم بررسی کنیم که آیا فیلد `id` در `Message::Hello` در بازه `3..=7` قرار دارد یا نه. همچنین می‌خواهیم مقدار را به متغیر `id_variable` متصل کنیم تا بتوانیم در کد مرتبط با بازو از آن استفاده کنیم. می‌توانستیم این متغیر را `id` بنامیم، مشابه فیلد، اما برای این مثال از نام متفاوتی استفاده خواهیم کرد. -<Listing number="19-29" caption="Using `@` to bind to a value in a pattern while also testing it"> +<Listing number="19-29" caption="استفاده از `@` برای اتصال به یک مقدار در یک الگو و همزمان آزمایش آن"> ```rust {{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-29/src/main.rs:here}} @@ -578,33 +369,16 @@ associated with the arm. </Listing> -This example will print `Found an id in range: 5`. By specifying `id @` before -the range `3..=7`, we’re capturing whatever value matched the range in a -variable named `id` while also testing that the value matched the range pattern. +این مثال پیام `Found an id in range: 5` را چاپ می‌کند. با مشخص کردن `id_variable @` قبل از بازه `3..=7`، ما هر مقداری که با بازه مطابقت داشت را ذخیره می‌کنیم و همزمان بررسی می‌کنیم که آیا مقدار با الگوی بازه مطابقت دارد. -In the second arm, where we only have a range specified in the pattern, the code -associated with the arm doesn’t have a variable that contains the actual value -of the `id` field. The `id` field’s value could have been 10, 11, or 12, but -the code that goes with that pattern doesn’t know which it is. The pattern code -isn’t able to use the value from the `id` field, because we haven’t saved the -`id` value in a variable. +در بازوی دوم، جایی که فقط یک بازه در الگو مشخص شده است، کدی که با بازو مرتبط است متغیری ندارد که مقدار واقعی فیلد `id` را شامل شود. مقدار فیلد `id` می‌توانست 10، 11، یا 12 باشد، اما کدی که با آن الگو مرتبط است نمی‌داند مقدار چیست. کد بازو نمی‌تواند از مقدار فیلد `id` استفاده کند، زیرا ما مقدار `id` را در یک متغیر ذخیره نکرده‌ایم. -In the last arm, where we’ve specified a variable without a range, we do have -the value available to use in the arm’s code in a variable named `id`. The -reason is that we’ve used the struct field shorthand syntax. But we haven’t -applied any test to the value in the `id` field in this arm, as we did with the -first two arms: any value would match this pattern. +در بازوی آخر، جایی که یک متغیر بدون بازه مشخص کرده‌ایم، مقدار برای استفاده در کد بازو در متغیری به نام `id` در دسترس است. دلیل این است که ما از نحو کوتاه فیلدهای struct استفاده کرده‌ایم. اما در این بازو هیچ آزمایشی برای مقدار در فیلد `id` اعمال نکرده‌ایم، همان‌طور که در دو بازوی اول انجام دادیم: هر مقداری با این الگو مطابقت خواهد داشت. -Using `@` lets us test a value and save it in a variable within one pattern. +استفاده از `@` به ما امکان می‌دهد یک مقدار را آزمایش کنیم و همزمان آن را در یک متغیر ذخیره کنیم، همه در یک الگو. ## Summary -Rust’s patterns are very useful in distinguishing between different kinds of -data. When used in `match` expressions, Rust ensures your patterns cover every -possible value, or your program won’t compile. Patterns in `let` statements and -function parameters make those constructs more useful, enabling the -destructuring of values into smaller parts and assigning those parts to -variables. We can create simple or complex patterns to suit our needs. +الگوهای راست در تشخیص بین انواع مختلف داده بسیار مفید هستند. وقتی در عبارات `match` استفاده می‌شوند، راست اطمینان حاصل می‌کند که الگوهای شما تمام مقادیر ممکن را پوشش می‌دهند، وگرنه برنامه شما کامپایل نخواهد شد. الگوها در عبارات `let` و پارامترهای تابع این ساختارها را مفیدتر می‌کنند و تخریب مقادیر به بخش‌های کوچک‌تر را همزمان با تخصیص به متغیرها ممکن می‌سازند. ما می‌توانیم الگوهای ساده یا پیچیده‌ای ایجاد کنیم که نیازهای ما را برآورده کنند. -Next, for the penultimate chapter of the book, we’ll look at some advanced -aspects of a variety of Rust’s features. +در فصل ماقبل آخر این کتاب، به برخی از جنبه‌های پیشرفته از ویژگی‌های مختلف راست خواهیم پرداخت. diff --git a/src/ch20-00-advanced-features.md b/src/ch20-00-advanced-features.md index 66e558eef7..61114b570d 100644 --- a/src/ch20-00-advanced-features.md +++ b/src/ch20-00-advanced-features.md @@ -1,22 +1,19 @@ # Advanced Features -By now, you’ve learned the most commonly used parts of the Rust programming -language. Before we do one more project, in Chapter 21, we’ll look at a few -aspects of the language you might run into every once in a while, but may not -use every day. You can use this chapter as a reference for when you encounter -any unknowns. The features covered here are useful in very specific situations. -Although you might not reach for them often, we want to make sure you have a -grasp of all the features Rust has to offer. +تا اینجا، با پرکاربردترین بخش‌های زبان برنامه‌نویسی Rust آشنا شده‌اید. +قبل از این‌که به یک پروژه‌ی دیگر بپردازیم، در فصل ۲۱ به چند جنبه از زبان نگاه خواهیم کرد +که ممکن است گهگاه با آن‌ها مواجه شوید، اما شاید هر روز از آن‌ها استفاده نکنید. +می‌توانید این فصل را به عنوان یک مرجع زمانی استفاده کنید که با موارد ناشناخته‌ای روبه‌رو می‌شوید. +ویژگی‌های مطرح شده در این فصل در موقعیت‌های بسیار خاص کاربرد دارند. +اگرچه ممکن است به‌ندرت به سراغ آن‌ها بروید، +اما می‌خواهیم اطمینان حاصل کنیم که بر تمام قابلیت‌هایی که Rust ارائه می‌دهد تسلط دارید. -In this chapter, we’ll cover: +در این فصل، ما به موضوعات زیر خواهیم پرداخت: -- Unsafe Rust: how to opt out of some of Rust’s guarantees and take - responsibility for manually upholding those guarantees -- Advanced traits: associated types, default type parameters, fully qualified - syntax, supertraits, and the newtype pattern in relation to traits -- Advanced types: more about the newtype pattern, type aliases, the never type, - and dynamically sized types -- Advanced functions and closures: function pointers and returning closures -- Macros: ways to define code that defines more code at compile time +- Unsafe Rust: چگونه می‌توان از برخی از تضمین‌های راست چشم‌پوشی کرد و مسئولیت تضمین دستی این موارد را بر عهده گرفت. +- Advanced traits: نوع‌های مرتبط (associated types)، پارامترهای نوع پیش‌فرض، نحو کاملاً واجد شرایط، ابر traits (supertraits)، و الگوی newtype در رابطه با traits. +- Advanced types: بیشتر درباره الگوی newtype، نام مستعار نوع (type aliases)، نوع never، و نوع‌های با اندازه پویا. +- Advanced functions and closures: اشاره‌گر (Pointer)های تابع و بازگرداندن closures. +- Macros: روش‌هایی برای تعریف کدی که در زمان کامپایل کد بیشتری تعریف می‌کند. -It’s a panoply of Rust features with something for everyone! Let’s dive in! +این یک مجموعه گسترده از ویژگی‌های راست است که برای همه چیزی در آن وجود دارد! بیایید شروع کنیم! diff --git a/src/ch20-01-unsafe-rust.md b/src/ch20-01-unsafe-rust.md index dc56d7b3bf..981e31a18f 100644 --- a/src/ch20-01-unsafe-rust.md +++ b/src/ch20-01-unsafe-rust.md @@ -1,59 +1,39 @@ ## Unsafe Rust -All the code we’ve discussed so far has had Rust’s memory safety guarantees -enforced at compile time. However, Rust has a second language hidden inside it -that doesn’t enforce these memory safety guarantees: it’s called _unsafe Rust_ -and works just like regular Rust, but gives us extra superpowers. - -Unsafe Rust exists because, by nature, static analysis is conservative. When -the compiler tries to determine whether or not code upholds the guarantees, -it’s better for it to reject some valid programs than to accept some invalid -programs. Although the code _might_ be okay, if the Rust compiler doesn’t have -enough information to be confident, it will reject the code. In these cases, -you can use unsafe code to tell the compiler, “Trust me, I know what I’m -doing.” Be warned, however, that you use unsafe Rust at your own risk: if you -use unsafe code incorrectly, problems can occur due to memory unsafety, such as -null pointer dereferencing. - -Another reason Rust has an unsafe alter ego is that the underlying computer -hardware is inherently unsafe. If Rust didn’t let you do unsafe operations, you -couldn’t do certain tasks. Rust needs to allow you to do low-level systems -programming, such as directly interacting with the operating system or even -writing your own operating system. Working with low-level systems programming -is one of the goals of the language. Let’s explore what we can do with unsafe -Rust and how to do it. +تمام کدی که تا به حال بررسی کرده‌ایم دارای تضمین‌های ایمنی حافظه راست بوده است که در زمان کامپایل اعمال می‌شوند. با این حال، راست دارای یک زبان دوم مخفی درون خود است که این تضمین‌های ایمنی حافظه را اعمال نمی‌کند: این زبان _Unsafe Rust_ نامیده می‌شود و درست مانند راست معمولی کار می‌کند، اما به ما قدرت‌های فوق‌العاده‌ای می‌دهد. + +وجود Unsafe Rust به این دلیل است که تحلیل ایستا ذاتاً محافظه‌کارانه است. وقتی کامپایلر سعی می‌کند تعیین کند که آیا کد تضمین‌ها را رعایت می‌کند یا نه، بهتر است برخی از برنامه‌های معتبر را رد کند تا اینکه برخی از برنامه‌های نامعتبر را بپذیرد. اگرچه ممکن است کد _درست_ باشد، اما اگر کامپایلر راست اطلاعات کافی برای اطمینان نداشته باشد، کد را رد خواهد کرد. در این موارد، می‌توانید از کد ناامن برای گفتن به کامپایلر استفاده کنید: «به من اعتماد کن، من می‌دانم چه کار می‌کنم.» اما هشدار داده شود که شما از کد ناامن به مسئولیت خودتان استفاده می‌کنید: اگر از کد ناامن به‌طور نادرست استفاده کنید، مشکلاتی ممکن است به دلیل ناامنی حافظه ایجاد شوند، مانند dereferencing اشاره‌گر (Pointer) null. + +دلیل دیگر وجود یک همزاد ناامن برای راست این است که سخت‌افزار کامپیوتر در ذات خود ناامن است. اگر راست به شما اجازه انجام عملیات ناامن را نمی‌داد، نمی‌توانستید برخی از وظایف را انجام دهید. راست باید به شما اجازه دهد تا برنامه‌نویسی سطح پایین سیستم، مانند تعامل مستقیم با سیستم‌عامل یا حتی نوشتن سیستم‌عامل خودتان را انجام دهید. کار با برنامه‌نویسی سطح پایین سیستم یکی از اهداف این زبان است. بیایید بررسی کنیم که با Unsafe Rust چه می‌توانیم انجام دهیم و چگونه باید این کار را انجام دهیم. ### Unsafe Superpowers -To switch to unsafe Rust, use the `unsafe` keyword and then start a new block -that holds the unsafe code. You can take five actions in unsafe Rust that you -can’t in safe Rust, which we call _unsafe superpowers_. Those superpowers -include the ability to: +برای تغییر به Unsafe Rust، از کلیدواژه `unsafe` استفاده کنید و سپس یک بلوک جدید که کد ناامن را نگه می‌دارد شروع کنید. در Unsafe Rust می‌توانید پنج عمل را انجام دهید که در راست امن نمی‌توانید، و ما این‌ها را _قدرت‌های فوق‌العاده ناامن_ می‌نامیم. این قدرت‌ها شامل توانایی‌های زیر هستند: -1. Dereference a raw pointer -1. Call an unsafe function or method -1. Access or modify a mutable static variable -1. Implement an unsafe trait -1. Access fields of `union`s +- Dereference a raw pointer +- Call an unsafe function or method +- Access or modify a mutable static variable +- Implement an unsafe trait +- Access fields of a `union` It’s important to understand that `unsafe` doesn’t turn off the borrow checker -or disable any of Rust’s other safety checks: if you use a reference in unsafe +or disable any other of Rust’s safety checks: if you use a reference in unsafe code, it will still be checked. The `unsafe` keyword only gives you access to these five features that are then not checked by the compiler for memory -safety. You’ll still get some degree of safety inside an unsafe block. +safety. You’ll still get some degree of safety inside of an unsafe block. In addition, `unsafe` does not mean the code inside the block is necessarily dangerous or that it will definitely have memory safety problems: the intent is that as the programmer, you’ll ensure the code inside an `unsafe` block will access memory in a valid way. -People are fallible and mistakes will happen, but by requiring these five -unsafe operations to be inside blocks annotated with `unsafe`, you’ll know that +People are fallible, and mistakes will happen, but by requiring these five +unsafe operations to be inside blocks annotated with `unsafe` you’ll know that any errors related to memory safety must be within an `unsafe` block. Keep `unsafe` blocks small; you’ll be thankful later when you investigate memory bugs. -To isolate unsafe code as much as possible, it’s best to enclose such code +To isolate unsafe code as much as possible, it’s best to enclose unsafe code within a safe abstraction and provide a safe API, which we’ll discuss later in the chapter when we examine unsafe functions and methods. Parts of the standard library are implemented as safe abstractions over unsafe code that has been @@ -67,29 +47,27 @@ some abstractions that provide a safe interface to unsafe code. ### Dereferencing a Raw Pointer -In Chapter 4, in [“Dangling References”][dangling-references]<!-- ignore -->, we -mentioned that the compiler ensures references are always valid. Unsafe Rust has -two new types called _raw pointers_ that are similar to references. As with -references, raw pointers can be immutable or mutable and are written as `*const -T` and `*mut T`, respectively. The asterisk isn’t the dereference operator; it’s -part of the type name. In the context of raw pointers, _immutable_ means that -the pointer can’t be directly assigned to after being dereferenced. +In Chapter 4, in the [“Dangling References”][dangling-references]<!-- ignore +--> section, we mentioned that the compiler ensures references are always +valid. Unsafe Rust has two new types called _raw pointers_ that are similar to +references. As with references, raw pointers can be immutable or mutable and +are written as `*const T` and `*mut T`, respectively. The asterisk isn’t the +dereference operator; it’s part of the type name. In the context of raw +pointers, _immutable_ means that the pointer can’t be directly assigned to +after being dereferenced. Different from references and smart pointers, raw pointers: -- Are allowed to ignore the borrowing rules by having both immutable and - mutable pointers or multiple mutable pointers to the same location -- Aren’t guaranteed to point to valid memory -- Are allowed to be null -- Don’t implement any automatic cleanup +- مجاز به نادیده گرفتن قوانین borrowing هستند، به این صورت که می‌توانند هم اشاره‌گر (Pointer)های immutable و هم اشاره‌گر (Pointer)های mutable به همان مکان داشته باشند. +- تضمینی برای اشاره به حافظه معتبر ندارند. +- می‌توانند null باشند. +- هیچ پاکسازی خودکاری را پیاده‌سازی نمی‌کنند. -By opting out of having Rust enforce these guarantees, you can give up -guaranteed safety in exchange for greater performance or the ability to -interface with another language or hardware where Rust’s guarantees don’t apply. +با صرف‌نظر از تضمین‌های اجباری راست، می‌توانید ایمنی تضمین‌شده را با عملکرد بهتر یا توانایی ارتباط با یک زبان یا سخت‌افزار دیگر که تضمین‌های راست در آن‌ها اعمال نمی‌شود، مبادله کنید. -Listing 20-1 shows how to create an immutable and a mutable raw pointer. +فهرست 20-1 نشان می‌دهد که چگونه یک اشاره‌گر (Pointer) خام immutable و یک اشاره‌گر (Pointer) خام mutable ایجاد کنیم. -<Listing number="20-1" caption="Creating raw pointers with the raw borrow operators"> +<Listing number="20-1" caption="ایجاد اشاره‌گر (Pointer)های خام با عملگرهای raw borrow"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-01/src/main.rs:here}} @@ -108,16 +86,15 @@ variable, we know these particular raw pointers are valid, but we can’t make that assumption about just any raw pointer. To demonstrate this, next we’ll create a raw pointer whose validity we can’t be -so certain of, using the keyword `as` to cast a value instead of using the raw -borrow operator. Listing 20-2 shows how to create a raw pointer to an arbitrary +so certain of, using `as` to cast a value instead of using the raw reference +operators. Listing 20-2 shows how to create a raw pointer to an arbitrary location in memory. Trying to use arbitrary memory is undefined: there might be -data at that address or there might not, the compiler might optimize the code -so there is no memory access, or the program might terminate with a -segmentation fault. Usually, there is no good reason to write code like this, -especially in cases where you can use a raw borrow operator instead, but it is -possible. +data at that address or there might not, the compiler might optimize the code so +there is no memory access, or the program might error with a segmentation fault. +Usually, there is no good reason to write code like this, especially in cases +where you can use a raw borrow operator instead, but it is possible. -<Listing number="20-2" caption="Creating a raw pointer to an arbitrary memory address"> +<Listing number="20-2" caption="ایجاد یک اشاره‌گر (Pointer) خام به یک آدرس حافظه دلخواه"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-02/src/main.rs:here}} @@ -125,11 +102,9 @@ possible. </Listing> -Recall that we can create raw pointers in safe code, but we can’t _dereference_ -raw pointers and read the data being pointed to. In Listing 20-3, we use the -dereference operator `*` on a raw pointer that requires an `unsafe` block. +به یاد داشته باشید که می‌توانیم اشاره‌گر (Pointer)های خام را در کد امن ایجاد کنیم، اما نمی‌توانیم اشاره‌گر (Pointer)های خام را _dereference_ کنیم و داده‌ای که به آن اشاره شده را بخوانیم. در فهرست 20-3، ما از عملگر dereference (`*`) روی یک اشاره‌گر (Pointer) خام استفاده می‌کنیم که به یک بلوک `unsafe` نیاز دارد. -<Listing number="20-3" caption="Dereferencing raw pointers within an `unsafe` block"> +<Listing number="20-3" caption="Dereferencing اشاره‌گر (Pointer)های خام درون یک بلوک `unsafe`"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-03/src/main.rs:here}} @@ -140,8 +115,8 @@ dereference operator `*` on a raw pointer that requires an `unsafe` block. Creating a pointer does no harm; it’s only when we try to access the value that it points at that we might end up dealing with an invalid value. -Note also that in Listings 20-1 and 20-3, we created `*const i32` and `*mut -i32` raw pointers that both pointed to the same memory location, where `num` is +Note also that in Listing 20-1 and 20-3, we created `*const i32` and `*mut i32` +raw pointers that both pointed to the same memory location, where `num` is stored. If we instead tried to create an immutable and a mutable reference to `num`, the code would not have compiled because Rust’s ownership rules don’t allow a mutable reference at the same time as any immutable references. With @@ -150,10 +125,12 @@ same location and change data through the mutable pointer, potentially creating a data race. Be careful! With all of these dangers, why would you ever use raw pointers? One major use -case is when interfacing with C code, as you’ll see in the next section. -Another case is when building up safe abstractions that the borrow checker -doesn’t understand. We’ll introduce unsafe functions and then look at an -example of a safe abstraction that uses unsafe code. +case is when interfacing with C code, as you’ll see in the next section, +[“Calling an Unsafe Function or +Method.”](#calling-an-unsafe-function-or-method)<!-- ignore --> Another case is +when building up safe abstractions that the borrow checker doesn’t understand. +We’ll introduce unsafe functions and then look at an example of a safe +abstraction that uses unsafe code. ### Calling an Unsafe Function or Method @@ -164,7 +141,7 @@ definition. The `unsafe` keyword in this context indicates the function has requirements we need to uphold when we call this function, because Rust can’t guarantee we’ve met these requirements. By calling an unsafe function within an `unsafe` block, we’re saying that we’ve read this function’s documentation and -we take responsibility for upholding the function’s contracts. +take responsibility for upholding the function’s contracts. Here is an unsafe function named `dangerous` that doesn’t do anything in its body: @@ -173,8 +150,7 @@ body: {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-01-unsafe-fn/src/main.rs:here}} ``` -We must call the `dangerous` function within a separate `unsafe` block. If we -try to call `dangerous` without the `unsafe` block, we’ll get an error: +ما باید تابع `dangerous` را در یک بلوک `unsafe` جداگانه فراخوانی کنیم. اگر سعی کنیم بدون بلوک `unsafe` تابع `dangerous` را فراخوانی کنیم، با خطا مواجه خواهیم شد: ```console {{#include ../listings/ch20-advanced-features/output-only-01-missing-unsafe/output.txt}} @@ -184,23 +160,17 @@ With the `unsafe` block, we’re asserting to Rust that we’ve read the functio documentation, we understand how to use it properly, and we’ve verified that we’re fulfilling the contract of the function. -To perform unsafe operations in the body of an `unsafe` function, you still -need to use an `unsafe` block, just as within a regular function, and the -compiler will warn you if you forget. This helps us keep `unsafe` blocks as -small as possible, as unsafe operations may not be needed across the whole -function body. +To perform unsafe operations in the body of an unsafe function, you still need +to use an `unsafe` block just as within a regular function, and the compiler +will warn you if you forget. This helps to keep `unsafe` blocks as small as +possible, as unsafe operations may not be needed across the whole function +body. #### Creating a Safe Abstraction over Unsafe Code -Just because a function contains unsafe code doesn’t mean we need to mark the -entire function as unsafe. In fact, wrapping unsafe code in a safe function is -a common abstraction. As an example, let’s study the `split_at_mut` function -from the standard library, which requires some unsafe code. We’ll explore how -we might implement it. This safe method is defined on mutable slices: it takes -one slice and makes it two by splitting the slice at the index given as an -argument. Listing 20-4 shows how to use `split_at_mut`. +فقط به این دلیل که یک تابع حاوی کد ناامن است به این معنا نیست که باید کل تابع را به‌عنوان ناامن علامت‌گذاری کنیم. در واقع، محصور کردن کد ناامن در یک تابع ایمن یک انتزاع رایج است. به‌عنوان مثال، بیایید تابع `split_at_mut` از کتابخانه استاندارد را بررسی کنیم که به کد ناامن نیاز دارد. ما بررسی خواهیم کرد که چگونه ممکن است آن را پیاده‌سازی کنیم. این متد ایمن روی برش‌های قابل تغییر (mutable slices) تعریف شده است: این تابع یک برش را می‌گیرد و آن را به دو قسمت تقسیم می‌کند با تقسیم کردن برش در ایندکسی که به‌عنوان آرگومان داده شده است. فهرست 20-4 نشان می‌دهد که چگونه از `split_at_mut` استفاده کنیم. -<Listing number="20-4" caption="Using the safe `split_at_mut` function"> +<Listing number="20-4" caption="استفاده از تابع ایمن `split_at_mut`"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-04/src/main.rs:here}} @@ -208,12 +178,9 @@ argument. Listing 20-4 shows how to use `split_at_mut`. </Listing> -We can’t implement this function using only safe Rust. An attempt might look -something like Listing 20-5, which won’t compile. For simplicity, we’ll -implement `split_at_mut` as a function rather than a method and only for slices -of `i32` values rather than for a generic type `T`. +ما نمی‌توانیم این تابع را فقط با استفاده از راست ایمن پیاده‌سازی کنیم. یک تلاش ممکن است چیزی شبیه به فهرست 20-5 باشد، که کامپایل نخواهد شد. برای سادگی، ما `split_at_mut` را به‌عنوان یک تابع پیاده‌سازی می‌کنیم نه یک متد، و فقط برای برش‌های `i32` به‌جای یک نوع generic `T`. -<Listing number="20-5" caption="An attempted implementation of `split_at_mut` using only safe Rust"> +<Listing number="20-5" caption="تلاش برای پیاده‌سازی `split_at_mut` فقط با استفاده از راست ایمن"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-05/src/main.rs:here}} @@ -231,22 +198,17 @@ Then we return two mutable slices in a tuple: one from the start of the original slice to the `mid` index and another from `mid` to the end of the slice. -When we try to compile the code in Listing 20-5, we’ll get an error: +When we try to compile the code in Listing 20-5, we’ll get an error. ```console {{#include ../listings/ch20-advanced-features/listing-20-05/output.txt}} ``` -Rust’s borrow checker can’t understand that we’re borrowing different parts of -the slice; it only knows that we’re borrowing from the same slice twice. -Borrowing different parts of a slice is fundamentally okay because the two -slices aren’t overlapping, but Rust isn’t smart enough to know this. When we -know code is okay, but Rust doesn’t, it’s time to reach for unsafe code. +Rust’s borrow checker نمی‌تواند بفهمد که ما در حال قرض گرفتن قسمت‌های مختلفی از یک برش هستیم؛ تنها چیزی که می‌داند این است که ما دو بار از همان برش قرض گرفته‌ایم. قرض گرفتن قسمت‌های مختلف یک برش اصولاً اشکالی ندارد، زیرا این دو برش با یکدیگر هم‌پوشانی ندارند، اما Rust به‌اندازه کافی هوشمند نیست که این موضوع را بداند. وقتی می‌دانیم کد مشکلی ندارد، اما Rust نمی‌داند، زمان استفاده از کد ناامن فرا می‌رسد. -Listing 20-6 shows how to use an `unsafe` block, a raw pointer, and some calls -to unsafe functions to make the implementation of `split_at_mut` work. +فهرست 20-6 نشان می‌دهد که چگونه از یک بلوک `unsafe`، یک اشاره‌گر (Pointer) خام، و چند فراخوانی به توابع ناامن برای اجرای تابع `split_at_mut` استفاده کنیم. -<Listing number="20-6" caption="Using unsafe code in the implementation of the `split_at_mut` function"> +<Listing number="20-6" caption="استفاده از کد ناامن در پیاده‌سازی تابع `split_at_mut`"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-06/src/main.rs:here}} @@ -254,12 +216,12 @@ to unsafe functions to make the implementation of `split_at_mut` work. </Listing> -Recall from [“The Slice Type”][the-slice-type]<!-- ignore --> in Chapter 4 that -a slice is a pointer to some data and the length of the slice. We use the `len` -method to get the length of a slice and the `as_mut_ptr` method to access the -raw pointer of a slice. In this case, because we have a mutable slice to `i32` -values, `as_mut_ptr` returns a raw pointer with the type `*mut i32`, which we’ve -stored in the variable `ptr`. +Recall from [“The Slice Type”][the-slice-type]<!-- ignore --> section in +Chapter 4 that slices are a pointer to some data and the length of the slice. +We use the `len` method to get the length of a slice and the `as_mut_ptr` +method to access the raw pointer of a slice. In this case, because we have a +mutable slice to `i32` values, `as_mut_ptr` returns a raw pointer with the type +`*mut i32`, which we’ve stored in the variable `ptr`. We keep the assertion that the `mid` index is within the slice. Then we get to the unsafe code: the `slice::from_raw_parts_mut` function takes a raw pointer @@ -271,7 +233,7 @@ items after `mid` as the length. The function `slice::from_raw_parts_mut` is unsafe because it takes a raw pointer and must trust that this pointer is valid. The `add` method on raw -pointers is also unsafe because it must trust that the offset location is also +pointers is also unsafe, because it must trust that the offset location is also a valid pointer. Therefore, we had to put an `unsafe` block around our calls to `slice::from_raw_parts_mut` and `add` so we could call them. By looking at the code and by adding the assertion that `mid` must be less than or equal to @@ -279,7 +241,7 @@ the code and by adding the assertion that `mid` must be less than or equal to will be valid pointers to data within the slice. This is an acceptable and appropriate use of `unsafe`. -Note that we don’t need to mark the resultant `split_at_mut` function as +Note that we don’t need to mark the resulting `split_at_mut` function as `unsafe`, and we can call this function from safe Rust. We’ve created a safe abstraction to the unsafe code with an implementation of the function that uses `unsafe` code in a safe way, because it creates only valid pointers from the @@ -289,7 +251,7 @@ In contrast, the use of `slice::from_raw_parts_mut` in Listing 20-7 would likely crash when the slice is used. This code takes an arbitrary memory location and creates a slice 10,000 items long. -<Listing number="20-7" caption="Creating a slice from an arbitrary memory location"> +<Listing number="20-7" caption="ایجاد یک برش از یک مکان حافظه دلخواه"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-07/src/main.rs:here}} @@ -297,26 +259,24 @@ location and creates a slice 10,000 items long. </Listing> -We don’t own the memory at this arbitrary location, and there is no guarantee -that the slice this code creates contains valid `i32` values. Attempting to use -`values` as though it’s a valid slice results in undefined behavior. +ما مالک حافظه در این مکان دلخواه نیستیم و هیچ تضمینی وجود ندارد که برشی که این کد ایجاد می‌کند حاوی مقادیر معتبر `i32` باشد. تلاش برای استفاده از `values` به‌عنوان اینکه یک برش معتبر است منجر به رفتار تعریف‌نشده می‌شود. #### Using `extern` Functions to Call External Code -Sometimes your Rust code might need to interact with code written in another +Sometimes, your Rust code might need to interact with code written in another language. For this, Rust has the keyword `extern` that facilitates the creation -and use of a _Foreign Function Interface (FFI)_, which is a way for a +and use of a _Foreign Function Interface (FFI)_. An FFI is a way for a programming language to define functions and enable a different (foreign) programming language to call those functions. Listing 20-8 demonstrates how to set up an integration with the `abs` function from the C standard library. Functions declared within `extern` blocks are -generally unsafe to call from Rust code, so `extern` blocks must also be marked -`unsafe`. The reason is that other languages don’t enforce Rust’s rules and -guarantees, and Rust can’t check them, so responsibility falls on the -programmer to ensure safety. +usually unsafe to call from Rust code, so they must also be marked `unsafe`. The +reason is that other languages don’t enforce Rust’s rules and guarantees, and +Rust can’t check them, so responsibility falls on the programmer to ensure +safety. -<Listing number="20-8" file-name="src/main.rs" caption="Declaring and calling an `extern` function defined in another language"> +<Listing number="20-8" file-name="src/main.rs" caption="اعلام و فراخوانی یک تابع `extern` تعریف‌شده در زبان دیگر"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-08/src/main.rs}} @@ -325,21 +285,18 @@ programmer to ensure safety. </Listing> Within the `unsafe extern "C"` block, we list the names and signatures of -external functions from another language we want to call. The `"C"` part -defines which _application binary interface (ABI)_ the external function uses: -the ABI defines how to call the function at the assembly level. The `"C"` ABI -is the most common and follows the C programming language’s ABI. Information -about all the ABIs Rust supports is available in [the Rust Reference][ABI]. - -Every item declared within an `unsafe extern` block is implicitly unsafe. -However, some FFI functions *are* safe to call. For example, the `abs` function -from C’s standard library does not have any memory safety considerations and we -know it can be called with any `i32`. In cases like this, we can use the `safe` -keyword to say that this specific function is safe to call even though it is in -an `unsafe extern` block. Once we make that change, calling it no longer -requires an `unsafe` block, as shown in Listing 20-9. - -<Listing number="20-9" file-name="src/main.rs" caption="Explicitly marking a function as `safe` within an `unsafe extern` block and calling it safely"> +external functions from another language we want to call. The `"C"` part defines +which _application binary interface (ABI)_ the external function uses: the ABI +defines how to call the function at the assembly level. The `"C"` ABI is the +most common and follows the C programming language’s ABI. + +This particular function does not have any memory safety considerations, though. +In fact, we know that any call to `abs` will always be safe for any `i32`, so we +can use the `safe` keyword to say that this specific function is safe to call +even though it is in an `unsafe extern` block. Once we make that change, calling +it no longer requires an `unsafe` block, as shown in Listing 20-9. + +<Listing number="20-9" file-name="src/main.rs" caption="علامت‌گذاری صریح یک تابع به‌عنوان `safe` درون یک بلوک `unsafe extern` و فراخوانی ایمن آن"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-09/src/main.rs}} @@ -348,49 +305,46 @@ requires an `unsafe` block, as shown in Listing 20-9. </Listing> Marking a function as `safe` does not inherently make it safe! Instead, it is -like a promise you are making to Rust that it is safe. It is still your +like a promise you are making to Rust that it _is_ safe. It is still your responsibility to make sure that promise is kept! -#### Calling Rust Functions from Other Languages - -We can also use `extern` to create an interface that allows other languages to -call Rust functions. Instead of creating a whole `extern` block, we add the -`extern` keyword and specify the ABI to use just before the `fn` keyword for -the relevant function. We also need to add an `#[unsafe(no_mangle)]` annotation -to tell the Rust compiler not to mangle the name of this function. _Mangling_ -is when a compiler changes the name we’ve given a function to a different name -that contains more information for other parts of the compilation process to -consume but is less human readable. Every programming language compiler mangles -names slightly differently, so for a Rust function to be nameable by other -languages, we must disable the Rust compiler’s name mangling. This is unsafe -because there might be name collisions across libraries without the built-in -mangling, so it is our responsibility to make sure the name we choose is safe -to export without mangling. - -In the following example, we make the `call_from_c` function accessible from C -code, after it’s compiled to a shared library and linked from C: - -``` -#[unsafe(no_mangle)] -pub extern "C" fn call_from_c() { - println!("Just called a Rust function from C!"); -} -``` - -This usage of `extern` requires `unsafe` only in the attribute, not on the -`extern` block. +> #### Calling Rust Functions from Other Languages +> +> We can also use `extern` to create an interface that allows other languages to +> call Rust functions. Instead of creating a whole `extern` block, we add the +> `extern` keyword and specify the ABI to use just before the `fn` keyword for +> the relevant function. We also need to add a `#[unsafe(no_mangle)]` annotation +> to tell the Rust compiler not to mangle the name of this function. _Mangling_ +> is when a compiler changes the name we’ve given a function to a different name +> that contains more information for other parts of the compilation process to +> consume but is less human readable. Every programming language compiler +> mangles names slightly differently, so for a Rust function to be nameable by +> other languages, we must disable the Rust compiler’s name mangling. This is +> unsafe because there might be name collisions across libraries without the +> built-in mangling, so it is our responsibility to make sure the name we have +> exported is safe to export without mangling. +> +> In the following example, we make the `call_from_c` function accessible from +> C code, after it’s compiled to a shared library and linked from C: +> +> ```rust +> #[unsafe(no_mangle)] +> pub extern "C" fn call_from_c() { +> println!("Just called a Rust function from C!"); +> } +> ``` +> +> This usage of `extern` does not require `unsafe`. ### Accessing or Modifying a Mutable Static Variable -In this book, we’ve not yet talked about global variables, which Rust does +In this book, we’ve not yet talked about _global variables_, which Rust does support but can be problematic with Rust’s ownership rules. If two threads are accessing the same mutable global variable, it can cause a data race. -In Rust, global variables are called _static_ variables. Listing 20-10 shows an -example declaration and use of a static variable with a string slice as a -value. +در راست، متغیرهای جهانی _static_ نامیده می‌شوند. فهرست 20-10 یک مثال از اعلام و استفاده از یک متغیر static با یک string slice به‌عنوان مقدار را نشان می‌دهد. -<Listing number="20-10" file-name="src/main.rs" caption="Defining and using an immutable static variable"> +<Listing number="20-10" file-name="src/main.rs" caption="تعریف و استفاده از یک متغیر static غیرقابل تغییر"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-10/src/main.rs}} @@ -398,9 +352,9 @@ value. </Listing> -Static variables are similar to constants, which we discussed in -[“Constants”][differences-between-variables-and-constants]<!-- ignore --> in -Chapter 3. The names of static variables are in `SCREAMING_SNAKE_CASE` by +Static variables are similar to constants, which we discussed in the +[“Constants”][differences-between-variables-and-constants]<!-- ignore --> section +in Chapter 3. The names of static variables are in `SCREAMING_SNAKE_CASE` by convention. Static variables can only store references with the `'static` lifetime, which means the Rust compiler can figure out the lifetime and we aren’t required to annotate it explicitly. Accessing an immutable static @@ -414,7 +368,7 @@ variables can be mutable. Accessing and modifying mutable static variables is _unsafe_. Listing 20-11 shows how to declare, access, and modify a mutable static variable named `COUNTER`. -<Listing number="20-11" file-name="src/main.rs" caption="Reading from or writing to a mutable static variable is unsafe."> +<Listing number="20-11" file-name="src/main.rs" caption="Reading from or writing to a mutable static variable is unsafe"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-11/src/main.rs}} @@ -427,7 +381,7 @@ code that reads or writes from `COUNTER` must be within an `unsafe` block. The code in Listing 20-11 compiles and prints `COUNTER: 3` as we would expect because it’s single threaded. Having multiple threads access `COUNTER` would likely result in data races, so it is undefined behavior. Therefore, we need to -mark the entire function as `unsafe` and document the safety limitation, so +mark the entire function as `unsafe`, and document the safety limitation, so anyone calling the function knows what they are and are not allowed to do safely. @@ -437,31 +391,24 @@ function safely. Likewise, whenever we perform an unsafe operation, it is idiomatic to write a comment starting with `SAFETY` to explain how the safety rules are upheld. -Additionally, the compiler will deny by default any attempt to create -references to a mutable static variable through a compiler lint. You must -either explicitly opt-out of that lint’s protections by adding an -`#[allow(static_mut_refs)]` annotation or access the mutable static variable -via a raw pointer created with one of the raw borrow operators. That includes -cases where the reference is created invisibly, as when it is used in the -`println!` in this code listing. Requiring references to static mutable -variables to be created via raw pointers helps make the safety requirements for -using them more obvious. +Additionally, the compiler will not allow you to create references to a mutable +static variable. You can only access it via a raw pointer, created with one of +the raw borrow operators. That includes in cases where the reference is created +invisibly, as when it is used in the `println!` in this code listing. The +requirement that references to static mutable variables can only be created via +raw pointers helps make the safety requirements for using them more obvious. With mutable data that is globally accessible, it’s difficult to ensure there are no data races, which is why Rust considers mutable static variables to be unsafe. Where possible, it’s preferable to use the concurrency techniques and thread-safe smart pointers we discussed in Chapter 16 so the compiler checks -that data access from different threads is done safely. +that data accessed from different threads is done safely. ### Implementing an Unsafe Trait -We can use `unsafe` to implement an unsafe trait. A trait is unsafe when at -least one of its methods has some invariant that the compiler can’t verify. We -declare that a trait is `unsafe` by adding the `unsafe` keyword before `trait` -and marking the implementation of the trait as `unsafe` too, as shown in -Listing 20-12. +می‌توانیم از `unsafe` برای پیاده‌سازی یک trait ناامن استفاده کنیم. یک trait زمانی ناامن است که حداقل یکی از متدهای آن دارای یک قاعده (invariant) باشد که کامپایلر نمی‌تواند آن را تأیید کند. ما با افزودن کلیدواژه `unsafe` قبل از `trait` اعلام می‌کنیم که یک trait ناامن است و پیاده‌سازی آن trait را نیز به‌عنوان `unsafe` علامت‌گذاری می‌کنیم، همان‌طور که در فهرست 20-12 نشان داده شده است. -<Listing number="20-12" caption="Defining and implementing an unsafe trait"> +<Listing number="20-12" caption="تعریف و پیاده‌سازی یک trait ناامن"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-12/src/main.rs:here}} @@ -472,82 +419,76 @@ Listing 20-12. By using `unsafe impl`, we’re promising that we’ll uphold the invariants that the compiler can’t verify. -As an example, recall the `Send` and `Sync` marker traits we discussed in -[“Extensible Concurrency with the `Send` and `Sync` -Traits”][extensible-concurrency-with-the-send-and-sync-traits]<!-- ignore --> -in Chapter 16: the compiler implements these traits automatically if our types -are composed entirely of other types that implement `Send` and `Sync`. If we -implement a type that contains a type that does not implement `Send` or `Sync`, -such as raw pointers, and we want to mark that type as `Send` or `Sync`, we -must use `unsafe`. Rust can’t verify that our type upholds the guarantees that -it can be safely sent across threads or accessed from multiple threads; -therefore, we need to do those checks manually and indicate as such with -`unsafe`. +As an example, recall the `Sync` and `Send` marker traits we discussed in the +[“Extensible Concurrency with the `Sync` and `Send` +Traits”][extensible-concurrency-with-the-sync-and-send-traits]<!-- ignore --> +section in Chapter 16: the compiler implements these traits automatically if +our types are composed entirely of `Send` and `Sync` types. If we implement a +type that contains a type that is not `Send` or `Sync`, such as raw pointers, +and we want to mark that type as `Send` or `Sync`, we must use `unsafe`. Rust +can’t verify that our type upholds the guarantees that it can be safely sent +across threads or accessed from multiple threads; therefore, we need to do +those checks manually and indicate as such with `unsafe`. ### Accessing Fields of a Union -The final action that works only with `unsafe` is accessing fields of a union. -A *union* is similar to a `struct`, but only one declared field is used in a -particular instance at one time. Unions are primarily used to interface with -unions in C code. Accessing union fields is unsafe because Rust can’t guarantee -the type of the data currently being stored in the union instance. You can -learn more about unions in [the Rust Reference][unions]. +The final action that works only with `unsafe` is accessing fields of a +_union_. A `union` is similar to a `struct`, but only one declared field is +used in a particular instance at one time. Unions are primarily used to +interface with unions in C code. Accessing union fields is unsafe because Rust +can’t guarantee the type of the data currently being stored in the union +instance. You can learn more about unions in [the Rust Reference][reference]. ### Using Miri to Check Unsafe Code When writing unsafe code, you might want to check that what you have written -actually is safe and correct. One of the best ways to do that is to use Miri, -an official Rust tool for detecting undefined behavior. Whereas the borrow -checker is a _static_ tool which works at compile time, Miri is a _dynamic_ -tool which works at runtime. It checks your code by running your program, or -its test suite, and detecting when you violate the rules it understands about -how Rust should work. +actually is safe and correct. One of the best ways to do that is to use +[Miri][miri], an official Rust tool for detecting undefined behavior. Whereas +the borrow checker is a _static_ tool which works at compile time, Miri is a +_dynamic_ tool which works at runtime. It checks your code by running your +program, or its test suite, and detecting when you violate the rules it +understands about how Rust should work. Using Miri requires a nightly build of Rust (which we talk about more in -[Appendix G: How Rust is Made and “Nightly Rust”][nightly]<!-- ignore -->). You -can install both a nightly version of Rust and the Miri tool by typing `rustup -+nightly component add miri`. This does not change what version of Rust your -project uses; it only adds the tool to your system so you can use it when you -want to. You can run Miri on a project by typing `cargo +nightly miri run` or -`cargo +nightly miri test`. +[Appendix G: How Rust is Made and “Nightly Rust”][nightly]). You can install +both a nightly version of Rust and the Miri tool by typing `rustup +nightly +component add miri`. This does not change what version of Rust your project +uses; it only adds the tool to your system so you can use it when you want to. +You can run Miri on a project by typing `cargo +nightly miri run` or `cargo ++nightly miri test`. For an example of how helpful this can be, consider what happens when we run it -against Listing 20-7. +against Listing 20-11: ```console {{#include ../listings/ch20-advanced-features/listing-20-07/output.txt}} ``` -Miri correctly warns us that we’re casting an integer to a pointer, which might -be a problem but Miri can’t detect if there is because it doesn’t know how the -pointer originated. Then, Miri returns an error where Listing 20-7 has -undefined behavior because we have a dangling pointer. Thanks to Miri, we now -know there is a risk of undefined behavior, and we can think about how to make -the code safe. In some cases, Miri can even make recommendations about how to -fix errors. - -Miri doesn’t catch everything you might get wrong when writing unsafe code. -Miri is a dynamic analysis tool, so it only catches problems with code that -actually gets run. That means you will need to use it in conjunction with good -testing techniques to increase your confidence about the unsafe code you have -written. Miri also does not cover every possible way your code can be unsound. - -Put another way: If Miri _does_ catch a problem, you know there’s a bug, but -just because Miri _doesn’t_ catch a bug doesn’t mean there isn’t a problem. It -can catch a lot, though. Try running it on the other examples of unsafe code in -this chapter and see what it says! - -You can learn more about Miri at [its GitHub repository][miri]. +It helpfully and correctly notices that we have shared references to mutable +data, and warns about it. In this case, it does not tell us how to fix the +problem, but it means that we know there is a possible issue and can think about +how to make sure it is safe. In other cases, it can actually tell us that some +code is _sure_ to be wrong and make recommendations about how to fix it. + +Miri doesn’t catch _everything_ you might get wrong when writing unsafe code. +For one thing, since it is a dynamic check, it only catches problems with code +that actually gets run. That means you will need to use it in conjunction with +good testing techniques to increase your confidence about the unsafe code you +have written. For another thing, it does not cover every possible way your code +can be unsound. If Miri _does_ catch a problem, you know there’s a bug, but just +because Miri _doesn’t_ catch a bug doesn’t mean there isn’t a problem. Miri can +catch a lot, though. Try running it on the other examples of unsafe code in this +chapter and see what it says! ### When to Use Unsafe Code -Using `unsafe` to use one of the five superpowers just discussed isn’t wrong or -even frowned upon, but it is trickier to get `unsafe` code correct because the -compiler can’t help uphold memory safety. When you have a reason to use -`unsafe` code, you can do so, and having the explicit `unsafe` annotation makes -it easier to track down the source of problems when they occur. Whenever you -write unsafe code, you can use Miri to help you be more confident that the code -you have written upholds Rust’s rules. +Using `unsafe` to take one of the five actions (superpowers) just discussed +isn’t wrong or even frowned upon. But it is trickier to get `unsafe` code +correct because the compiler can’t help uphold memory safety. When you have a +reason to use `unsafe` code, you can do so, and having the explicit `unsafe` +annotation makes it easier to track down the source of problems when they occur. +Whenever you write unsafe code, you can use Miri to help you be more confident +that the code you have written upholds Rust’s rules. For a much deeper exploration of how to work effectively with unsafe Rust, read Rust’s official guide to the subject, the [Rustonomicon][nomicon]. diff --git a/src/ch20-02-advanced-traits.md b/src/ch20-02-advanced-traits.md index ec9cc648a2..30f4679803 100644 --- a/src/ch20-02-advanced-traits.md +++ b/src/ch20-02-advanced-traits.md @@ -1,9 +1,6 @@ ## Advanced Traits -We first covered traits in [“Traits: Defining Shared -Behavior”][traits-defining-shared-behavior]<!-- ignore --> in Chapter 10, but we -didn’t discuss the more advanced details. Now that you know more about Rust, we -can get into the nitty-gritty. +ما ابتدا `trait`ها را در بخش [«Traits: تعریف رفتار مشترک»][traits-defining-shared-behavior]<!-- ignore --> در فصل ۱۰ بررسی کردیم، اما وارد جزئیات پیشرفته‌تر آن نشدیم. اکنون که با Rust بیشتر آشنا شده‌اید، می‌توانیم به نکات دقیق‌تر و تخصصی‌تر بپردازیم. <!-- Old link, do not remove --> @@ -11,25 +8,13 @@ can get into the nitty-gritty. ### Associated Types -_Associated types_ connect a type placeholder with a trait such that the trait -method definitions can use these placeholder types in their signatures. The -implementor of a trait will specify the concrete type to be used instead of the -placeholder type for the particular implementation. That way, we can define a -trait that uses some types without needing to know exactly what those types are -until the trait is implemented. +_نوع‌های مرتبط_ (Associated types) یک نوع جایگزین را با یک trait متصل می‌کنند، به‌گونه‌ای که تعریف‌های متد trait می‌توانند از این نوع‌های جایگزین در امضاهای خود استفاده کنند. پیاده‌ساز یک trait نوع خاصی را برای جایگزینی نوع جایگزین برای پیاده‌سازی خاص مشخص می‌کند. به این ترتیب، می‌توانیم یک trait تعریف کنیم که از برخی نوع‌ها استفاده می‌کند بدون اینکه نیاز داشته باشیم دقیقاً بدانیم این نوع‌ها چه هستند تا زمانی که trait پیاده‌سازی شود. -We’ve described most of the advanced features in this chapter as being rarely -needed. Associated types are somewhere in the middle: they’re used more rarely -than features explained in the rest of the book but more commonly than many of -the other features discussed in this chapter. +بیشتر ویژگی‌های پیشرفته‌ای که در این فصل توضیح داده شده است، به‌ندرت مورد نیاز هستند. نوع‌های مرتبط در حد وسط قرار دارند: آن‌ها کمتر از ویژگی‌های توضیح داده‌شده در بقیه کتاب استفاده می‌شوند، اما بیشتر از بسیاری از ویژگی‌های دیگر مورد بحث در این فصل به کار می‌روند. -One example of a trait with an associated type is the `Iterator` trait that the -standard library provides. The associated type is named `Item` and stands in -for the type of the values the type implementing the `Iterator` trait is -iterating over. The definition of the `Iterator` trait is as shown in Listing -20-13. +یکی از مثال‌های یک trait با یک نوع مرتبط، trait `Iterator` است که کتابخانه استاندارد فراهم می‌کند. نوع مرتبط با نام `Item` مشخص شده و به‌جای نوع مقادیری که نوع پیاده‌سازی‌کننده `Iterator` از روی آن‌ها تکرار می‌کند قرار می‌گیرد. تعریف trait `Iterator` همان‌طور که در فهرست 20-13 نشان داده شده است: -<Listing number="20-13" caption="The definition of the `Iterator` trait that has an associated type `Item`"> +<Listing number="20-13" caption="تعریف trait `Iterator` که دارای یک نوع مرتبط به نام `Item` است"> ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-13/src/lib.rs}} @@ -37,16 +22,9 @@ iterating over. The definition of the `Iterator` trait is as shown in Listing </Listing> -The type `Item` is a placeholder, and the `next` method’s definition shows that -it will return values of type `Option<Self::Item>`. Implementors of the -`Iterator` trait will specify the concrete type for `Item`, and the `next` -method will return an `Option` containing a value of that concrete type. +نوع `Item` یک جایگزین است و تعریف متد `next` نشان می‌دهد که مقادیری از نوع `Option<Self::Item>` را بازمی‌گرداند. پیاده‌سازان trait `Iterator` نوع خاصی را برای `Item` مشخص می‌کنند و متد `next` یک `Option` حاوی مقدار از آن نوع خاص بازمی‌گرداند. -Associated types might seem like a similar concept to generics, in that the -latter allow us to define a function without specifying what types it can -handle. To examine the difference between the two concepts, we’ll look at an -implementation of the `Iterator` trait on a type named `Counter` that specifies -the `Item` type is `u32`: +نوع‌های مرتبط ممکن است مفهومی مشابه با genericها به نظر برسند، به این معنا که genericها به ما اجازه می‌دهند یک تابع بدون مشخص کردن نوع‌هایی که می‌تواند با آن‌ها کار کند، تعریف کنیم. برای بررسی تفاوت بین این دو مفهوم، به یک پیاده‌سازی trait `Iterator` روی یک نوع به نام `Counter` نگاه خواهیم کرد که نوع `Item` را به‌عنوان `u32` مشخص می‌کند: <Listing file-name="src/lib.rs"> @@ -56,10 +34,9 @@ the `Item` type is `u32`: </Listing> -This syntax seems comparable to that of generics. So why not just define the -`Iterator` trait with generics, as shown in Listing 20-14? +این سینتکس با سینتکس genericها قابل مقایسه به نظر می‌رسد. پس چرا به جای این کار، trait `Iterator` را با استفاده از genericها تعریف نکنیم، همان‌طور که در فهرست 20-14 نشان داده شده است؟ -<Listing number="20-14" caption="A hypothetical definition of the `Iterator` trait using generics"> +<Listing number="20-14" caption="یک تعریف فرضی از trait `Iterator` با استفاده از genericها"> ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-14/src/lib.rs}} @@ -67,46 +44,21 @@ This syntax seems comparable to that of generics. So why not just define the </Listing> -The difference is that when using generics, as in Listing 20-14, we must -annotate the types in each implementation; because we can also implement -`Iterator<String> for Counter` or any other type, we could have multiple -implementations of `Iterator` for `Counter`. In other words, when a trait has a -generic parameter, it can be implemented for a type multiple times, changing -the concrete types of the generic type parameters each time. When we use the -`next` method on `Counter`, we would have to provide type annotations to -indicate which implementation of `Iterator` we want to use. - -With associated types, we don’t need to annotate types because we can’t -implement a trait on a type multiple times. In Listing 20-13 with the -definition that uses associated types, we can choose what the type of `Item` -will be only once because there can be only one `impl Iterator for Counter`. We -don’t have to specify that we want an iterator of `u32` values everywhere we -call `next` on `Counter`. - -Associated types also become part of the trait’s contract: implementors of the -trait must provide a type to stand in for the associated type placeholder. -Associated types often have a name that describes how the type will be used, -and documenting the associated type in the API documentation is a good practice. +تفاوت این است که هنگام استفاده از genericها، همان‌طور که در فهرست 20-14 نشان داده شده است، ما باید نوع‌ها را در هر پیاده‌سازی حاشیه‌نویسی کنیم. زیرا می‌توانیم همچنین `Iterator<String> for Counter` یا هر نوع دیگری را پیاده‌سازی کنیم، به‌طوری که بتوانیم پیاده‌سازی‌های متعددی از `Iterator` برای `Counter` داشته باشیم. به عبارت دیگر، زمانی که یک trait یک پارامتر generic دارد، می‌تواند برای یک نوع چندین بار پیاده‌سازی شود و نوع‌های خاص پارامترهای generic را هر بار تغییر دهد. زمانی که ما از متد `next` بر روی `Counter` استفاده می‌کنیم، باید حاشیه‌نویسی نوع ارائه دهیم تا مشخص کنیم کدام پیاده‌سازی `Iterator` را می‌خواهیم استفاده کنیم. + +با استفاده از نوع‌های وابسته (associated types)، نیازی به مشخص‌کردن نوع‌ها نداریم، زیرا نمی‌توان یک `trait` را چند بار برای یک نوع پیاده‌سازی کرد. در لیستینگ 20-13، با تعریفی که از نوع‌های وابسته استفاده می‌کند، تنها یک‌بار می‌توانیم مشخص کنیم که نوع `Item` چه چیزی خواهد بود، چرا که تنها یک `impl Iterator for Counter` می‌تواند وجود داشته باشد. بنابراین، لازم نیست هر بار که روی `Counter` تابع `next` را صدا می‌زنیم، مشخص کنیم که می‌خواهیم یک `iterator` از نوع `u32` داشته باشیم. + +نوع‌های وابسته همچنین بخشی از قرارداد `trait` محسوب می‌شوند: پیاده‌سازان یک `trait` باید نوعی را به‌جای جای‌نگهدار (placeholder) نوع وابسته ارائه دهند. معمولاً نام نوع‌های وابسته به‌گونه‌ای انتخاب می‌شود که نشان دهد چگونه از آن نوع استفاده خواهد شد، و مستندسازی نوع‌های وابسته در مستندات API یک کار بسیار خوب و توصیه‌شده است. ### Default Generic Type Parameters and Operator Overloading -When we use generic type parameters, we can specify a default concrete type for -the generic type. This eliminates the need for implementors of the trait to -specify a concrete type if the default type works. You specify a default type -when declaring a generic type with the `<PlaceholderType=ConcreteType>` syntax. +وقتی که از پارامترهای generic type استفاده می‌کنیم، می‌توانیم یک نوع خاص پیش‌فرض برای پارامتر generic تعیین کنیم. این نیاز به مشخص کردن یک نوع خاص توسط پیاده‌سازان trait را در صورتی که نوع پیش‌فرض کار کند، از بین می‌برد. شما می‌توانید هنگام اعلام یک نوع generic، یک نوع پیش‌فرض با سینتکس `<PlaceholderType=ConcreteType>` مشخص کنید. -A great example of a situation where this technique is useful is with _operator -overloading_, in which you customize the behavior of an operator (such as `+`) -in particular situations. +یک مثال عالی از وضعیتی که این تکنیک مفید است، _بارگذاری مجدد عملگرها_ است، جایی که شما رفتار یک عملگر (مانند `+`) را در شرایط خاص شخصی‌سازی می‌کنید. -Rust doesn’t allow you to create your own operators or overload arbitrary -operators. But you can overload the operations and corresponding traits listed -in `std::ops` by implementing the traits associated with the operator. For -example, in Listing 20-15 we overload the `+` operator to add two `Point` -instances together. We do this by implementing the `Add` trait on a `Point` -struct. +زبان Rust اجازه نمی‌دهد که عملگرهای دلخواه خودتان را ایجاد کرده یا هر عملگری را به‌دلخواه overload کنید. اما می‌توانید عملیات و traitهای متناظر فهرست‌شده در `std::ops` را با پیاده‌سازی traitهای مربوط به آن عملگر overload کنید. برای مثال، در لیستینگ 20-15 عملگر `+` را overload می‌کنیم تا دو نمونه از `Point` را با یکدیگر جمع کنیم. این کار را با پیاده‌سازی trait `Add` برای ساختار `Point` انجام می‌دهیم. -<Listing number="20-15" file-name="src/main.rs" caption="Implementing the `Add` trait to overload the `+` operator for `Point` instances"> +<Listing number="20-15" file-name="src/main.rs" caption="پیاده‌سازی trait `Add` برای بارگذاری مجدد عملگر `+` برای نمونه‌های `Point`"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-15/src/main.rs}} @@ -114,13 +66,9 @@ struct. </Listing> -The `add` method adds the `x` values of two `Point` instances and the `y` -values of two `Point` instances to create a new `Point`. The `Add` trait has an -associated type named `Output` that determines the type returned from the `add` -method. +متد `add` مقادیر `x` دو نمونه `Point` و مقادیر `y` دو نمونه `Point` را اضافه می‌کند تا یک نمونه جدید از `Point` ایجاد کند. trait `Add` دارای یک نوع مرتبط با نام `Output` است که نوع بازگشتی از متد `add` را تعیین می‌کند. -The default generic type in this code is within the `Add` trait. Here is its -definition: +نوع generic پیش‌فرض در این کد در داخل trait `Add` است. در اینجا تعریف آن آمده است: ```rust trait Add<Rhs=Self> { @@ -130,28 +78,13 @@ trait Add<Rhs=Self> { } ``` -This code should look generally familiar: a trait with one method and an -associated type. The new part is `Rhs=Self`: this syntax is called _default -type parameters_. The `Rhs` generic type parameter (short for “right-hand -side”) defines the type of the `rhs` parameter in the `add` method. If we don’t -specify a concrete type for `Rhs` when we implement the `Add` trait, the type -of `Rhs` will default to `Self`, which will be the type we’re implementing -`Add` on. - -When we implemented `Add` for `Point`, we used the default for `Rhs` because we -wanted to add two `Point` instances. Let’s look at an example of implementing -the `Add` trait where we want to customize the `Rhs` type rather than using the -default. - -We have two structs, `Millimeters` and `Meters`, holding values in different -units. This thin wrapping of an existing type in another struct is known as the -_newtype pattern_, which we describe in more detail in the [“Using the Newtype -Pattern to Implement External Traits”][newtype]<!-- ignore ---> section. We want to add values in millimeters to values in meters and have -the implementation of `Add` do the conversion correctly. We can implement `Add` -for `Millimeters` with `Meters` as the `Rhs`, as shown in Listing 20-16. - -<Listing number="20-16" file-name="src/lib.rs" caption="Implementing the `Add` trait on `Millimeters` to add `Millimeters` and `Meters`"> +این کد باید به‌طور کلی آشنا به نظر برسد: یک `trait` با یک متد و یک نوع وابسته. بخش جدید `Rhs=Self` است؛ این نحوی به *پارامتر نوع پیش‌فرض* (default type parameters) معروف است. پارامتر نوع generic با نام `Rhs` (مخفف "right-hand side") نوع پارامتر `rhs` در متد `add` را تعریف می‌کند. اگر هنگام پیاده‌سازی `trait` `Add` نوع مشخصی برای `Rhs` تعیین نکنیم، مقدار پیش‌فرض `Rhs` برابر با `Self` خواهد بود، یعنی همان نوعی که در حال پیاده‌سازی `Add` برای آن هستیم. + +هنگامی که `Add` را برای `Point` پیاده‌سازی کردیم، از پیش‌فرض برای `Rhs` استفاده کردیم زیرا می‌خواستیم دو نمونه `Point` را به هم اضافه کنیم. حال، بیایید به مثالی از پیاده‌سازی trait `Add` نگاه کنیم که در آن می‌خواهیم نوع `Rhs` را شخصی‌سازی کنیم و از پیش‌فرض استفاده نکنیم. + +ما دو `struct` به نام‌های `Millimeters` و `Meters` داریم که مقادیر را در واحدهای مختلف نگهداری می‌کنند. این بسته‌بندی نازک یک نوع موجود درون یک `struct` دیگر به الگوی *newtype* معروف است، که در بخش \[«استفاده از الگوی Newtype برای پیاده‌سازی Traitهای خارجی»]\[newtype]<!-- ignore --> به‌صورت دقیق‌تر توضیح داده‌ایم. ما می‌خواهیم مقادیر `millimeters` را با مقادیر `meters` جمع کنیم و پیاده‌سازی `Add` تبدیل واحد را به‌درستی انجام دهد. می‌توانیم `Add` را برای `Millimeters` پیاده‌سازی کنیم به‌گونه‌ای که `Meters` به‌عنوان `Rhs` استفاده شود، همان‌طور که در لیستینگ 20-16 نشان داده شده است. + +<Listing number="20-16" file-name="src/lib.rs" caption="پیاده‌سازی trait `Add` برای `Millimeters` جهت افزودن `Millimeters` به `Meters`"> ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-16/src/lib.rs}} @@ -159,25 +92,16 @@ for `Millimeters` with `Meters` as the `Rhs`, as shown in Listing 20-16. </Listing> -To add `Millimeters` and `Meters`, we specify `impl Add<Meters>` to set the -value of the `Rhs` type parameter instead of using the default of `Self`. +برای افزودن `Millimeters` و `Meters`، ما `impl Add<Meters>` را مشخص می‌کنیم تا مقدار پارامتر نوع `Rhs` را به جای استفاده از پیش‌فرض `Self` تنظیم کنیم. -You’ll use default type parameters in two main ways: +شما از پارامترهای نوع پیش‌فرض در دو حالت اصلی استفاده خواهید کرد: -1. To extend a type without breaking existing code -2. To allow customization in specific cases most users won’t need +۱. برای گسترش یک نوع بدون آن‌که کد موجود را دچار شکست کنیم +۲. برای فراهم‌کردن امکان سفارشی‌سازی در موارد خاصی که اکثر کاربران به آن نیازی نخواهند داشت -The standard library’s `Add` trait is an example of the second purpose: -usually, you’ll add two like types, but the `Add` trait provides the ability to -customize beyond that. Using a default type parameter in the `Add` trait -definition means you don’t have to specify the extra parameter most of the -time. In other words, a bit of implementation boilerplate isn’t needed, making -it easier to use the trait. +trait `Add` در کتابخانه استاندارد یک مثال از هدف دوم است: معمولاً شما دو نوع مشابه را اضافه خواهید کرد، اما trait `Add` قابلیت شخصی‌سازی فراتر از آن را فراهم می‌کند. استفاده از پارامتر نوع پیش‌فرض در تعریف trait `Add` به این معناست که شما بیشتر اوقات نیازی به مشخص کردن پارامتر اضافی ندارید. به عبارت دیگر، مقدار کمی از کد اضافی حذف می‌شود و استفاده از trait آسان‌تر می‌شود. -The first purpose is similar to the second but in reverse: if you want to add a -type parameter to an existing trait, you can give it a default to allow -extension of the functionality of the trait without breaking the existing -implementation code. +هدف اول مشابه هدف دوم است، اما به‌صورت معکوس: اگر بخواهید یک پارامتر نوع را به یک trait موجود اضافه کنید، می‌توانید برای گسترش قابلیت‌های trait بدون شکستن کد پیاده‌سازی موجود، یک مقدار پیش‌فرض برای آن تنظیم کنید. <!-- Old link, do not remove --> @@ -185,18 +109,11 @@ implementation code. ### Disambiguating Between Methods with the Same Name -Nothing in Rust prevents a trait from having a method with the same name as -another trait’s method, nor does Rust prevent you from implementing both traits -on one type. It’s also possible to implement a method directly on the type with -the same name as methods from traits. +در راست هیچ محدودیتی برای داشتن یک متد با همان نام در یک trait و در نوعی دیگر وجود ندارد و همچنین راست مانع نمی‌شود که هر دو trait را بر روی یک نوع پیاده‌سازی کنید. همچنین می‌توانید متدی را مستقیماً بر روی نوعی پیاده‌سازی کنید که همان نام متدهای مربوط به traits را دارد. -When calling methods with the same name, you’ll need to tell Rust which one you -want to use. Consider the code in Listing 20-17 where we’ve defined two traits, -`Pilot` and `Wizard`, that both have a method called `fly`. We then implement -both traits on a type `Human` that already has a method named `fly` implemented -on it. Each `fly` method does something different. +هنگام فراخوانی متدهایی با همان نام، باید به راست بگویید که کدام یک را می‌خواهید استفاده کنید. کد زیر در فهرست 20-17 را در نظر بگیرید که در آن دو trait به نام‌های `Pilot` و `Wizard` تعریف شده‌اند که هر دو دارای متدی به نام `fly` هستند. سپس هر دو trait بر روی نوع `Human` پیاده‌سازی می‌شوند که قبلاً متدی به نام `fly` نیز بر روی آن پیاده‌سازی شده است. هر متد `fly` کاری متفاوت انجام می‌دهد. -<Listing number="20-17" file-name="src/main.rs" caption="Two traits are defined to have a `fly` method and are implemented on the `Human` type, and a `fly` method is implemented on `Human` directly."> +<Listing number="20-17" file-name="src/main.rs" caption="دو trait تعریف شده‌اند که یک متد مشترک دارند و بر روی نوع `Human` پیاده‌سازی شده‌اند، و یک متد `fly` به‌طور مستقیم بر روی `Human` پیاده‌سازی شده است"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-17/src/main.rs:here}} @@ -204,10 +121,9 @@ on it. Each `fly` method does something different. </Listing> -When we call `fly` on an instance of `Human`, the compiler defaults to calling -the method that is directly implemented on the type, as shown in Listing 20-18. +وقتی متد `fly` را بر روی یک نمونه از `Human` فراخوانی می‌کنیم، کامپایلر به طور پیش‌فرض متدی را که مستقیماً بر روی نوع پیاده‌سازی شده است، فراخوانی می‌کند، همان‌طور که در فهرست 20-18 نشان داده شده است. -<Listing number="20-18" file-name="src/main.rs" caption="Calling `fly` on an instance of `Human`"> +<Listing number="20-18" file-name="src/main.rs" caption="فراخوانی `fly` بر روی یک نمونه از `Human`"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-18/src/main.rs:here}} @@ -215,14 +131,11 @@ the method that is directly implemented on the type, as shown in Listing 20-18. </Listing> -Running this code will print `*waving arms furiously*`, showing that Rust -called the `fly` method implemented on `Human` directly. +اجرای این کد متن `*waving arms furiously*` را چاپ می‌کند و نشان می‌دهد که راست متد `fly` پیاده‌سازی‌شده بر روی `Human` را مستقیماً فراخوانی کرده است. -To call the `fly` methods from either the `Pilot` trait or the `Wizard` trait, -we need to use more explicit syntax to specify which `fly` method we mean. -Listing 20-19 demonstrates this syntax. +برای فراخوانی متدهای `fly` از `Pilot` یا `Wizard`، باید از سینتکس صریح‌تری برای مشخص کردن متدی که منظور ماست، استفاده کنیم. فهرست 20-19 این سینتکس را نشان می‌دهد. -<Listing number="20-19" file-name="src/main.rs" caption="Specifying which trait’s `fly` method we want to call"> +<Listing number="20-19" file-name="src/main.rs" caption="مشخص کردن متد `fly` مربوط به کدام trait را می‌خواهیم فراخوانی کنیم"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-19/src/main.rs:here}} @@ -230,32 +143,19 @@ Listing 20-19 demonstrates this syntax. </Listing> -Specifying the trait name before the method name clarifies to Rust which -implementation of `fly` we want to call. We could also write -`Human::fly(&person)`, which is equivalent to the `person.fly()` that we used -in Listing 20-19, but this is a bit longer to write if we don’t need to -disambiguate. +مشخص کردن نام trait قبل از نام متد، به راست مشخص می‌کند که کدام پیاده‌سازی متد `fly` را می‌خواهیم فراخوانی کنیم. همچنین می‌توانیم `Human::fly(&person)` بنویسیم که معادل با `person.fly()` است که در فهرست 20-19 استفاده کرده‌ایم، اما اگر نیازی به رفع ابهام نباشد، این روش کمی طولانی‌تر است. -Running this code prints the following: +اجرای این کد خروجی زیر را چاپ می‌کند: ```console {{#include ../listings/ch20-advanced-features/listing-20-19/output.txt}} ``` -Because the `fly` method takes a `self` parameter, if we had two _types_ that -both implement one _trait_, Rust could figure out which implementation of a -trait to use based on the type of `self`. +از آنجا که متد `fly` یک پارامتر `self` می‌گیرد، اگر دو _نوع_ داشته باشیم که یک _trait_ را پیاده‌سازی کنند، راست می‌تواند بر اساس نوع `self` مشخص کند که کدام پیاده‌سازی trait را باید استفاده کند. -However, associated functions that are not methods don’t have a `self` -parameter. When there are multiple types or traits that define non-method -functions with the same function name, Rust doesn’t always know which type you -mean unless you use fully qualified syntax. For example, in Listing 20-20 we -create a trait for an animal shelter that wants to name all baby dogs Spot. We -make an `Animal` trait with an associated non-method function `baby_name`. The -`Animal` trait is implemented for the struct `Dog`, on which we also provide an -associated non-method function `baby_name` directly. +با این حال، توابع وابسته‌ای (associated functions) که متد نیستند، پارامتر `self` ندارند. زمانی که چندین نوع یا `trait` توابع غیرمتدی با نام یکسان تعریف می‌کنند، Rust همیشه نمی‌تواند تشخیص دهد که منظور شما کدام نوع است، مگر آن‌که از نحوی به‌نام *fully qualified syntax* استفاده کنید. برای مثال، در لیستینگ 20-20 یک `trait` برای یک پناهگاه حیوانات ایجاد می‌کنیم که می‌خواهد نام تمام توله‌سگ‌ها را Spot بگذارد. یک `trait` به‌نام `Animal` تعریف می‌کنیم که شامل یک تابع وابسته غیرمتدی `baby_name` است. این `trait` برای `struct`ی به‌نام `Dog` پیاده‌سازی می‌شود، و بر روی خود `Dog` نیز مستقیماً یک تابع وابسته غیرمتدی به‌نام `baby_name` ارائه می‌دهیم. -<Listing number="20-20" file-name="src/main.rs" caption="A trait with an associated function and a type with an associated function of the same name that also implements the trait"> +<Listing number="20-20" file-name="src/main.rs" caption="یک trait با یک تابع مرتبط و یک نوع با یک تابع مرتبط با همان نام که همچنین trait را پیاده‌سازی می‌کند"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-20/src/main.rs}} @@ -263,26 +163,17 @@ associated non-method function `baby_name` directly. </Listing> -We implement the code for naming all puppies Spot in the `baby_name` associated -function that is defined on `Dog`. The `Dog` type also implements the trait -`Animal`, which describes characteristics that all animals have. Baby dogs are -called puppies, and that is expressed in the implementation of the `Animal` -trait on `Dog` in the `baby_name` function associated with the `Animal` trait. +ما کدی برای نام‌گذاری تمام سگ‌های کوچک به نام Spot در تابع مرتبط `baby_name` که بر روی `Dog` تعریف شده است، پیاده‌سازی می‌کنیم. نوع `Dog` همچنین trait `Animal` را پیاده‌سازی می‌کند، که ویژگی‌هایی که تمام حیوانات دارند را توصیف می‌کند. سگ‌های کوچک به نام puppy شناخته می‌شوند و این در پیاده‌سازی trait `Animal` بر روی `Dog` در تابع `baby_name` مرتبط با trait `Animal` بیان شده است. -In `main`, we call the `Dog::baby_name` function, which calls the associated -function defined on `Dog` directly. This code prints the following: +در تابع `main`، ما تابع `Dog::baby_name` را فراخوانی می‌کنیم، که تابع مرتبط تعریف شده بر روی `Dog` را مستقیماً فراخوانی می‌کند. این کد خروجی زیر را چاپ می‌کند: ```console {{#include ../listings/ch20-advanced-features/listing-20-20/output.txt}} ``` -This output isn’t what we wanted. We want to call the `baby_name` function that -is part of the `Animal` trait that we implemented on `Dog` so the code prints -`A baby dog is called a puppy`. The technique of specifying the trait name that -we used in Listing 20-19 doesn’t help here; if we change `main` to the code in -Listing 20-21, we’ll get a compilation error. +این خروجی آن چیزی نیست که ما می‌خواستیم. ما می‌خواهیم تابع `baby_name` که بخشی از trait `Animal` است و بر روی `Dog` پیاده‌سازی شده است را فراخوانی کنیم تا کد `A baby dog is called a puppy` را چاپ کند. تکنیکی که در فهرست 20-19 برای مشخص کردن نام trait استفاده کردیم، اینجا کمکی نمی‌کند. اگر `main` را به کد موجود در فهرست 20-21 تغییر دهیم، خطای کامپایل دریافت خواهیم کرد. -<Listing number="20-21" file-name="src/main.rs" caption="Attempting to call the `baby_name` function from the `Animal` trait, but Rust doesn’t know which implementation to use"> +<Listing number="20-21" file-name="src/main.rs" caption="تلاش برای فراخوانی تابع `baby_name` از trait `Animal`، اما راست نمی‌داند که از کدام پیاده‌سازی استفاده کند"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-21/src/main.rs:here}} @@ -290,20 +181,15 @@ Listing 20-21, we’ll get a compilation error. </Listing> -Because `Animal::baby_name` doesn’t have a `self` parameter, and there could be -other types that implement the `Animal` trait, Rust can’t figure out which -implementation of `Animal::baby_name` we want. We’ll get this compiler error: +از آنجا که `Animal::baby_name` پارامتر `self` ندارد، و ممکن است انواع دیگری وجود داشته باشند که trait `Animal` را پیاده‌سازی کرده باشند، راست نمی‌تواند تشخیص دهد که کدام پیاده‌سازی از `Animal::baby_name` مورد نظر ما است. در نتیجه این خطای کامپایلر را دریافت خواهیم کرد: ```console {{#include ../listings/ch20-advanced-features/listing-20-21/output.txt}} ``` -To disambiguate and tell Rust that we want to use the implementation of -`Animal` for `Dog` as opposed to the implementation of `Animal` for some other -type, we need to use fully qualified syntax. Listing 20-22 demonstrates how to -use fully qualified syntax. +برای رفع ابهام و مشخص کردن اینکه ما می‌خواهیم از پیاده‌سازی trait `Animal` برای `Dog` استفاده کنیم، به جای پیاده‌سازی trait `Animal` برای نوع دیگری، باید از fully qualified syntax استفاده کنیم. فهرست 20-22 نشان می‌دهد چگونه از fully qualified syntax استفاده کنیم. -<Listing number="20-22" file-name="src/main.rs" caption="Using fully qualified syntax to specify that we want to call the `baby_name` function from the `Animal` trait as implemented on `Dog`"> +<Listing number="20-22" file-name="src/main.rs" caption="استفاده از fully qualified syntax برای مشخص کردن اینکه می‌خواهیم تابع `baby_name` از trait `Animal` که بر روی `Dog` پیاده‌سازی شده است، فراخوانی کنیم"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-22/src/main.rs:here}} @@ -311,47 +197,29 @@ use fully qualified syntax. </Listing> -We’re providing Rust with a type annotation within the angle brackets, which -indicates we want to call the `baby_name` method from the `Animal` trait as -implemented on `Dog` by saying that we want to treat the `Dog` type as an -`Animal` for this function call. This code will now print what we want: +ما با استفاده از یک اعلان نوع در داخل angle brackets به راست می‌گوییم که می‌خواهیم متد `baby_name` از trait `Animal` که بر روی `Dog` پیاده‌سازی شده است، فراخوانی شود، با این کار مشخص می‌کنیم که می‌خواهیم نوع `Dog` را برای این فراخوانی تابع به‌عنوان یک `Animal` در نظر بگیریم. این کد اکنون خروجی مورد نظر ما را چاپ می‌کند: ```console {{#include ../listings/ch20-advanced-features/listing-20-22/output.txt}} ``` -In general, fully qualified syntax is defined as follows: +به طور کلی، fully qualified syntax به صورت زیر تعریف می‌شود: ```rust,ignore <Type as Trait>::function(receiver_if_method, next_arg, ...); ``` -For associated functions that aren’t methods, there would not be a `receiver`: -there would only be the list of other arguments. You could use fully qualified -syntax everywhere that you call functions or methods. However, you’re allowed -to omit any part of this syntax that Rust can figure out from other information -in the program. You only need to use this more verbose syntax in cases where -there are multiple implementations that use the same name and Rust needs help -to identify which implementation you want to call. +برای توابع مرتبطی که متد نیستند، `receiver` وجود نخواهد داشت: فقط لیستی از آرگومان‌های دیگر خواهد بود. شما می‌توانید fully qualified syntax را در هر جایی که توابع یا متدها را فراخوانی می‌کنید، استفاده کنید. با این حال، مجاز هستید هر بخشی از این سینتکس را که راست می‌تواند از اطلاعات دیگر برنامه تشخیص دهد، حذف کنید. شما فقط در مواردی که چندین پیاده‌سازی با نام یکسان وجود دارد و راست به کمک نیاز دارد تا مشخص کند کدام پیاده‌سازی را می‌خواهید فراخوانی کنید، نیاز به استفاده از این سینتکس دقیق‌تر دارید. <!-- Old link, do not remove --> <a id="using-supertraits-to-require-one-traits-functionality-within-another-trait"></a> -### Using Supertraits +### استفاده از Supertraitها -Sometimes you might write a trait definition that depends on another trait: for -a type to implement the first trait, you want to require that type to also -implement the second trait. You would do this so that your trait definition can -make use of the associated items of the second trait. The trait your trait -definition is relying on is called a _supertrait_ of your trait. +گاهی ممکن است بخواهید یک تعریف `trait` بنویسید که به `trait` دیگری وابسته باشد: برای آن‌که یک نوع بتواند `trait` اول را پیاده‌سازی کند، لازم است آن نوع همچنین `trait` دوم را نیز پیاده‌سازی کرده باشد. این کار را برای آن انجام می‌دهید که تعریف `trait` شما بتواند از اعضای وابسته‌ی (associated items) `trait` دوم استفاده کند. `trait`ای که تعریف شما به آن وابسته است، *supertrait* نامیده می‌شود. -For example, let’s say we want to make an `OutlinePrint` trait with an -`outline_print` method that will print a given value formatted so that it’s -framed in asterisks. That is, given a `Point` struct that implements the -standard library trait `Display` to result in `(x, y)`, when we call -`outline_print` on a `Point` instance that has `1` for `x` and `3` for `y`, it -should print the following: +برای مثال، فرض کنید می‌خواهیم یک `trait` به‌نام `OutlinePrint` ایجاد کنیم با یک متد `outline_print` که مقدار داده‌شده را به‌صورتی فرمت‌شده چاپ می‌کند که درون قاب ستاره‌ای قرار گیرد. یعنی، اگر یک `struct` به‌نام `Point` داشته باشیم که `trait` استاندارد `Display` را پیاده‌سازی کرده باشد و خروجی آن `(x, y)` باشد، وقتی `outline_print` را روی یک نمونه از `Point` با `x` برابر با `1` و `y` برابر با `3` فراخوانی کنیم، باید چیزی مشابه زیر چاپ شود: ```text ********** @@ -361,15 +229,9 @@ should print the following: ********** ``` -In the implementation of the `outline_print` method, we want to use the -`Display` trait’s functionality. Therefore, we need to specify that the -`OutlinePrint` trait will work only for types that also implement `Display` and -provide the functionality that `OutlinePrint` needs. We can do that in the -trait definition by specifying `OutlinePrint: Display`. This technique is -similar to adding a trait bound to the trait. Listing 20-23 shows an -implementation of the `OutlinePrint` trait. +در پیاده‌سازی متد `outline_print`، می‌خواهیم از قابلیت‌های trait `Display` استفاده کنیم. بنابراین، نیاز داریم مشخص کنیم که trait `OutlinePrint` فقط برای انواعی کار خواهد کرد که همچنین trait `Display` را پیاده‌سازی کرده باشند و قابلیت‌های مورد نیاز `OutlinePrint` را ارائه دهند. می‌توانیم این کار را در تعریف trait با مشخص کردن `OutlinePrint: Display` انجام دهیم. این تکنیک شبیه به اضافه کردن یک محدودیت trait به trait است. فهرست 20-23 یک پیاده‌سازی از trait `OutlinePrint` را نشان می‌دهد. -<Listing number="20-23" file-name="src/main.rs" caption="Implementing the `OutlinePrint` trait that requires the functionality from `Display`"> +<Listing number="20-23" file-name="src/main.rs" caption="پیاده‌سازی trait `OutlinePrint` که نیاز به قابلیت‌های `Display` دارد"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-23/src/main.rs:here}} @@ -377,15 +239,9 @@ implementation of the `OutlinePrint` trait. </Listing> -Because we’ve specified that `OutlinePrint` requires the `Display` trait, we -can use the `to_string` function that is automatically implemented for any type -that implements `Display`. If we tried to use `to_string` without adding a -colon and specifying the `Display` trait after the trait name, we’d get an -error saying that no method named `to_string` was found for the type `&Self` in -the current scope. +از آنجایی که مشخص کرده‌ایم که `OutlinePrint` به trait `Display` نیاز دارد، می‌توانیم از تابع `to_string` استفاده کنیم که به طور خودکار برای هر نوعی که `Display` را پیاده‌سازی کرده باشد، پیاده‌سازی شده است. اگر سعی کنیم `to_string` را بدون اضافه کردن دو نقطه و مشخص کردن trait `Display` بعد از نام trait استفاده کنیم، خطایی دریافت خواهیم کرد که می‌گوید هیچ متدی به نام `to_string` برای نوع `&Self` در محدوده فعلی یافت نشد. -Let’s see what happens when we try to implement `OutlinePrint` on a type that -doesn’t implement `Display`, such as the `Point` struct: +حالا ببینیم چه اتفاقی می‌افتد اگر بخواهیم `OutlinePrint` را برای یک نوعی که `Display` را پیاده‌سازی نکرده است، مانند ساختار `Point`، پیاده‌سازی کنیم: <Listing file-name="src/main.rs"> @@ -395,14 +251,13 @@ doesn’t implement `Display`, such as the `Point` struct: </Listing> -We get an error saying that `Display` is required but not implemented: +خطایی دریافت می‌کنیم که می‌گوید `Display` مورد نیاز است ولی پیاده‌سازی نشده است: ```console {{#include ../listings/ch20-advanced-features/no-listing-02-impl-outlineprint-for-point/output.txt}} ``` -To fix this, we implement `Display` on `Point` and satisfy the constraint that -`OutlinePrint` requires, like so: +برای رفع این مشکل، باید `Display` را برای `Point` پیاده‌سازی کنیم و محدودیت مورد نیاز `OutlinePrint` را برآورده کنیم، به این صورت: <Listing file-name="src/main.rs"> @@ -412,36 +267,19 @@ To fix this, we implement `Display` on `Point` and satisfy the constraint that </Listing> -Then, implementing the `OutlinePrint` trait on `Point` will compile -successfully, and we can call `outline_print` on a `Point` instance to display -it within an outline of asterisks. +سپس، پیاده‌سازی `trait` به‌نام `OutlinePrint` برای `Point` با موفقیت کامپایل خواهد شد، و می‌توانیم متد `outline_print` را روی یک نمونه از `Point` فراخوانی کنیم تا آن را درون یک قاب ستاره‌ای نمایش دهد. <!-- Old link, do not remove --> <a id="using-the-newtype-pattern-to-implement-external-traits-on-external-types"></a> -### Using the Newtype Pattern to Implement External Traits - -In [“Implementing a Trait on a Type”][implementing-a-trait-on-a-type]<!-- -ignore --> in Chapter 10, we mentioned the orphan rule that states we’re only -allowed to implement a trait on a type if either the trait or the type, or -both, are local to our crate. It’s possible to get around this restriction -using the _newtype pattern_, which involves creating a new type in a tuple -struct. (We covered tuple structs in [“Using Tuple Structs Without Named Fields -to Create Different Types”][tuple-structs]<!-- ignore --> in Chapter 5.) The -tuple struct will have one field and be a thin wrapper around the type for -which we want to implement a trait. Then the wrapper type is local to our -crate, and we can implement the trait on the wrapper. _Newtype_ is a term that -originates from the Haskell programming language. There is no runtime -performance penalty for using this pattern, and the wrapper type is elided at -compile time. - -As an example, let’s say we want to implement `Display` on `Vec<T>`, which the -orphan rule prevents us from doing directly because the `Display` trait and the -`Vec<T>` type are defined outside our crate. We can make a `Wrapper` struct -that holds an instance of `Vec<T>`; then we can implement `Display` on -`Wrapper` and use the `Vec<T>` value, as shown in Listing 20-24. - -<Listing number="20-24" file-name="src/main.rs" caption="Creating a `Wrapper` type around `Vec<String>` to implement `Display`"> +### استفاده از الگوی Newtype برای پیاده‌سازی Traitهای خارجی + +در بخش [«پیاده‌سازی یک Trait برای یک نوع»][implementing-a-trait-on-a-type]<!-- ignore --> در فصل ۱۰، به قانونی به نام *قانون یتیم* (orphan rule) اشاره کردیم که می‌گوید تنها در صورتی اجازه داریم یک `trait` را برای یک نوع پیاده‌سازی کنیم که یا خود `trait`، یا آن نوع، یا هر دو، محلی (local) به `crate` ما باشند. می‌توان با استفاده از الگوی _newtype_ این محدودیت را دور زد. این الگو شامل ایجاد یک نوع جدید در قالب یک `tuple struct` است. (در فصل ۵ در بخش [«استفاده از Tuple Structها بدون فیلدهای نام‌گذاری‌شده برای ایجاد انواع مختلف»][tuple-structs]<!-- ignore --> به این موضوع پرداختیم.) +این `tuple struct` فقط یک فیلد خواهد داشت و در واقع یک بسته‌بندی نازک روی نوعی است که می‌خواهیم `trait` را برای آن پیاده‌سازی کنیم. از آن‌جایی که نوع بسته‌بندی‌شده محلی به `crate` ما خواهد بود، می‌توانیم `trait` مورد نظر را روی آن پیاده‌سازی کنیم. واژه‌ی _newtype_ از زبان برنامه‌نویسی Haskell گرفته شده است. استفاده از این الگو هیچ‌گونه هزینه‌ای در زمان اجرا ندارد، زیرا نوع بسته‌بندی‌شده در زمان کامپایل حذف می‌شود. + +به‌عنوان مثال، فرض کنید می‌خواهیم `Display` را روی `Vec<T>` پیاده‌سازی کنیم، که قانون orphan مانع انجام این کار به‌صورت مستقیم می‌شود زیرا trait `Display` و نوع `Vec<T>` خارج از crate ما تعریف شده‌اند. می‌توانیم یک ساختار `Wrapper` بسازیم که شامل یک نمونه از `Vec<T>` باشد؛ سپس می‌توانیم `Display` را روی `Wrapper` پیاده‌سازی کنیم و از مقدار `Vec<T>` استفاده کنیم، همانطور که در فهرست 20-24 نشان داده شده است. + +<Listing number="20-24" file-name="src/main.rs" caption="ایجاد نوع `Wrapper` دور `Vec<String>` برای پیاده‌سازی `Display`"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-24/src/main.rs}} @@ -449,24 +287,11 @@ that holds an instance of `Vec<T>`; then we can implement `Display` on </Listing> -The implementation of `Display` uses `self.0` to access the inner `Vec<T>` -because `Wrapper` is a tuple struct and `Vec<T>` is the item at index 0 in the -tuple. Then we can use the functionality of the `Display` trait on `Wrapper`. - -The downside of using this technique is that `Wrapper` is a new type, so it -doesn’t have the methods of the value it’s holding. We would have to implement -all the methods of `Vec<T>` directly on `Wrapper` such that the methods -delegate to `self.0`, which would allow us to treat `Wrapper` exactly like a -`Vec<T>`. If we wanted the new type to have every method the inner type has, -implementing the `Deref` trait on the `Wrapper` to return the inner type would -be a solution (we discussed implementing the `Deref` trait in [“Treating Smart -Pointers Like Regular References with `Deref`”][smart-pointer-deref]<!-- ignore ---> in Chapter 15). If we didn’t want the `Wrapper` type to have all the -methods of the inner type—for example, to restrict the `Wrapper` type’s -behavior—we would have to implement just the methods we do want manually. - -This newtype pattern is also useful even when traits are not involved. Let’s -switch focus and look at some advanced ways to interact with Rust’s type system. +پیاده‌سازی `Display` از `self.0` برای دسترسی به `Vec<T>` داخلی استفاده می‌کند، زیرا `Wrapper` یک `tuple struct` است و `Vec<T>` در موقعیت اندیس ۰ این `tuple` قرار دارد. سپس می‌توانیم از قابلیت‌های `trait` `Display` روی `Wrapper` استفاده کنیم. + +نکته‌ی منفی در استفاده از این تکنیک این است که `Wrapper` یک نوع جدید است، بنابراین متدهای نوعی که درون خود نگه می‌دارد را ندارد. باید تمام متدهای `Vec<T>` را مستقیماً روی `Wrapper` پیاده‌سازی کنیم به‌گونه‌ای که این متدها به `self.0` ارجاع دهند؛ این کار به ما اجازه می‌دهد که با `Wrapper` مانند یک `Vec<T>` رفتار کنیم. اگر بخواهیم نوع جدید همه‌ی متدهای نوع درونی را داشته باشد، پیاده‌سازی `trait` `Deref` برای `Wrapper` که نوع درونی را بازمی‌گرداند، یک راه‌حل خواهد بود (در فصل ۱۵ در بخش [«رفتار دادن به Smart Pointerها مانند رفرنس‌های معمولی با `Deref`»][smart-pointer-deref]<!-- ignore --> درباره‌ی پیاده‌سازی `Deref` صحبت کردیم). اما اگر نخواهیم نوع `Wrapper` همه‌ی متدهای نوع درونی را داشته باشد—برای مثال، برای محدود کردن رفتار نوع `Wrapper`—باید فقط متدهایی را که نیاز داریم، به‌صورت دستی پیاده‌سازی کنیم. + +این الگوی newtype حتی زمانی که traits درگیر نیستند نیز مفید است. حالا بیایید تمرکز خود را تغییر دهیم و به برخی از روش‌های پیشرفته برای تعامل با سیستم نوع Rust بپردازیم. [newtype]: ch20-02-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits [implementing-a-trait-on-a-type]: ch10-02-traits.html#implementing-a-trait-on-a-type diff --git a/src/ch20-03-advanced-types.md b/src/ch20-03-advanced-types.md index fbf25bb0d6..a21e3000ce 100644 --- a/src/ch20-03-advanced-types.md +++ b/src/ch20-03-advanced-types.md @@ -1,86 +1,51 @@ -## Advanced Types - -The Rust type system has some features that we’ve so far mentioned but haven’t -yet discussed. We’ll start by discussing newtypes in general as we examine why -newtypes are useful as types. Then we’ll move on to type aliases, a feature -similar to newtypes but with slightly different semantics. We’ll also discuss -the `!` type and dynamically sized types. - -### Using the Newtype Pattern for Type Safety and Abstraction - -This section assumes you’ve read the earlier section [“Using the Newtype Pattern -to Implement External Traits”][using-the-newtype-pattern]<!-- -ignore -->. The newtype pattern is also useful for tasks beyond those we’ve -discussed so far, including statically enforcing that values are never confused -and indicating the units of a value. You saw an example of using newtypes to -indicate units in Listing 20-16: recall that the `Millimeters` and `Meters` -structs wrapped `u32` values in a newtype. If we wrote a function with a -parameter of type `Millimeters`, we wouldn’t be able to compile a program that -accidentally tried to call that function with a value of type `Meters` or a -plain `u32`. - -We can also use the newtype pattern to abstract away some implementation -details of a type: the new type can expose a public API that is different from -the API of the private inner type. - -Newtypes can also hide internal implementation. For example, we could provide a -`People` type to wrap a `HashMap<i32, String>` that stores a person’s ID -associated with their name. Code using `People` would only interact with the -public API we provide, such as a method to add a name string to the `People` -collection; that code wouldn’t need to know that we assign an `i32` ID to names -internally. The newtype pattern is a lightweight way to achieve encapsulation to -hide implementation details, which we discussed in [“Encapsulation that Hides -Implementation Details”][encapsulation-that-hides-implementation-details]<!-- -ignore --> in Chapter 18. - -### Creating Type Synonyms with Type Aliases - -Rust provides the ability to declare a _type alias_ to give an existing type -another name. For this we use the `type` keyword. For example, we can create -the alias `Kilometers` to `i32` like so: +## انواع (Typeهای) پیشرفته + +سیستم نوع‌بندی Rust شامل ویژگی‌هایی است که تاکنون فقط به آن‌ها اشاره کرده‌ایم و هنوز به‌طور کامل مورد بحث قرار نگرفته‌اند. ابتدا به بررسی الگوی newtype می‌پردازیم تا بفهمیم چرا این الگو به‌عنوان انواع مفید است. سپس به aliasهای نوع می‌پردازیم، که ویژگی مشابهی با newtype دارند اما با تفاوت‌هایی در معناشناسی. همچنین، نوع `!` و انواع پویا (dynamically sized types) را نیز بررسی خواهیم کرد. + +### استفاده از الگوی Newtype برای ایمنی نوع و انتزاع + +این بخش فرض می‌کند که پیش‌تر بخش [«استفاده از الگوی Newtype برای پیاده‌سازی Traitهای خارجی»][using-the-newtype-pattern]<!-- ignore --> را خوانده‌اید. الگوی newtype برای کارهایی فراتر از آن‌چه تاکنون بحث کردیم نیز مفید است، از جمله اعمال محدودیت‌های ایستا (statically) برای جلوگیری از اشتباه گرفتن مقادیر و مشخص‌کردن واحد یک مقدار. مثالی از استفاده‌ی newtype برای مشخص‌کردن واحدها را در لیستینگ 20-16 مشاهده کردید: به خاطر بیاورید که ساختارهای `Millimeters` و `Meters` مقادیر `u32` را درون یک newtype می‌پیچیدند. اگر تابعی با پارامتری از نوع `Millimeters` بنویسیم، برنامه‌ای که به‌اشتباه سعی کند آن تابع را با مقداری از نوع `Meters` یا یک `u32` معمولی فراخوانی کند، کامپایل نخواهد شد. + +ما همچنین می‌توانیم از الگوی newtype برای انتزاع جزئیات پیاده‌سازی یک نوع استفاده کنیم: نوع جدید می‌تواند یک API عمومی ارائه دهد که با API نوع داخلی خصوصی متفاوت است. + +الگوی newtype همچنین می‌تواند پیاده‌سازی داخلی را پنهان کند. برای مثال، می‌توانیم یک نوع `People` ارائه دهیم که یک `HashMap<i32, String>` را در خود بپیچد؛ این ساختار شناسه‌ی هر فرد را با نام او نگه می‌دارد. کدی که از `People` استفاده می‌کند، تنها با API عمومی‌ای که ما ارائه می‌دهیم تعامل خواهد داشت، مانند متدی برای افزودن یک رشته‌ی نام به مجموعه‌ی `People`؛ این کد نیازی ندارد بداند که ما به‌صورت داخلی برای نام‌ها یک شناسه‌ی `i32` اختصاص می‌دهیم. الگوی newtype روشی سبک‌وزن برای رسیدن به کپسوله‌سازی و پنهان‌سازی جزئیات پیاده‌سازی است؛ موضوعی که در فصل ۱۸، در بخش [«کپسوله‌سازی برای پنهان‌سازی جزئیات پیاده‌سازی»][encapsulation-that-hides-implementation-details]<!-- ignore --> بررسی کردیم. + + +### ایجاد مترادف‌های نوع با استفاده از Type Aliases + +Rust قابلیت تعریف _alias نوع_ را برای ارائه یک نام دیگر برای یک نوع موجود فراهم می‌کند. برای این کار از کلمه‌کلیدی `type` استفاده می‌کنیم. به‌عنوان مثال، می‌توانیم alias‌ای به نام `Kilometers` برای نوع `i32` ایجاد کنیم: ```rust {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-04-kilometers-alias/src/main.rs:here}} ``` -Now the alias `Kilometers` is a _synonym_ for `i32`; unlike the `Millimeters` -and `Meters` types we created in Listing 20-16, `Kilometers` is not a separate, -new type. Values that have the type `Kilometers` will be treated the same as -values of type `i32`: +اکنون نام مستعار `Kilometers` یک *مترادف* برای نوع `i32` است؛ برخلاف انواع `Millimeters` و `Meters` که در لیستینگ 20-16 ایجاد کردیم، `Kilometers` یک نوع جداگانه و جدید نیست. مقادیری که نوع آن‌ها `Kilometers` باشد، همانند مقادیر نوع `i32` در نظر گرفته می‌شوند: ```rust {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-04-kilometers-alias/src/main.rs:there}} ``` -Because `Kilometers` and `i32` are the same type, we can add values of both -types and we can pass `Kilometers` values to functions that take `i32` -parameters. However, using this method, we don’t get the type-checking benefits -that we get from the newtype pattern discussed earlier. In other words, if we -mix up `Kilometers` and `i32` values somewhere, the compiler will not give us -an error. +از آن‌جا که `Kilometers` و `i32` از نظر نوع یکسان هستند، می‌توانیم مقادیری از هر دو نوع را با هم جمع کنیم و همچنین می‌توانیم مقادیر `Kilometers` را به توابعی بدهیم که پارامترهایی از نوع `i32` دارند. +با این حال، با استفاده از این روش، مزایای بررسی نوع را که از الگوی newtype (الگوی نوع جدید) به‌دست می‌آید، نخواهیم داشت. +به‌عبارت دیگر، اگر در جایی `Kilometers` و `i32` را با هم اشتباه بگیریم، کامپایلر به ما خطا نخواهد داد. -The main use case for type synonyms is to reduce repetition. For example, we -might have a lengthy type like this: +استفاده اصلی از مترادف‌های نوع برای کاهش تکرار است. به‌عنوان مثال، ممکن است یک نوع طولانی مانند این داشته باشیم: ```rust,ignore Box<dyn Fn() + Send + 'static> ``` -Writing this lengthy type in function signatures and as type annotations all -over the code can be tiresome and error prone. Imagine having a project full of -code like that in Listing 20-25. +نوشتن این نوع طولانی در امضاهای توابع و به‌عنوان توضیحات نوع در سراسر کد می‌تواند خسته‌کننده و مستعد خطا باشد. تصور کنید پروژه‌ای پر از کدی مانند آنچه در فهرست 20-25 نشان داده شده است. -<Listing number="20-25" caption="Using a long type in many places"> +<فهرست شماره="20-25" عنوان="استفاده از یک نوع طولانی در مکان‌های متعدد"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-25/src/main.rs:here}} ``` -</Listing> +</فهرست> -A type alias makes this code more manageable by reducing the repetition. In -Listing 20-26, we’ve introduced an alias named `Thunk` for the verbose type and -can replace all uses of the type with the shorter alias `Thunk`. +یک **نوع مستعار** (type alias) این کد را با کاهش تکرار خواناتر و مدیریت‌پذیرتر می‌کند. در فهرست 20-26، ما یک مستعار به نام `Thunk` برای نوع طولانی معرفی کرده‌ایم و می‌توانیم همه استفاده‌ها از این نوع را با مستعار کوتاه‌تر `Thunk` جایگزین کنیم. <Listing number="20-26" caption="Introducing a type alias, `Thunk`, to reduce repetition"> @@ -88,66 +53,46 @@ can replace all uses of the type with the shorter alias `Thunk`. {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-26/src/main.rs:here}} ``` -</Listing> +</فهرست> -This code is much easier to read and write! Choosing a meaningful name for a -type alias can help communicate your intent as well (_thunk_ is a word for code -to be evaluated at a later time, so it’s an appropriate name for a closure that -gets stored). +این کد بسیار خواناتر و نوشتن آن آسان‌تر است! انتخاب یک نام معنادار برای نوع مستعار می‌تواند به انتقال مقصود شما کمک کند. (برای مثال، _thunk_ کلمه‌ای است که به کدی اشاره دارد که قرار است در آینده اجرا شود، بنابراین برای اشاره به یک closure که ذخیره می‌شود، مناسب است). -Type aliases are also commonly used with the `Result<T, E>` type for reducing -repetition. Consider the `std::io` module in the standard library. I/O -operations often return a `Result<T, E>` to handle situations when operations -fail to work. This library has a `std::io::Error` struct that represents all -possible I/O errors. Many of the functions in `std::io` will be returning -`Result<T, E>` where the `E` is `std::io::Error`, such as these functions in -the `Write` trait: +نوع‌های مستعار همچنین معمولاً با نوع `Result<T, E>` برای کاهش تکرار استفاده می‌شوند. به‌عنوان نمونه، ماژول `std::io` در کتابخانه استاندارد را در نظر بگیرید. عملیات I/O اغلب یک `Result<T, E>` برمی‌گرداند تا مواقعی که عملیات با شکست مواجه می‌شود مدیریت شود. این کتابخانه یک ساختار `std::io::Error` دارد که تمامی خطاهای ممکن در I/O را نمایش می‌دهد. بسیاری از توابع در `std::io` `Result<T, E>` را برمی‌گردانند که در آن `E` برابر با `std::io::Error` است، مانند این توابع در trait `Write`: ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-05-write-trait/src/lib.rs}} ``` -The `Result<..., Error>` is repeated a lot. As such, `std::io` has this type -alias declaration: +عبارت `Result<..., Error>` به دفعات تکرار شده است. به همین دلیل، در ماژول `std::io` یک نوع مستعار (type alias) به این شکل تعریف شده است: ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-06-result-alias/src/lib.rs:here}} ``` -Because this declaration is in the `std::io` module, we can use the fully -qualified alias `std::io::Result<T>`; that is, a `Result<T, E>` with the `E` -filled in as `std::io::Error`. The `Write` trait function signatures end up -looking like this: +از آنجا که این تعریف در ماژول `std::io` قرار دارد، می‌توانیم از نوع مستعار `std::io::Result<T>` استفاده کنیم؛ به این معنی که `Result<T, E>` با مقدار `E` برابر با `std::io::Error` است. امضای توابع موجود در trait `Write` به این شکل خواهد بود: ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-06-result-alias/src/lib.rs:there}} ``` -The type alias helps in two ways: it makes code easier to write _and_ it gives -us a consistent interface across all of `std::io`. Because it’s an alias, it’s -just another `Result<T, E>`, which means we can use any methods that work on -`Result<T, E>` with it, as well as special syntax like the `?` operator. +این نوع مستعار از دو جنبه کمک‌کننده است: نوشتن کد را ساده‌تر می‌کند _و_ یک رابط کاربری یکپارچه در تمام بخش‌های `std::io` فراهم می‌آورد. از آنجا که این یک مستعار است، همچنان یک `Result<T, E>` معمولی است؛ به این معنی که می‌توانیم از تمام متدهایی که روی `Result<T, E>` کار می‌کنند استفاده کنیم، همچنین از نحو خاص مانند عملگر `?`. ### The Never Type That Never Returns -Rust has a special type named `!` that’s known in type theory lingo as the -_empty type_ because it has no values. We prefer to call it the _never type_ -because it stands in the place of the return type when a function will never -return. Here is an example: +Rust دارای یک نوع ویژه به نام `!` است که در نظریه نوع‌ها به عنوان _نوع خالی_ (empty type) شناخته می‌شود، زیرا هیچ مقداری ندارد. ما ترجیح می‌دهیم آن را _نوعی که هرگز بازنمی‌گردد_ (never type) بنامیم، زیرا به‌جای نوع بازگشتی قرار می‌گیرد زمانی که یک تابع هرگز بازنمی‌گردد. به مثال زیر توجه کنید: ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-07-never-type/src/lib.rs:here}} ``` -This code is read as “the function `bar` returns never.” Functions that return -never are called _diverging functions_. We can’t create values of the type `!`, -so `bar` can never possibly return. +این کد به این صورت خوانده می‌شود: «تابع `bar` هرگز باز نمی‌گردد.» +تابع‌هایی که هرگز باز نمی‌گردند، *تابع‌های واگرا (diverging functions)* نام دارند. +ما نمی‌توانیم مقداری از نوع `!` بسازیم، بنابراین `bar` نمی‌تواند هیچ‌گاه مقداری بازگرداند. -But what use is a type you can never create values for? Recall the code from -Listing 2-5, part of the number-guessing game; we’ve reproduced a bit of it -here in Listing 20-27. +اما چه فایده‌ای دارد نوعی که نمی‌توانید هیچ مقداری از آن بسازید؟ +کدی را به یاد آورید که در لیست ۲-۵، بخشی از بازی حدس عدد بود؛ ما بخشی از آن را این‌جا در لیست 20-27 بازتولید کرده‌ایم. -<Listing number="20-27" caption="A `match` with an arm that ends in `continue`"> +<Listing number="20-27" caption="یک `match` با بازوی پایانی که به `continue` ختم می‌شود"> ```rust,ignore {{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-05/src/main.rs:ch19}} @@ -155,141 +100,105 @@ here in Listing 20-27. </Listing> -At the time, we skipped over some details in this code. In [“The `match` -Control Flow Construct”][the-match-control-flow-construct]<!-- ignore --> in -Chapter 6, we discussed that `match` arms must all return the same type. So, -for example, the following code doesn’t work: +در آن زمان، از برخی جزئیات در این کد عبور کردیم. +در بخش [«ساختار کنترلی `match`»][the-match-control-flow-construct]<!-- ignore --> در فصل ۶، +بحث کردیم که تمام بازوهای `match` باید یک نوع بازگشتی یکسان داشته باشند. +برای مثال، کد زیر کار نمی‌کند: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-08-match-arms-different-types/src/main.rs:here}} ``` -The type of `guess` in this code would have to be an integer _and_ a string, -and Rust requires that `guess` have only one type. So what does `continue` -return? How were we allowed to return a `u32` from one arm and have another arm -that ends with `continue` in Listing 20-27? +نوع `guess` در این کد باید هم عدد صحیح (_integer_) و هم رشته (_string_) باشد، و Rust نیاز دارد که `guess` تنها یک نوع داده داشته باشد. بنابراین، دستور `continue` چه مقداری را برمی‌گرداند؟ چگونه توانستیم از یک بازو مقدار `u32` بازگردانیم و در بازوی دیگر `continue` را قرار دهیم که در لیست ۲۰-۲۷ آورده شده است؟ -As you might have guessed, `continue` has a `!` value. That is, when Rust -computes the type of `guess`, it looks at both match arms, the former with a -value of `u32` and the latter with a `!` value. Because `!` can never have a -value, Rust decides that the type of `guess` is `u32`. +همان‌طور که احتمالاً حدس زده‌اید، دستور `continue` دارای نوع `!` است. یعنی، وقتی Rust نوع `guess` را محاسبه می‌کند، به هر دو بازوی `match` نگاه می‌کند: بازوی اول مقداری از نوع `u32` دارد و بازوی دوم مقداری از نوع `!`. از آنجا که `!` نمی‌تواند هیچ مقداری داشته باشد، Rust نتیجه‌گیری می‌کند که نوع `guess` برابر با `u32` است. -The formal way of describing this behavior is that expressions of type `!` can -be coerced into any other type. We’re allowed to end this `match` arm with -`continue` because `continue` doesn’t return a value; instead, it moves control -back to the top of the loop, so in the `Err` case, we never assign a value to -`guess`. +روش رسمی برای توصیف این رفتار این است که عبارت‌های نوع `!` می‌توانند به هر نوع دیگری تبدیل شوند (_coerce_). ما می‌توانیم بازوی `match` را با دستور `continue` پایان دهیم زیرا `continue` مقداری باز نمی‌گرداند؛ بلکه کنترل را به بالای حلقه بازمی‌گرداند، بنابراین در حالت `Err`، هیچ مقداری به `guess` اختصاص داده نمی‌شود. -The never type is useful with the `panic!` macro as well. Recall the `unwrap` -function that we call on `Option<T>` values to produce a value or panic with -this definition: +نوع `!` در ماکرو `panic!` نیز مفید است. به یاد بیاورید تابع `unwrap` که روی مقادیر `Option<T>` فراخوانی می‌کنیم تا مقداری را تولید کند یا با استفاده از این تعریف متوقف شود: ```rust,ignore {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-09-unwrap-definition/src/lib.rs:here}} ``` -In this code, the same thing happens as in the `match` in Listing 20-27: Rust -sees that `val` has the type `T` and `panic!` has the type `!`, so the result -of the overall `match` expression is `T`. This code works because `panic!` -doesn’t produce a value; it ends the program. In the `None` case, we won’t be -returning a value from `unwrap`, so this code is valid. +در این کد، همان چیزی که در `match` لیست ۲۰-۲۷ رخ داد اتفاق می‌افتد: Rust می‌بیند که `val` از نوع `T` است و `panic!` از نوع `!` است، بنابراین نتیجه کلی عبارت `match` برابر با `T` است. این کد کار می‌کند زیرا `panic!` هیچ مقداری تولید نمی‌کند؛ بلکه برنامه را متوقف می‌کند. در حالت `None`، ما مقداری از `unwrap` بازنمی‌گردانیم، بنابراین این کد معتبر است. -One final expression that has the type `!` is a `loop`: +یک عبارت نهایی که نوع `!` دارد، حلقه `loop` است: ```rust,ignore {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-10-loop-returns-never/src/main.rs:here}} ``` -Here, the loop never ends, so `!` is the value of the expression. However, this -wouldn’t be true if we included a `break`, because the loop would terminate -when it got to the `break`. +اینجا، حلقه هیچ‌گاه متوقف نمی‌شود، بنابراین نوع `!` مقدار عبارت خواهد بود. با این حال، اگر `break` درون حلقه باشد، این موضوع درست نخواهد بود، زیرا حلقه وقتی به `break` می‌رسد، متوقف می‌شود. -### Dynamically Sized Types and the `Sized` Trait +فایل تکمیل شد. -Rust needs to know certain details about its types, such as how much space to -allocate for a value of a particular type. This leaves one corner of its type -system a little confusing at first: the concept of _dynamically sized types_. -Sometimes referred to as _DSTs_ or _unsized types_, these types let us write -code using values whose size we can know only at runtime. +### typeها با اندازه پویا (dynamic) و ویژگی `Sized` -Let’s dig into the details of a dynamically sized type called `str`, which -we’ve been using throughout the book. That’s right, not `&str`, but `str` on -its own, is a DST. In many cases, such as when storing text entered by a user, -we can’t know how long the string is until runtime. That means we can’t create -a variable of type `str`, nor can we take an argument of type `str`. Consider -the following code, which does not work: +بیایید وارد جزئیات نوعی با اندازه‌ی پویا (Dynamically Sized Type یا DST) به نام `str` شویم +که در طول این کتاب بارها از آن استفاده کرده‌ایم. +بله درست است، منظورمان `&str` نیست، بلکه خود `str` به‌تنهایی یک DST است. +در بسیاری از موارد، مانند زمانی که متنی توسط کاربر وارد می‌شود، +ما نمی‌دانیم طول رشته چقدر است تا زمانی که برنامه اجرا شود. +این بدان معناست که نمی‌توانیم یک متغیر از نوع `str` ایجاد کنیم +و نه می‌توانیم آرگومانی از نوع `str` دریافت کنیم. +به مثال زیر توجه کنید که کار نمی‌کند: ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-11-cant-create-str/src/main.rs:here}} ``` -Rust needs to know how much memory to allocate for any value of a particular -type, and all values of a type must use the same amount of memory. If Rust -allowed us to write this code, these two `str` values would need to take up the -same amount of space. But they have different lengths: `s1` needs 12 bytes of -storage and `s2` needs 15. This is why it’s not possible to create a variable -holding a dynamically sized type. - -So what do we do? In this case, you already know the answer: we make the types -of `s1` and `s2` a `&str` rather than a `str`. Recall from [“String -Slices”][string-slices]<!-- ignore --> in Chapter 4 that the slice data -structure just stores the starting position and the length of the slice. So, -although a `&T` is a single value that stores the memory address of where the -`T` is located, a `&str` is _two_ values: the address of the `str` and its -length. As such, we can know the size of a `&str` value at compile time: it’s -twice the length of a `usize`. That is, we always know the size of a `&str`, no -matter how long the string it refers to is. In general, this is the way in which -dynamically sized types are used in Rust: they have an extra bit of metadata -that stores the size of the dynamic information. The golden rule of dynamically -sized types is that we must always put values of dynamically sized types behind -a pointer of some kind. - -We can combine `str` with all kinds of pointers: for example, `Box<str>` or -`Rc<str>`. In fact, you’ve seen this before but with a different dynamically -sized type: traits. Every trait is a dynamically sized type we can refer to by -using the name of the trait. In [“Using Trait Objects to Abstract over Shared -Behavior”][using-trait-objects-to-abstract-over-shared-behavior]<!-- ignore --> -in Chapter 18, we mentioned that to use traits as trait objects, we must put -them behind a pointer, such as `&dyn Trait` or `Box<dyn Trait>` (`Rc<dyn -Trait>` would work too). - -To work with DSTs, Rust provides the `Sized` trait to determine whether or not -a type’s size is known at compile time. This trait is automatically implemented -for everything whose size is known at compile time. In addition, Rust -implicitly adds a bound on `Sized` to every generic function. That is, a -generic function definition like this: +Rust نیاز دارد که بداند چقدر حافظه برای هر مقدار از یک نوع خاص تخصیص دهد، و تمام مقادیر یک نوع باید از همان مقدار حافظه استفاده کنند. اگر Rust اجازه می‌داد این کد را بنویسیم، این دو مقدار `str` باید از یک مقدار فضا استفاده می‌کردند. اما آن‌ها طول‌های متفاوتی دارند: `s1` به ۱۲ بایت فضای ذخیره‌سازی نیاز دارد و `s2` به ۱۵ بایت. به همین دلیل است که ایجاد یک متغیر که یک نوع با اندازه دایتانیک داشته باشد ممکن نیست. + +پس چه کار باید بکنیم؟ در این حالت، شما از قبل پاسخ را می‌دانید: +باید نوع‌های `s1` و `s2` را به جای `str`، به `&str` تغییر دهیم. +به یاد دارید از بخش [“برش‌های رشته‌ای”][string-slices]<!-- ignore --> در فصل ۴ +که ساختار داده‌ی slice فقط موقعیت شروع و طول برش را ذخیره می‌کند. +بنابراین، اگرچه یک `&T` یک مقدار منفرد است که آدرس حافظه‌ای که `T` در آن قرار دارد را ذخیره می‌کند، +یک `&str` *دو* مقدار دارد: آدرس `str` و طول آن. + +از این رو، می‌توانیم اندازه‌ی مقدار `&str` را در زمان کامپایل بدانیم: +این اندازه دو برابر طول یک `usize` است. +یعنی، همیشه اندازه‌ی `&str` را می‌دانیم، فارغ از اینکه طول رشته‌ای که به آن اشاره می‌کند چقدر باشد. +به طور کلی، این همان روشی است که نوع‌های با اندازه‌ی پویا در Rust استفاده می‌شوند: +آن‌ها یک قطعه اضافی از فراداده (metadata) دارند که اندازه‌ی اطلاعات پویا را ذخیره می‌کند. +قانون طلایی نوع‌های با اندازه‌ی پویا این است که +همیشه باید مقادیر این نوع‌ها را پشت یک نوع اشاره‌گر (pointer) قرار دهیم. + +ما می‌توانیم `str` را با انواع مختلفی از اشاره‌گرها ترکیب کنیم: +برای مثال، `Box<str>` یا `Rc<str>`. +در واقع، قبلاً نیز این را دیده‌اید اما با نوعی دیگر از نوع‌های با اندازه‌ی پویا: traits. +هر trait یک نوع با اندازه‌ی پویا است که می‌توانیم با استفاده از نام trait به آن ارجاع دهیم. +در بخش [“استفاده از trait objectها برای انتزاع‌سازی روی رفتار مشترک”][using-trait-objects-to-abstract-over-shared-behavior]<!-- ignore --> +در فصل ۱۸، گفتیم که برای استفاده از traits به عنوان trait object، +باید آن‌ها را پشت یک اشاره‌گر قرار دهیم، مانند `&dyn Trait` یا `Box<dyn Trait>` (`Rc<dyn Trait>` نیز کار می‌کند). + +برای کار با تایپ‌های دایتانیک، Rust ویژگی `Sized` را فراهم می‌کند تا تعیین کند که آیا اندازه یک نوع در زمان کامپایل مشخص است یا خیر. این ویژگی به طور خودکار برای هر چیزی که اندازه آن در زمان کامپایل مشخص باشد پیاده‌سازی می‌شود. علاوه بر این، Rust به طور ضمنی یک محدودیت روی `Sized` را به هر تابع جنریک اضافه می‌کند. یعنی یک تعریف تابع جنریک به این صورت: ```rust,ignore {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-12-generic-fn-definition/src/lib.rs}} ``` -is actually treated as though we had written this: +در واقع، به گونه‌ای رفتار می‌شود که گویی این را نوشته‌ایم: ```rust,ignore {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-13-generic-implicit-sized-bound/src/lib.rs}} ``` -By default, generic functions will work only on types that have a known size at -compile time. However, you can use the following special syntax to relax this -restriction: +به طور پیش‌فرض، توابع جنریک فقط روی نوع‌هایی کار خواهند کرد که اندازه آن‌ها در زمان کامپایل مشخص باشد. با این حال، می‌توانید از سینتکس خاص زیر برای کاهش این محدودیت استفاده کنید: ```rust,ignore {{#rustdoc_include ../listings/ch20-advanced-features/no-listing-14-generic-maybe-sized/src/lib.rs}} ``` -A trait bound on `?Sized` means “`T` may or may not be `Sized`” and this -notation overrides the default that generic types must have a known size at -compile time. The `?Trait` syntax with this meaning is only available for -`Sized`, not any other traits. +یک محدودیت ویژگی روی `?Sized` به این معنی است که "`T` ممکن است `Sized` باشد یا نباشد" و این یادداشت، پیش‌فرضی که نوع‌های جنریک باید اندازه مشخصی در زمان کامپایل داشته باشند را لغو می‌کند. سینتکس `?Trait` با این معنا تنها برای `Sized` در دسترس است، نه برای هیچ ویژگی دیگری. -Also note that we switched the type of the `t` parameter from `T` to `&T`. -Because the type might not be `Sized`, we need to use it behind some kind of -pointer. In this case, we’ve chosen a reference. +همچنین توجه داشته باشید که نوع پارامتر `t` را از `T` به `&T` تغییر دادیم. از آنجایی که نوع ممکن است `Sized` نباشد، باید از آن پشت یک نوع اشاره‌گر (Pointer) استفاده کنیم. در این مورد، یک ارجاع انتخاب کرده‌ایم. -Next, we’ll talk about functions and closures! +در ادامه، درباره توابع و closureها صحبت خواهیم کرد! [encapsulation-that-hides-implementation-details]: ch18-01-what-is-oo.html#encapsulation-that-hides-implementation-details [string-slices]: ch04-03-slices.html#string-slices [the-match-control-flow-construct]: ch06-02-match.html#the-match-control-flow-construct [using-trait-objects-to-abstract-over-shared-behavior]: ch18-02-trait-objects.html#using-trait-objects-to-abstract-over-shared-behavior -[using-the-newtype-pattern]: ch20-02-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits +[using-the-newtype-pattern]: ch20-02-advanced-traits.html#استفاده-از-الگوی-newtype-برای-پیادهسازی-traits-خارجی-روی-انواع-خارجی diff --git a/src/ch20-04-advanced-functions-and-closures.md b/src/ch20-04-advanced-functions-and-closures.md index 8c625d1f78..a46fc22942 100644 --- a/src/ch20-04-advanced-functions-and-closures.md +++ b/src/ch20-04-advanced-functions-and-closures.md @@ -1,28 +1,14 @@ -## Advanced Functions and Closures +## توابع پیشرفته و Closureها -This section explores some advanced features related to functions and closures, -including function pointers and returning closures. +این بخش به بررسی برخی از ویژگی‌های پیشرفته مربوط به توابع و Closureها می‌پردازد، از جمله Pointerهای تابع و بازگرداندن Closureها. -### Function Pointers +### Pointerهای تابع -We’ve talked about how to pass closures to functions; you can also pass regular -functions to functions! This technique is useful when you want to pass a -function you’ve already defined rather than defining a new closure. Functions -coerce to the type `fn` (with a lowercase _f_), not to be confused with the -`Fn` closure trait. The `fn` type is called a _function pointer_. Passing -functions with function pointers will allow you to use functions as arguments -to other functions. +ما درباره نحوهٔ ارسال کلوزرها به توابع صحبت کردیم؛ همچنین می‌توانید توابع معمولی را نیز به توابع دیگر ارسال کنید! این تکنیک زمانی مفید است که بخواهید تابعی را که از قبل تعریف کرده‌اید ارسال کنید، به‌جای آن‌که یک کلوزر جدید تعریف کنید. توابع به نوع `fn` (با f کوچک) تبدیل می‌شوند، که نباید با `Fn` که یک `trait` برای کلوزرهاست، اشتباه گرفته شود. نوع `fn` یک *پویتر به تابع* نامیده می‌شود. ارسال توابع با استفاده از پویترهای تابع این امکان را می‌دهد که از توابع به‌عنوان آرگومان به توابع دیگر استفاده کنید. -The syntax for specifying that a parameter is a function pointer is similar to -that of closures, as shown in Listing 20-28, where we’ve defined a function -`add_one` that adds 1 to its parameter. The function `do_twice` takes two -parameters: a function pointer to any function that takes an `i32` parameter -and returns an `i32`, and one `i32` value. The `do_twice` function calls the -function `f` twice, passing it the `arg` value, then adds the two function call -results together. The `main` function calls `do_twice` with the arguments -`add_one` and `5`. +نحو مشخص‌کردن اینکه یک پارامتر از نوع پویتر تابع است، مشابه نحوهٔ تعریف کلوزرهاست؛ همان‌طور که در لیست 20-28 نشان داده شده است. در آنجا تابعی به نام `add_one` تعریف کرده‌ایم که ۱ واحد به پارامتر خود اضافه می‌کند. تابع `do_twice` دو پارامتر می‌گیرد: یک پویتر به تابعی که یک پارامتر از نوع `i32` می‌گیرد و مقدار `i32` برمی‌گرداند، و یک مقدار `i32`. تابع `do_twice` تابع `f` را دو بار با مقدار `arg` فراخوانی می‌کند، سپس نتایج این دو فراخوانی را با هم جمع می‌زند. تابع `main` تابع `do_twice` را با آرگومان‌های `add_one` و `5` فراخوانی می‌کند. -<Listing number="20-28" file-name="src/main.rs" caption="Using the `fn` type to accept a function pointer as an argument"> +<Listing number="20-28" file-name="src/main.rs" caption="استفاده از نوع `fn` برای پذیرش یک اشاره‌گر (Pointer) تابع به عنوان آرگومان"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-28/src/main.rs}} @@ -30,31 +16,17 @@ results together. The `main` function calls `do_twice` with the arguments </Listing> -This code prints `The answer is: 12`. We specify that the parameter `f` in -`do_twice` is an `fn` that takes one parameter of type `i32` and returns an -`i32`. We can then call `f` in the body of `do_twice`. In `main`, we can pass -the function name `add_one` as the first argument to `do_twice`. +این کد مقدار `The answer is: 12` را چاپ می‌کند. ما مشخص کرده‌ایم که پارامتر `f` در `do_twice` یک `fn` است که یک پارامتر از نوع `i32` می‌گیرد و یک `i32` باز می‌گرداند. سپس می‌توانیم `f` را در بدنه تابع `do_twice` فراخوانی کنیم. در `main`، می‌توانیم نام تابع `add_one` را به عنوان آرگومان اول به `do_twice` ارسال کنیم. -Unlike closures, `fn` is a type rather than a trait, so we specify `fn` as the -parameter type directly rather than declaring a generic type parameter with one -of the `Fn` traits as a trait bound. +برخلاف Closureها `fn` یک نوع است و نه یک ویژگی، بنابراین ما `fn` را به طور مستقیم به عنوان نوع پارامتر مشخص می‌کنیم، به جای اعلام یک پارامتر جنریک با یکی از ویژگی‌های `Fn` به عنوان محدودیت ویژگی. -Function pointers implement all three of the closure traits (`Fn`, `FnMut`, and -`FnOnce`), meaning you can always pass a function pointer as an argument for a -function that expects a closure. It’s best to write functions using a generic -type and one of the closure traits so your functions can accept either -functions or closures. +Pointerهای تابع تمام سه ویژگی Closureها (`Fn`، `FnMut`، و `FnOnce`) را پیاده‌سازی می‌کنند، به این معنی که شما همیشه می‌توانید یک اشاره‌گر (Pointer) تابع را به عنوان آرگومان برای یک تابع که انتظار یک Closureها را دارد ارسال کنید. بهتر است توابع را با استفاده از یک نوع جنریک و یکی از ویژگی‌های Closureها بنویسید تا توابع شما بتوانند هم توابع و هم Closureها را بپذیرند. -That said, one example of where you would want to only accept `fn` and not -closures is when interfacing with external code that doesn’t have closures: C -functions can accept functions as arguments, but C doesn’t have closures. +با این حال، یک مثال از جایی که ممکن است بخواهید فقط `fn` را بپذیرید و نه Closureها زمانی است که با کد خارجی که Closureها ندارد تعامل می‌کنید: توابع C می‌توانند توابع را به عنوان آرگومان بپذیرند، اما C Closureها ندارد. -As an example of where you could use either a closure defined inline or a named -function, let’s look at a use of the `map` method provided by the `Iterator` -trait in the standard library. To use the `map` method to turn a vector of -numbers into a vector of strings, we could use a closure, as in Listing 20-29. +به‌عنوان مثالی از جایی که می‌توانید از یک کلوزر تعریف‌شده به‌صورت درجا یا از یک تابع نام‌گذاری‌شده استفاده کنید، بیایید نگاهی بیندازیم به یک استفاده از متد `map` که توسط `Iterator` در کتابخانه استاندارد فراهم شده است. برای استفاده از متد `map` به‌منظور تبدیل یک `vector` از اعداد به یک `vector` از رشته‌ها، می‌توانیم از یک کلوزر استفاده کنیم، همان‌طور که در لیست 20-29 نشان داده شده است. -<Listing number="20-29" caption="Using a closure with the `map` method to convert numbers to strings"> +<Listing number="20-29" caption="استفاده از یک کلوزر با متد `map` برای تبدیل اعداد به رشته‌ها"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-29/src/main.rs:here}} @@ -62,10 +34,9 @@ numbers into a vector of strings, we could use a closure, as in Listing 20-29. </Listing> -Or we could name a function as the argument to `map` instead of the closure. -Listing 20-30 shows what this would look like. +یا می‌توانیم به‌جای کلوزر، یک تابع را به‌عنوان آرگومان به `map` بدهیم. لیست 20-30 نشان می‌دهد که این کار چگونه انجام می‌شود. -<Listing number="20-30" caption="Using the `String::to_string` function with the `map` method method to convert numbers to strings"> +<Listing number="20-30" caption="استفاده از تابع `String::to_string` با متد `map` برای تبدیل اعداد به رشته‌ها"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-30/src/main.rs:here}} @@ -73,21 +44,20 @@ Listing 20-30 shows what this would look like. </Listing> -Note that we must use the fully qualified syntax that we talked about in -[“Advanced Traits”][advanced-traits]<!-- ignore --> because there are multiple -functions available named `to_string`. +توجه داشته باشید که باید از سینتکس کاملاً مشخصی که در بخش +[«ویژگی‌های پیشرفته»][advanced-traits]<!-- ignore --> درباره آن صحبت کردیم استفاده کنیم، +زیرا توابع متعددی با نام `to_string` وجود دارند. -Here, we’re using the `to_string` function defined in the `ToString` trait, -which the standard library has implemented for any type that implements -`Display`. +در این‌جا، از تابع `to_string` استفاده می‌کنیم که در `trait` به نام `ToString` تعریف شده +و کتابخانه استاندارد آن را برای هر نوعی که `Display` را پیاده‌سازی کرده باشد، پیاده‌سازی کرده است. -Recall from [“Enum Values”][enum-values]<!-- ignore --> in Chapter 6 that the -name of each enum variant that we define also becomes an initializer function. -We can use these initializer functions as function pointers that implement the -closure traits, which means we can specify the initializer functions as -arguments for methods that take closures, as seen in Listing 20-31. +به یاد بیاورید که در بخش [«مقادیر enum»][enum-values]<!-- ignore --> از فصل ۶ اشاره کردیم +که نام هر واریانت `enum` که تعریف می‌کنیم، همچنین تبدیل به یک تابع سازنده (initializer function) می‌شود. +ما می‌توانیم این توابع سازنده را به‌عنوان فانکشن‌پوینترهایی که `closure trait`‌ها را پیاده‌سازی می‌کنند استفاده کنیم؛ +این یعنی می‌توانیم این توابع سازنده را به‌عنوان آرگومان برای متدهایی که کلوزر دریافت می‌کنند مشخص کنیم، +همان‌طور که در لیست 31-20 مشاهده می‌کنید. -<Listing number="20-31" caption="Using an enum initializer with the `map` method to create a `Status` instance from numbers"> +<Listing number="20-31" caption="استفاده از سازنده enum با متد `map` برای ایجاد نمونه‌ای از `Status` از روی اعداد"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-31/src/main.rs:here}} @@ -95,26 +65,22 @@ arguments for methods that take closures, as seen in Listing 20-31. </Listing> -Here, we create `Status::Value` instances using each `u32` value in the range -that `map` is called on by using the initializer function of `Status::Value`. -Some people prefer this style and some people prefer to use closures. They -compile to the same code, so use whichever style is clearer to you. +در این‌جا، با استفاده از تابع سازنده `Status::Value`، برای هر مقدار `u32` در بازه‌ای که `map` روی آن فراخوانی شده، نمونه‌هایی از `Status::Value` ایجاد می‌کنیم. +برخی افراد این سبک را ترجیح می‌دهند و برخی دیگر ترجیح می‌دهند از کلوزرها استفاده کنند. +هر دو روش به یک کد کامپایل می‌شوند، پس از هر سبکی که برای شما واضح‌تر است استفاده کنید. -### Returning Closures +### بازگرداندن کلوزرها (closures) (Returning Closures) -Closures are represented by traits, which means you can’t return closures -directly. In most cases where you might want to return a trait, you can instead -use the concrete type that implements the trait as the return value of the -function. However, you can’t usually do that with closures because they don’t -have a concrete type that is returnable; you’re not allowed to use the function -pointer `fn` as a return type if the closure captures any values from its -scope, for example. +کلوزرها توسط `trait`ها نمایش داده می‌شوند، به این معنا که نمی‌توان آن‌ها را مستقیماً به عنوان مقدار بازگشتی برگرداند. +در بیشتر مواردی که ممکن است بخواهید یک `trait` را برگردانید، می‌توانید به جای آن از نوع مشخصی که آن `trait` را پیاده‌سازی می‌کند به‌عنوان مقدار بازگشتی تابع استفاده کنید. +اما معمولاً نمی‌توانید این کار را با کلوزرها انجام دهید، چون آن‌ها نوع مشخصی ندارند که قابل بازگشت باشد؛ +برای مثال، اگر کلوزری مقداری از اسکوپ خود را کپچر کند، مجاز به استفاده از نوع اشاره‌گر تابع `fn` به‌عنوان نوع بازگشتی نیستید. -Instead, you will normally use the `impl Trait` syntax we learned about in -Chapter 10. You can return any function type, using `Fn`, `FnOnce` and `FnMut`. -For example, the code in Listing 20-32 will compile just fine. +در عوض، معمولاً از سینتکس `impl Trait` که در فصل ۱۰ یاد گرفتیم استفاده می‌شود. +می‌توانید هر نوع تابعی را با استفاده از `Fn`، `FnOnce` و `FnMut` بازگردانید. +برای مثال، کدی که در لیست 20-32 آمده است، بدون مشکل کامپایل می‌شود. -<Listing number="20-32" caption="Returning a closure from a function using the `impl Trait` syntax"> +<Listing number="20-32" caption="بازگرداندن یک کلوزر از تابع با استفاده از سینتکس `impl Trait`"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-32/src/lib.rs}} @@ -122,14 +88,11 @@ For example, the code in Listing 20-32 will compile just fine. </Listing> -However, as we noted in [“Closure Type Inference and -Annotation”][closure-types]<!-- ignore --> in Chapter 13, each closure is also -its own distinct type. If you need to work with multiple functions that have the -same signature but different implementations, you will need to use a trait -object for them. Consider what happens if you write code like that shown in -Listing 20-33. +با این حال، همان‌طور که در بخش [“استنباط نوع کلوزر و حاشیه‌نویسی”][closure-types]<!-- ignore --> در فصل ۱۳ اشاره شد، +هر کلوزر همچنین نوع خاص خودش را دارد. اگر نیاز دارید با چندین تابع که امضای یکسانی دارند اما پیاده‌سازی متفاوتی دارند کار کنید، +باید از یک آبجکت `trait` برای آن‌ها استفاده کنید. ببینید چه اتفاقی می‌افتد اگر کدی مشابه لیست 20-33 بنویسید. -<Listing file-name="src/main.rs" number="20-33" caption="Creating a `Vec<T>` of closures defined by functions that return `impl Fn` types"> +<Listing file-name="src/main.rs" number="20-33" caption="ایجاد یک `Vec<T>` از کلوزرهایی که توسط توابعی بازگردانده می‌شوند که نوع `impl Fn` دارند"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-33/src/main.rs}} @@ -137,27 +100,29 @@ Listing 20-33. </Listing> -Here we have two functions, `returns_closure` and `returns_initialized_closure`, -which both return `impl Fn(i32) -> i32`. Notice that the closures that they -return are different, even though they implement the same type. If we try to -compile this, Rust lets us know that it won’t work: +در این‌جا دو تابع داریم به نام‌های `returns_closure` و `returns_initialized_closure`، +که هر دو مقدار `impl Fn(i32) -> i32` را بازمی‌گردانند. +توجه داشته باشید که کلوزرهایی که این توابع بازمی‌گردانند با یکدیگر متفاوت‌اند، +حتی اگر هر دو `trait` یکسانی را پیاده‌سازی کنند. +اگر سعی کنیم این کد را کامپایل کنیم، Rust به ما اطلاع می‌دهد که این کار امکان‌پذیر نیست: ```text {{#include ../listings/ch20-advanced-features/listing-20-33/output.txt}} ``` -The error message tells us that whenever we return an `impl Trait`, Rust -creates a unique _opaque type_, a type where we cannot see into the details of -what Rust constructs for us, nor can we guess the type Rust will generate to -write ourselves. So even though these functions return closures that implement -the same trait, `Fn(i32) -> i32`, the opaque types Rust generates for each are -distinct. (This is similar to how Rust produces different concrete types for -distinct async blocks even when they have the same output type, as we saw in -[“Working with Any Number of Futures”][any-number-of-futures]<!-- ignore --> in -Chapter 17.) We have seen a solution to this problem a few times now: we can -use a trait object, as in Listing 20-34. +پیام خطا به ما می‌گوید که زمانی که ما یک `impl Trait` را بازمی‌گردانیم، +Rust یک *نوع مبهم* (opaque type) منحصربه‌فرد ایجاد می‌کند؛ +نوعی که نمی‌توانیم جزئیات آن را ببینیم و همچنین نمی‌توانیم نوع تولیدشده توسط Rust را +حدس بزنیم یا خودمان بنویسیم. بنابراین، حتی اگر این توابع کلوزرهایی را بازگردانند +که `trait` یکسانی مانند `Fn(i32) -> i32` را پیاده‌سازی می‌کنند، +نوع‌های مبهم تولیدشده توسط Rust برای هر کدام متفاوت‌اند. +(این مشابه نحوه‌ای است که Rust نوع‌های مشخص مختلفی برای بلوک‌های `async` متمایز تولید می‌کند، +حتی اگر خروجی آن‌ها یکسان باشد، همان‌طور که در بخش +[“کار با هر تعداد future”][any-number-of-futures]<!-- ignore --> در فصل ۱۷ دیدیم.) +ما پیش از این نیز چندین بار راه‌حل این مشکل را دیده‌ایم: +می‌توانیم از یک trait object استفاده کنیم، مانند نمونه‌ای که در Listing 20-34 نشان داده شده است. -<Listing number="20-34" caption="Creating a `Vec<T>` of closures defined by functions that return `Box<dyn Fn>` so they have the same type"> +<Listing number="20-34" caption="ساخت یک `Vec<T>` از کلوزرهایی که توسط توابعی بازمی‌گردند که مقدار `Box<dyn Fn>` را برمی‌گردانند تا نوع آن‌ها یکسان باشد"> ```rust {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-34/src/main.rs:here}} @@ -165,12 +130,12 @@ use a trait object, as in Listing 20-34. </Listing> -This code will compile just fine. For more about trait objects, refer to the -section [“Using Trait Objects That Allow for Values of Different -Types”][using-trait-objects-to-abstract-over-shared-behavior]<!-- ignore ---> in Chapter 18. +این کد بدون مشکل کامپایل خواهد شد. برای اطلاعات بیشتر در مورد trait object‌ها، +به بخش [“استفاده از trait objectهایی که اجازه استفاده از مقادیر با نوع‌های متفاوت را می‌دهند”][using-trait-objects-to-abstract-over-shared-behavior]<!-- ignore --> +در فصل ۱۸ مراجعه کنید. + +حال بیایید نگاهی به ماکروها بیندازیم! -Next, let’s look at macros! [advanced-traits]: ch20-02-advanced-traits.html#advanced-traits [enum-values]: ch06-01-defining-an-enum.html#enum-values diff --git a/src/ch20-05-macros.md b/src/ch20-05-macros.md index 51404a389c..780d3d0461 100644 --- a/src/ch20-05-macros.md +++ b/src/ch20-05-macros.md @@ -1,81 +1,40 @@ -## Macros - -We’ve used macros like `println!` throughout this book, but we haven’t fully -explored what a macro is and how it works. The term _macro_ refers to a family -of features in Rust: _declarative_ macros with `macro_rules!` and three kinds -of _procedural_ macros: - -- Custom `#[derive]` macros that specify code added with the `derive` attribute - used on structs and enums -- Attribute-like macros that define custom attributes usable on any item -- Function-like macros that look like function calls but operate on the tokens - specified as their argument - -We’ll talk about each of these in turn, but first, let’s look at why we even -need macros when we already have functions. - -### The Difference Between Macros and Functions - -Fundamentally, macros are a way of writing code that writes other code, which -is known as _metaprogramming_. In Appendix C, we discuss the `derive` -attribute, which generates an implementation of various traits for you. We’ve -also used the `println!` and `vec!` macros throughout the book. All of these -macros _expand_ to produce more code than the code you’ve written manually. - -Metaprogramming is useful for reducing the amount of code you have to write and -maintain, which is also one of the roles of functions. However, macros have -some additional powers that functions don’t have. - -A function signature must declare the number and type of parameters the -function has. Macros, on the other hand, can take a variable number of -parameters: we can call `println!("hello")` with one argument or -`println!("hello {}", name)` with two arguments. Also, macros are expanded -before the compiler interprets the meaning of the code, so a macro can, for -example, implement a trait on a given type. A function can’t, because it gets -called at runtime and a trait needs to be implemented at compile time. - -The downside to implementing a macro instead of a function is that macro -definitions are more complex than function definitions because you’re writing -Rust code that writes Rust code. Due to this indirection, macro definitions are -generally more difficult to read, understand, and maintain than function -definitions. - -Another important difference between macros and functions is that you must -define macros or bring them into scope _before_ you call them in a file, as -opposed to functions you can define anywhere and call anywhere. - -### Declarative Macros with `macro_rules!` for General Metaprogramming - -The most widely used form of macros in Rust is the _declarative macro_. These -are also sometimes referred to as “macros by example,” “`macro_rules!` macros,” -or just plain “macros.” At their core, declarative macros allow you to write -something similar to a Rust `match` expression. As discussed in Chapter 6, -`match` expressions are control structures that take an expression, compare the -resultant value of the expression to patterns, and then run the code associated -with the matching pattern. Macros also compare a value to patterns that are -associated with particular code: in this situation, the value is the literal -Rust source code passed to the macro; the patterns are compared with the -structure of that source code; and the code associated with each pattern, when -matched, replaces the code passed to the macro. This all happens during -compilation. - -To define a macro, you use the `macro_rules!` construct. Let’s explore how to -use `macro_rules!` by looking at how the `vec!` macro is defined. Chapter 8 -covered how we can use the `vec!` macro to create a new vector with particular -values. For example, the following macro creates a new vector containing three -integers: +## ماکروها (Macros) + +ما در طول این کتاب از ماکروهایی مانند `println!` استفاده کرده‌ایم، اما هنوز به طور کامل بررسی نکرده‌ایم که یک ماکرو چیست و چگونه کار می‌کند. اصطلاح _ماکرو_ به مجموعه‌ای از قابلیت‌ها در Rust اشاره دارد: ماکروهای _اعلانی_ (declarative) با `macro_rules!` و سه نوع ماکرو _رویه‌ای_ (procedural): + +- ماکروهای سفارشی `#[derive]` که کدی را که با ویژگی `derive` برای ساختارها (structs) و شمارش‌ها (enums) اضافه می‌شود مشخص می‌کنند. +- ماکروهای شبیه ویژگی (Attribute-like) که ویژگی‌های سفارشی تعریف می‌کنند که می‌توانند روی هر آیتمی استفاده شوند. +- ماکروهای شبیه تابع (Function-like) که مانند فراخوانی تابع به نظر می‌رسند اما روی توکن‌هایی که به عنوان آرگومان مشخص شده‌اند عمل می‌کنند. + +ما به نوبت درباره هر یک از این‌ها صحبت خواهیم کرد، اما ابتدا بیایید نگاهی بیندازیم که چرا اصلاً به ماکروها نیاز داریم وقتی قبلاً توابع را داریم. + +### تفاوت بین ماکروها و توابع + +در اصل، ماکروها روشی برای نوشتن کدی هستند که کد دیگری را می‌نویسد، که به عنوان _فرابرنامه‌نویسی_ (metaprogramming) شناخته می‌شود. در پیوست C، ما ویژگی `derive` را بررسی می‌کنیم که پیاده‌سازی ویژگی‌های مختلف را برای شما تولید می‌کند. همچنین ما از ماکروهای `println!` و `vec!` در طول کتاب استفاده کرده‌ایم. همه این ماکروها _توسعه_ پیدا می‌کنند تا کدی بیشتر از کدی که به صورت دستی نوشته‌اید تولید کنند. + +برنامه‌نویسی فراداده (Metaprogramming) برای کاهش میزان کدی که باید بنویسید و نگهداری کنید مفید است، که این نیز یکی از وظایف توابع است. با این حال، ماکروها توانایی‌های اضافی‌ای دارند که توابع از آن‌ها برخوردار نیستند. + +یک امضای تابع باید تعداد و نوع پارامترهایی که تابع دارد را مشخص کند. از سوی دیگر، ماکروها می‌توانند تعداد متغیری از پارامترها را بپذیرند: می‌توانیم `println!("hello")` را با یک آرگومان یا `println!("hello {}", name)` را با دو آرگومان فراخوانی کنیم. همچنین، ماکروها قبل از اینکه کامپایلر معنی کد را تفسیر کند گسترش می‌یابند، بنابراین یک ماکرو می‌تواند، به عنوان مثال، یک ویژگی را روی یک نوع مشخص پیاده‌سازی کند. اما یک تابع نمی‌تواند، زیرا در زمان اجرا فراخوانی می‌شود و یک ویژگی باید در زمان کامپایل پیاده‌سازی شود. + +عیب پیاده‌سازی یک ماکرو به جای یک تابع این است که تعریف ماکروها پیچیده‌تر از تعریف توابع است زیرا شما در حال نوشتن کدی در Rust هستید که کد دیگری را در Rust می‌نویسد. به دلیل این واسطه‌گری، تعریف ماکروها به طور کلی سخت‌تر از توابع خوانده می‌شود، فهمیده می‌شود و نگهداری می‌شود. + +یکی دیگر از تفاوت‌های مهم بین ماکروها و توابع این است که شما باید ماکروها را _قبل_ از فراخوانی آن‌ها در یک فایل تعریف کنید یا به دامنه بیاورید، برخلاف توابع که می‌توانید آن‌ها را در هر جایی تعریف کرده و در هر جایی فراخوانی کنید. + +### ماکروهای اعلانی با `macro_rules!` برای فرابرنامه‌نویسی عمومی + +رایج‌ترین شکل استفاده از ماکروها در Rust، *ماکروهای اعلامی* (declarative macro) هستند. این نوع ماکروها گاهی با عنوان‌هایی مانند "ماکروهای بر پایه‌ی مثال"، "ماکروهای `macro_rules!`"، یا به‌سادگی "ماکروها" شناخته می‌شوند. در هسته‌ی خود، ماکروهای اعلامی به شما اجازه می‌دهند چیزی مشابه یک عبارت `match` در Rust بنویسید. همان‌طور که در فصل ۶ بیان شد، عبارات `match` ساختارهای کنترلی‌ای هستند که یک عبارت را می‌گیرند، مقدار حاصل از آن را با الگوها مقایسه می‌کنند، و سپس کدی که با الگوی مطابق مرتبط است را اجرا می‌کنند. ماکروها نیز یک مقدار را با الگوهایی مقایسه می‌کنند که با کد خاصی مرتبط‌اند: در این حالت، مقدار، کد منبع Rust است که به ماکرو داده می‌شود؛ الگوها با ساختار این کد منبع مقایسه می‌شوند؛ و کدی که با هر الگو مرتبط است، در صورت تطابق، جایگزین کدی می‌شود که به ماکرو داده شده است. تمام این فرآیند در زمان کامپایل اتفاق می‌افتد. + +برای تعریف یک ماکرو، از ساختار `macro_rules!` استفاده می‌کنید. بیایید بررسی کنیم چگونه از `macro_rules!` استفاده کنیم با نگاهی به نحوه تعریف ماکروی `vec!`. فصل ۸ پوشش داد که چگونه می‌توانیم از ماکروی `vec!` برای ایجاد یک بردار جدید با مقادیر خاص استفاده کنیم. به عنوان مثال، ماکروی زیر یک بردار جدید حاوی سه عدد صحیح ایجاد می‌کند: ```rust let v: Vec<u32> = vec![1, 2, 3]; ``` -We could also use the `vec!` macro to make a vector of two integers or a vector -of five string slices. We wouldn’t be able to use a function to do the same -because we wouldn’t know the number or type of values up front. +ما همچنین می‌توانیم از ماکروی `vec!` برای ساخت یک بردار شامل دو عدد صحیح یا یک بردار شامل پنج برش رشته استفاده کنیم. نمی‌توانیم از یک تابع برای انجام همین کار استفاده کنیم زیرا نمی‌دانیم تعداد یا نوع مقادیر از پیش چیست. -Listing 20-35 shows a slightly simplified definition of the `vec!` macro. +فهرست 20-35 نسخه‌ای کمی ساده‌شده از تعریف ماکروی `vec!` را نشان می‌دهد. -<Listing number="20-35" file-name="src/lib.rs" caption="A simplified version of the `vec!` macro definition"> +<Listing number="20-29" file-name="src/lib.rs" caption="یک نسخه ساده‌شده از تعریف ماکروی `vec!`"> ```rust,noplayground {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-35/src/lib.rs}} @@ -83,54 +42,23 @@ Listing 20-35 shows a slightly simplified definition of the `vec!` macro. </Listing> -> Note: The actual definition of the `vec!` macro in the standard library -> includes code to pre-allocate the correct amount of memory up front. That code -> is an optimization that we don’t include here, to make the example simpler. - -The `#[macro_export]` annotation indicates that this macro should be made -available whenever the crate in which the macro is defined is brought into -scope. Without this annotation, the macro can’t be brought into scope. - -We then start the macro definition with `macro_rules!` and the name of the -macro we’re defining _without_ the exclamation mark. The name, in this case -`vec`, is followed by curly brackets denoting the body of the macro definition. - -The structure in the `vec!` body is similar to the structure of a `match` -expression. Here we have one arm with the pattern `( $( $x:expr ),* )`, -followed by `=>` and the block of code associated with this pattern. If the -pattern matches, the associated block of code will be emitted. Given that this -is the only pattern in this macro, there is only one valid way to match; any -other pattern will result in an error. More complex macros will have more than -one arm. - -Valid pattern syntax in macro definitions is different from the pattern syntax -covered in Chapter 19 because macro patterns are matched against Rust code -structure rather than values. Let’s walk through what the pattern pieces in -Listing 20-29 mean; for the full macro pattern syntax, see the [Rust -Reference][ref]. - -First we use a set of parentheses to encompass the whole pattern. We use a -dollar sign (`$`) to declare a variable in the macro system that will contain -the Rust code matching the pattern. The dollar sign makes it clear this is a -macro variable as opposed to a regular Rust variable. Next comes a set of -parentheses that captures values that match the pattern within the parentheses -for use in the replacement code. Within `$()` is `$x:expr`, which matches any -Rust expression and gives the expression the name `$x`. - -The comma following `$()` indicates that a literal comma separator character -must appear between each instance of the code that matches the code in `$()`. -The `*` specifies that the pattern matches zero or more of whatever precedes -the `*`. - -When we call this macro with `vec![1, 2, 3];`, the `$x` pattern matches three -times with the three expressions `1`, `2`, and `3`. - -Now let’s look at the pattern in the body of the code associated with this arm: -`temp_vec.push()` within `$()*` is generated for each part that matches `$()` -in the pattern zero or more times depending on how many times the pattern -matches. The `$x` is replaced with each expression matched. When we call this -macro with `vec![1, 2, 3];`, the code generated that replaces this macro call -will be the following: +> نکته: تعریف واقعی ماکروی `vec!` در کتابخانه استاندارد شامل کدی برای پیش‌اختصاص دادن مقدار مناسبی از حافظه به‌صورت اولیه است. این کد یک بهینه‌سازی است که برای ساده‌تر شدن مثال، در این‌جا گنجانده نشده است. + +حاشیه‌نویسی `#[macro_export]` نشان می‌دهد که این ماکرو باید هر زمان که crate‌ای که ماکرو در آن تعریف شده است به دامنه آورده شود، در دسترس قرار گیرد. بدون این حاشیه‌نویسی، ماکرو نمی‌تواند به دامنه آورده شود. + +سپس تعریف ماکرو را با `macro_rules!` و نام ماکرویی که تعریف می‌کنیم _بدون_ علامت تعجب شروع می‌کنیم. نام، که در اینجا `vec` است، با آکولادهایی دنبال می‌شود که بدنه تعریف ماکرو را مشخص می‌کنند. + +ساختار بدنه `vec!` مشابه ساختار یک عبارت `match` است. در اینجا یک بازو با الگوی `( $( $x:expr ),* )` داریم، که با `=>` و بلوک کدی که با این الگو مرتبط است دنبال می‌شود. اگر الگو تطابق یابد، بلوک کد مرتبط گسترش می‌یابد. با توجه به اینکه این تنها الگو در این ماکرو است، تنها یک روش معتبر برای تطابق وجود دارد؛ هر الگوی دیگری باعث خطا خواهد شد. ماکروهای پیچیده‌تر ممکن است بیش از یک بازو داشته باشند. + +سینتکس الگوهای معتبر در تعریف ماکروها با سینتکس الگوهایی که در فصل ۱۹ بررسی کردیم متفاوت است، زیرا الگوهای ماکرو در برابر ساختار کد راست مطابقت داده می‌شوند، نه در برابر مقادیر. بیایید قدم‌به‌قدم بررسی کنیم که اجزای الگو در لیست ۲۰-۲۹ چه معنایی دارند؛ برای مشاهده‌ی کامل سینتکس الگوهای ماکرو، به [مرجع رسمی Rust][ref] مراجعه کنید. + +ابتدا از یک جفت پرانتز برای در بر گرفتن کل الگو استفاده می‌کنیم. از علامت دلار (`$`) برای تعریف یک متغیر در سیستم ماکرو استفاده می‌شود که کد راستی را که با الگو مطابقت دارد، در خود نگه می‌دارد. علامت دلار نشان می‌دهد که این یک متغیر ماکرو است و نه یک متغیر معمولی در راست. سپس یک جفت پرانتز می‌آید که مقادیری را که با الگو مطابقت دارند، در خود می‌گیرد تا در کد جایگزین مورد استفاده قرار گیرند. درون `$()`، عبارت `$x:expr` قرار دارد، که هر عبارت راست را مطابقت می‌دهد و به آن نام `$x` می‌دهد. + +کامی که بعد از `$()` آمده است نشان می‌دهد که باید بین هر نمونه از کدی که با الگوی درون `$()` مطابقت دارد، یک کاراکتر ویرگول قرار داشته باشد. علامت `*` مشخص می‌کند که الگو صفر یا تعداد بیشتری از موردی را که قبل از `*` آمده، مطابقت می‌دهد. + +وقتی این ماکرو را با `vec![1, 2, 3];` فراخوانی می‌کنیم، الگوی `$x` سه بار با سه عبارت `1`، `2` و `3` تطابق پیدا می‌کند. + +حالا بیایید به الگویی که در بدنه کد مرتبط با این بازو وجود دارد نگاه کنیم: `temp_vec.push()` درون `$()*` برای هر بخشی که با `$()` در الگو تطابق دارد، صفر یا بیشتر بار بسته به اینکه الگو چند بار تطابق پیدا می‌کند، تولید می‌شود. `$x` با هر عبارتی که تطابق پیدا کند جایگزین می‌شود. وقتی این ماکرو را با `vec![1, 2, 3];` فراخوانی می‌کنیم، کدی که جایگزین این فراخوانی ماکرو می‌شود به شکل زیر خواهد بود: ```rust,ignore { @@ -142,29 +70,17 @@ will be the following: } ``` -We’ve defined a macro that can take any number of arguments of any type and can -generate code to create a vector containing the specified elements. +ما یک ماکرو تعریف کرده‌ایم که می‌تواند هر تعداد آرگومان از هر نوعی را بپذیرد و کدی برای ایجاد یک بردار که شامل عناصر مشخص‌شده است تولید کند. -To learn more about how to write macros, consult the online documentation or -other resources, such as [“The Little Book of Rust Macros”][tlborm] started by -Daniel Keep and continued by Lukas Wirth. +برای یادگیری بیشتر در مورد نحوه نوشتن ماکروها، به مستندات آنلاین یا منابع دیگر مانند [“The Little Book of Rust Macros”][tlborm] که توسط Daniel Keep آغاز و توسط Lukas Wirth ادامه داده شده است، مراجعه کنید. -### Procedural Macros for Generating Code from Attributes +### ماکروهای رویه‌ای (Procedural) برای تولید کد از ویژگی‌ها (Attributes) -The second form of macros is the procedural macro, which acts more like a -function (and is a type of procedure). _Procedural macros_ accept some code as -an input, operate on that code, and produce some code as an output rather than -matching against patterns and replacing the code with other code as declarative -macros do. The three kinds of procedural macros are custom `derive`, -attribute-like, and function-like, and all work in a similar fashion. +نوع دوم ماکروها، ماکروهای رویه‌ای (procedural macros) هستند که رفتاری شبیه به توابع دارند (و در واقع نوعی رویه محسوب می‌شوند). *ماکروهای رویه‌ای* قطعه‌ای از کد را به عنوان ورودی دریافت می‌کنند، روی آن کد پردازش انجام می‌دهند و کدی را به عنوان خروجی تولید می‌کنند، در حالی که ماکروهای اعلامی (declarative) با الگوها مطابقت داده و کد را با کدی دیگر جایگزین می‌کنند. سه نوع از ماکروهای رویه‌ای وجود دارد: `derive` سفارشی، ماکروهای شبیه-صفت (attribute-like)، و ماکروهای شبیه-تابع (function-like)، و همگی به شکلی مشابه عمل می‌کنند. -When creating procedural macros, the definitions must reside in their own crate -with a special crate type. This is for complex technical reasons that we hope -to eliminate in the future. In Listing 20-36, we show how to define a -procedural macro, where `some_attribute` is a placeholder for using a specific -macro variety. +هنگام ایجاد ماکروهای رویه‌ای، تعریف آن‌ها باید در یک crate جداگانه قرار گیرد که نوع crate آن به‌صورت ویژه مشخص شده باشد. این الزام به دلایل فنی پیچیده‌ای است که امیدواریم در آینده برطرف شوند. در لیست 20-36، نحوه تعریف یک ماکرو رویه‌ای را نشان می‌دهیم که در آن `some_attribute` یک جایگزین برای نوع خاصی از ماکرو است. -<Listing number="20-36" file-name="src/lib.rs" caption="An example of defining a procedural macro"> +<Listing number="20-36" file-name="src/lib.rs" caption="نمونه‌ای از تعریف یک ماکرو رویه‌ای"> ```rust,ignore use proc_macro; @@ -176,32 +92,15 @@ pub fn some_name(input: TokenStream) -> TokenStream { </Listing> -The function that defines a procedural macro takes a `TokenStream` as an input -and produces a `TokenStream` as an output. The `TokenStream` type is defined by -the `proc_macro` crate that is included with Rust and represents a sequence of -tokens. This is the core of the macro: the source code that the macro is -operating on makes up the input `TokenStream`, and the code the macro produces -is the output `TokenStream`. The function also has an attribute attached to it -that specifies which kind of procedural macro we’re creating. We can have -multiple kinds of procedural macros in the same crate. - -Let’s look at the different kinds of procedural macros. We’ll start with a -custom `derive` macro and then explain the small dissimilarities that make the -other forms different. - -### How to Write a Custom `derive` Macro - -Let’s create a crate named `hello_macro` that defines a trait named -`HelloMacro` with one associated function named `hello_macro`. Rather than -making our users implement the `HelloMacro` trait for each of their types, -we’ll provide a procedural macro so users can annotate their type with -`#[derive(HelloMacro)]` to get a default implementation of the `hello_macro` -function. The default implementation will print `Hello, Macro! My name is -TypeName!` where `TypeName` is the name of the type on which this trait has -been defined. In other words, we’ll write a crate that enables another -programmer to write code like Listing 20-37 using our crate. - -<Listing number="20-37" file-name="src/main.rs" caption="The code a user of our crate will be able to write when using our procedural macro"> +تابعی که یک ماکروی رویه‌ای را تعریف می‌کند، یک `TokenStream` را به عنوان ورودی می‌گیرد و یک `TokenStream` را به عنوان خروجی تولید می‌کند. نوع `TokenStream` توسط crate به نام `proc_macro` تعریف شده است که با Rust همراه است و نمایانگر یک توالی از توکن‌ها است. این هسته ماکرو است: کد منبعی که ماکرو روی آن عمل می‌کند ورودی `TokenStream` را تشکیل می‌دهد و کدی که ماکرو تولید می‌کند خروجی `TokenStream` است. این تابع همچنین دارای یک ویژگی (attribute) متصل به خود است که مشخص می‌کند کدام نوع از ماکروی رویه‌ای را ایجاد می‌کنیم. ما می‌توانیم چندین نوع از ماکروهای رویه‌ای را در یک crate داشته باشیم. + +بیایید نگاهی به انواع مختلف ماکروهای رویه‌ای بیندازیم. ابتدا با یک ماکرو `derive` سفارشی شروع می‌کنیم و سپس تفاوت‌های جزئی که باعث تمایز شکل‌های دیگر می‌شوند را توضیح خواهیم داد. + +### نحوه نوشتن یک ماکروی `derive` سفارشی + +بیایید یک crate به نام `hello_macro` ایجاد کنیم که یک `trait` به نام `HelloMacro` را تعریف می‌کند با یک تابع مرتبط به نام `hello_macro`. به‌جای آن‌که کاربرانمان مجبور باشند `trait`‌ مربوطه را برای هرکدام از نوع‌هایشان پیاده‌سازی کنند، ما یک ماکروی روندی فراهم خواهیم کرد تا کاربران بتوانند نوع خود را با `#[derive(HelloMacro)]` مشخص کنند و به‌طور خودکار یک پیاده‌سازی پیش‌فرض از تابع `hello_macro` دریافت کنند. این پیاده‌سازی پیش‌فرض، عبارت `Hello, Macro! My name is TypeName!` را چاپ خواهد کرد، جایی که `TypeName` نام نوعی است که این `trait` روی آن تعریف شده است. به بیان دیگر، ما یک crate خواهیم نوشت که به برنامه‌نویس دیگری اجازه می‌دهد کدی شبیه لیست ۲۰-۳۷ را با استفاده از crate ما بنویسد. + +<Listing number="20-37" file-name="src/main.rs" caption="کدی که کاربر crate ما می‌تواند هنگام استفاده از ماکروی روندی ما بنویسد"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-37/src/main.rs}} @@ -209,15 +108,13 @@ programmer to write code like Listing 20-37 using our crate. </Listing> -This code will print `Hello, Macro! My name is Pancakes!` when we’re done. The -first step is to make a new library crate, like this: +این کد متن `Hello, Macro! My name is Pancakes!` را چاپ می‌کند وقتی کار ما تمام شود. اولین قدم این است که یک crate جدید از نوع کتابخانه بسازیم، به این صورت: ```console $ cargo new hello_macro --lib ``` -Next, in Listing 20-38, we’ll define the `HelloMacro` trait and its associated -function. +سپس، در لیست ۲۰-۳۸، `trait` مربوط به `HelloMacro` و تابع مرتبط با آن را تعریف خواهیم کرد. <Listing file-name="src/lib.rs" number="20-38" caption="A simple trait that we will use with the `derive` macro"> @@ -227,10 +124,9 @@ function. </Listing> -We have a trait and its function. At this point, our crate user could implement -the trait to achieve the desired functionality, as in Listing 20-39. +ما یک `trait` و تابع مربوط به آن داریم. در این مرحله، کاربر crate ما می‌تواند این trait را به صورت دستی پیاده‌سازی کند تا به عملکرد مورد نظر دست یابد، همان‌طور که در لیست ۲۰-۳۹ نشان داده شده است. -<Listing number="20-39" file-name="src/main.rs" caption="How it would look if users wrote a manual implementation of the `HelloMacro` trait"> +<Listing number="20-39" file-name="src/main.rs" caption="نحوه‌ای که کاربران می‌توانند پیاده‌سازی دستی trait به نام `HelloMacro` را انجام دهند"> ```rust,ignore {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-39/pancakes/src/main.rs}} @@ -238,41 +134,19 @@ the trait to achieve the desired functionality, as in Listing 20-39. </Listing> -However, they would need to write the implementation block for each type they -wanted to use with `hello_macro`; we want to spare them from having to do this -work. +با این حال، آن‌ها باید بلوک پیاده‌سازی را برای هر نوعی که می‌خواهند با `hello_macro` استفاده کنند بنویسند؛ ما می‌خواهیم آن‌ها را از انجام این کار معاف کنیم. -Additionally, we can’t yet provide the `hello_macro` function with default -implementation that will print the name of the type the trait is implemented -on: Rust doesn’t have reflection capabilities, so it can’t look up the type’s -name at runtime. We need a macro to generate code at compile time. +علاوه بر این، ما هنوز نمی‌توانیم برای تابع `hello_macro` یک پیاده‌سازی پیش‌فرض ارائه دهیم که نام نوعی که ویژگی روی آن پیاده‌سازی شده است را چاپ کند: Rust قابلیت‌های بازتاب (reflection) ندارد، بنابراین نمی‌تواند نام نوع را در زمان اجرا جستجو کند. ما به یک ماکرو نیاز داریم تا کد را در زمان کامپایل تولید کند. -The next step is to define the procedural macro. At the time of this writing, -procedural macros need to be in their own crate. Eventually, this restriction -might be lifted. The convention for structuring crates and macro crates is as -follows: for a crate named `foo`, a custom `derive` procedural macro crate is -called `foo_derive`. Let’s start a new crate called `hello_macro_derive` inside -our `hello_macro` project: +گام بعدی تعریف ماکروی رویه‌ای است. در زمان نگارش این مطلب، ماکروهای رویه‌ای باید در یک کرِیت جداگانه قرار داشته باشند. ممکن است این محدودیت در آینده برداشته شود. قرارداد ساختاردهی کرِیت‌ها و کرِیت‌های ماکرو به این صورت است: برای کرِیتی به نام `foo`، کرِیت ماکروی `derive` سفارشی با نام `foo_derive` شناخته می‌شود. بیایید یک کرِیت جدید با نام `hello_macro_derive` درون پروژه `hello_macro` خود ایجاد کنیم: ```console $ cargo new hello_macro_derive --lib ``` -Our two crates are tightly related, so we create the procedural macro crate -within the directory of our `hello_macro` crate. If we change the trait -definition in `hello_macro`, we’ll have to change the implementation of the -procedural macro in `hello_macro_derive` as well. The two crates will need to -be published separately, and programmers using these crates will need to add -both as dependencies and bring them both into scope. We could instead have the -`hello_macro` crate use `hello_macro_derive` as a dependency and re-export the -procedural macro code. However, the way we’ve structured the project makes it -possible for programmers to use `hello_macro` even if they don’t want the -`derive` functionality. - -We need to declare the `hello_macro_derive` crate as a procedural macro crate. -We’ll also need functionality from the `syn` and `quote` crates, as you’ll see -in a moment, so we need to add them as dependencies. Add the following to the -_Cargo.toml_ file for `hello_macro_derive`: +دو crate ما به شدت به هم مرتبط هستند، بنابراین ما crate ماکروی رویه‌ای را درون دایرکتوری crate `hello_macro` ایجاد می‌کنیم. اگر تعریف ویژگی را در `hello_macro` تغییر دهیم، باید پیاده‌سازی ماکروی رویه‌ای در `hello_macro_derive` را نیز تغییر دهیم. این دو crate باید به طور جداگانه منتشر شوند و برنامه‌نویسانی که از این جعبه‌ها (crates) استفاده می‌کنند باید هر دو را به عنوان وابستگی اضافه کرده و آن‌ها را به دامنه بیاورند. در عوض، می‌توانستیم crate `hello_macro` از `hello_macro_derive` به عنوان یک وابستگی استفاده کند و کد ماکروی رویه‌ای را دوباره صادر کند. با این حال، روشی که پروژه را ساختاربندی کرده‌ایم، این امکان را فراهم می‌کند که برنامه‌نویسان از `hello_macro` حتی اگر عملکرد `derive` را نخواهند، استفاده کنند. + +ما باید crate `hello_macro_derive` را به عنوان یک crate ماکروی رویه‌ای اعلام کنیم. همچنین به عملکردهایی از جعبه‌ها (crates)ی `syn` و `quote` نیاز خواهیم داشت، همان‌طور که به زودی خواهید دید، بنابراین باید آن‌ها را به عنوان وابستگی اضافه کنیم. موارد زیر را به فایل _Cargo.toml_ برای `hello_macro_derive` اضافه کنید: <Listing file-name="hello_macro_derive/Cargo.toml"> @@ -282,11 +156,9 @@ _Cargo.toml_ file for `hello_macro_derive`: </Listing> -To start defining the procedural macro, place the code in Listing 20-40 into -your _src/lib.rs_ file for the `hello_macro_derive` crate. Note that this code -won’t compile until we add a definition for the `impl_hello_macro` function. +برای شروع تعریف ماکروی رویه‌ای، کد موجود در لیست 20-40 را در فایل *src/lib.rs* کرِیت `hello_macro_derive` قرار دهید. توجه داشته باشید که این کد تا زمانی که یک تعریف برای تابع `impl_hello_macro` اضافه نکنیم، کامپایل نخواهد شد. -<Listing number="20-40" file-name="hello_macro_derive/src/lib.rs" caption="Code that most procedural macro crates will require in order to process Rust code"> +<Listing number="20-40" file-name="hello_macro_derive/src/lib.rs" caption="کدی که اکثر کرِیت‌های ماکروی رویه‌ای برای پردازش کد Rust نیاز دارند"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-40/hello_macro/hello_macro_derive/src/lib.rs}} @@ -294,41 +166,17 @@ won’t compile until we add a definition for the `impl_hello_macro` function. </Listing> -Notice that we’ve split the code into the `hello_macro_derive` function, which -is responsible for parsing the `TokenStream`, and the `impl_hello_macro` -function, which is responsible for transforming the syntax tree: this makes -writing a procedural macro more convenient. The code in the outer function -(`hello_macro_derive` in this case) will be the same for almost every -procedural macro crate you see or create. The code you specify in the body of -the inner function (`impl_hello_macro` in this case) will be different -depending on your procedural macro’s purpose. - -We’ve introduced three new crates: `proc_macro`, [`syn`][syn]<!--ignore -->, -and [`quote`][quote]<!-- ignore -->. The `proc_macro` crate comes with Rust, -so we didn’t need to add that to the dependencies in _Cargo.toml_. The -`proc_macro` crate is the compiler’s API that allows us to read and manipulate -Rust code from our code. - -The `syn` crate parses Rust code from a string into a data structure that we -can perform operations on. The `quote` crate turns `syn` data structures back -into Rust code. These crates make it much simpler to parse any sort of Rust -code we might want to handle: writing a full parser for Rust code is no simple -task. - -The `hello_macro_derive` function will be called when a user of our library -specifies `#[derive(HelloMacro)]` on a type. This is possible because we’ve -annotated the `hello_macro_derive` function here with `proc_macro_derive` and -specified the name `HelloMacro`, which matches our trait name; this is the -convention most procedural macros follow. - -The `hello_macro_derive` function first converts the `input` from a -`TokenStream` to a data structure that we can then interpret and perform -operations on. This is where `syn` comes into play. The `parse` function in -`syn` takes a `TokenStream` and returns a `DeriveInput` struct representing the -parsed Rust code. Listing 20-41 shows the relevant parts of the `DeriveInput` -struct we get from parsing the `struct Pancakes;` string. - -<Listing number="20-41" caption="The `DeriveInput` instance we get when parsing the code that has the macro’s attribute in Listing 20-37"> +توجه کنید که کد را به دو تابع تقسیم کرده‌ایم: `hello_macro_derive` که مسئول پردازش `TokenStream` است، و `impl_hello_macro` که مسئول تبدیل درخت نحوی است. این کار نوشتن یک ماکروی رویه‌ای را آسان‌تر می‌کند. کد تابع بیرونی (`hello_macro_derive` در اینجا) تقریباً برای تمام جعبه‌ها (crates)ی ماکروی رویه‌ای که می‌بینید یا ایجاد می‌کنید یکسان خواهد بود. کدی که در بدنه تابع داخلی (`impl_hello_macro` در اینجا) مشخص می‌کنید بسته به هدف ماکروی رویه‌ای شما متفاوت خواهد بود. + +ما سه crate جدید معرفی کرده‌ایم: `proc_macro`، [`syn`]، و [`quote`]. crate `proc_macro` همراه با Rust ارائه می‌شود، بنابراین نیازی به افزودن آن به وابستگی‌ها در _Cargo.toml_ نداریم. crate `proc_macro` API کامپایلر است که به ما اجازه می‌دهد کد Rust را از کد خود بخوانیم و دستکاری کنیم. + +crate `syn` کد Rust را از یک رشته به یک ساختار داده‌ای تبدیل می‌کند که می‌توانیم عملیات روی آن انجام دهیم. crate `quote` ساختارهای داده `syn` را دوباره به کد Rust تبدیل می‌کند. این جعبه‌ها (crates) پردازش هر نوع کد Rust که بخواهیم مدیریت کنیم را بسیار ساده‌تر می‌کنند: نوشتن یک تجزیه‌کننده کامل برای کد Rust کار ساده‌ای نیست. + +تابع `hello_macro_derive` زمانی فراخوانی می‌شود که یک کاربر از کتابخانه ما ویژگی `#[derive(HelloMacro)]` را روی یک نوع مشخص کند. این امر به این دلیل ممکن است که ما تابع `hello_macro_derive` را با `proc_macro_derive` حاشیه‌نویسی کرده‌ایم و نام `HelloMacro` را مشخص کرده‌ایم، که با نام ویژگی ما مطابقت دارد؛ این روش معمولی‌ای است که بیشتر ماکروهای رویه‌ای دنبال می‌کنند. + +تابع `hello_macro_derive` ابتدا `input` را از یک `TokenStream` به یک ساختار داده تبدیل می‌کند که سپس می‌توانیم آن را تفسیر کرده و عملیات‌هایی روی آن انجام دهیم. اینجاست که crate `syn` به کار می‌آید. تابع `parse` در `syn` یک `TokenStream` می‌گیرد و یک ساختار `DeriveInput` را که نمایانگر کد Rust تجزیه‌شده است، بازمی‌گرداند. لیست ۲۰-۳۳ بخش‌های مرتبط از ساختار `DeriveInput` را نشان می‌دهد که هنگام تجزیه کد `struct Pancakes;` دریافت می‌کنیم: + +<Listing number="20-41" caption="نمونه‌ای از `DeriveInput` که هنگام تجزیه کدی که ویژگی ماکرو در لیست 20-37 را دارد، دریافت می‌کنیم"> ```rust,ignore DeriveInput { @@ -352,31 +200,15 @@ DeriveInput { </Listing> -The fields of this struct show that the Rust code we’ve parsed is a unit struct -with the `ident` (_identifier_, meaning the name) of `Pancakes`. There are more -fields on this struct for describing all sorts of Rust code; check the [`syn` -documentation for `DeriveInput`][syn-docs] for more information. +فیلدهای این `struct` نشان می‌دهند که کدی که در Rust تجزیه کرده‌ایم یک ساختار واحد (unit struct) با `ident` (شناساگر، یعنی نام) به نام `Pancakes` است. فیلدهای بیشتری نیز در این `struct` وجود دارند که برای توصیف انواع مختلفی از کدهای Rust استفاده می‌شوند؛ برای اطلاعات بیشتر به [مستندات `syn` درباره‌ی `DeriveInput`][syn-docs] مراجعه کنید. -Soon we’ll define the `impl_hello_macro` function, which is where we’ll build -the new Rust code we want to include. But before we do, note that the output -for our `derive` macro is also a `TokenStream`. The returned `TokenStream` is -added to the code that our crate users write, so when they compile their crate, -they’ll get the extra functionality that we provide in the modified -`TokenStream`. +به‌زودی تابع `impl_hello_macro` را تعریف خواهیم کرد؛ این تابع جایی است که کد جدید Rust را که می‌خواهیم به کد اضافه کنیم، تولید خواهیم کرد. اما پیش از آن، توجه داشته باشید که خروجی ماکروی `derive` ما نیز یک `TokenStream` است. این `TokenStream` بازگشتی به کدی که کاربران crate ما نوشته‌اند اضافه می‌شود، بنابراین زمانی که crate خود را کامپایل می‌کنند، عملکرد اضافه‌ای که ما در `TokenStream` تغییر‌یافته فراهم کرده‌ایم به کدشان افزوده خواهد شد. -You might have noticed that we’re calling `unwrap` to cause the -`hello_macro_derive` function to panic if the call to the `syn::parse` function -fails here. It’s necessary for our procedural macro to panic on errors because -`proc_macro_derive` functions must return `TokenStream` rather than `Result` to -conform to the procedural macro API. We’ve simplified this example by using -`unwrap`; in production code, you should provide more specific error messages -about what went wrong by using `panic!` or `expect`. +ممکن است متوجه شده باشید که ما از `unwrap` استفاده می‌کنیم تا در صورتی که فراخوانی تابع `syn::parse` شکست بخورد، تابع `hello_macro_derive` به وحشت بیفتد (_panic_). لازم است ماکروی رویه‌ای ما در صورت بروز خطا به وحشت بیفتد، زیرا توابع `proc_macro_derive` باید به جای `Result` یک `TokenStream` بازگردانند تا با API ماکروهای رویه‌ای سازگار باشند. برای ساده کردن این مثال از `unwrap` استفاده کرده‌ایم؛ در کد تولیدی، بهتر است پیام‌های خطای خاص‌تری درباره مشکل ایجاد شده با استفاده از `panic!` یا `expect` ارائه دهید. -Now that we have the code to turn the annotated Rust code from a `TokenStream` -into a `DeriveInput` instance, let’s generate the code that implements the -`HelloMacro` trait on the annotated type, as shown in Listing 20-42. +اکنون که کدی داریم که کد Rust حاشیه‌نویسی‌شده را از یک `TokenStream` به نمونه‌ای از `DeriveInput` تبدیل می‌کند، بیایید کدی را تولید کنیم که `trait` مربوط به `HelloMacro` را برای نوع حاشیه‌نویسی‌شده پیاده‌سازی می‌کند، همان‌طور که در لیستینگ 20-42 نشان داده شده است. -<Listing number="20-42" file-name="hello_macro_derive/src/lib.rs" caption="Implementing the `HelloMacro` trait using the parsed Rust code"> +<Listing number="20-42" file-name="hello_macro_derive/src/lib.rs" caption="پیاده‌سازی trait مربوط به `HelloMacro` با استفاده از کد Rust تجزیه‌شده"> ```rust,ignore {{#rustdoc_include ../listings/ch20-advanced-features/listing-20-42/hello_macro/hello_macro_derive/src/lib.rs:here}} @@ -384,133 +216,74 @@ into a `DeriveInput` instance, let’s generate the code that implements the </Listing> -We get an `Ident` struct instance containing the name (identifier) of the -annotated type using `ast.ident`. The struct in Listing 20-41 shows that when -we run the `impl_hello_macro` function on the code in Listing 20-37, the -`ident` we get will have the `ident` field with a value of `"Pancakes"`. Thus -the `name` variable in Listing 20-42 will contain an `Ident` struct instance -that, when printed, will be the string `"Pancakes"`, the name of the struct in -Listing 20-37. - -The `quote!` macro lets us define the Rust code that we want to return. The -compiler expects something different to the direct result of the `quote!` -macro’s execution, so we need to convert it to a `TokenStream`. We do this by -calling the `into` method, which consumes this intermediate representation and -returns a value of the required `TokenStream` type. - -The `quote!` macro also provides some very cool templating mechanics: we can -enter `#name`, and `quote!` will replace it with the value in the variable -`name`. You can even do some repetition similar to the way regular macros work. -Check out [the `quote` crate’s docs][quote-docs] for a thorough introduction. - -We want our procedural macro to generate an implementation of our `HelloMacro` -trait for the type the user annotated, which we can get by using `#name`. The -trait implementation has the one function `hello_macro`, whose body contains the -functionality we want to provide: printing `Hello, Macro! My name is` and then -the name of the annotated type. - -The `stringify!` macro used here is built into Rust. It takes a Rust -expression, such as `1 + 2`, and at compile time turns the expression into a -string literal, such as `"1 + 2"`. This is different from `format!` or -`println!`, macros which evaluate the expression and then turn the result into -a `String`. There is a possibility that the `#name` input might be an -expression to print literally, so we use `stringify!`. Using `stringify!` also -saves an allocation by converting `#name` to a string literal at compile time. - -At this point, `cargo build` should complete successfully in both `hello_macro` -and `hello_macro_derive`. Let’s hook up these crates to the code in Listing -20-37 to see the procedural macro in action! Create a new binary project in -your _projects_ directory using `cargo new pancakes`. We need to add -`hello_macro` and `hello_macro_derive` as dependencies in the `pancakes` -crate’s _Cargo.toml_. If you’re publishing your versions of `hello_macro` and -`hello_macro_derive` to [crates.io](https://crates.io/)<!-- ignore -->, they -would be regular dependencies; if not, you can specify them as `path` -dependencies as follows: +با استفاده از `ast.ident`، یک نمونه از ساختار `Ident` دریافت می‌کنیم که شامل نام (identifier) نوعی است که با ماکرو حاشیه‌نویسی شده است. ساختار نشان‌داده‌شده در لیستینگ 20-41 نشان می‌دهد که زمانی که تابع `impl_hello_macro` را بر روی کد موجود در لیستینگ 20-37 اجرا کنیم، فیلد `ident` که دریافت می‌کنیم، دارای مقدار `"Pancakes"` خواهد بود. بنابراین، متغیر `name` در لیستینگ 20-42 شامل یک نمونه از ساختار `Ident` خواهد بود که هنگام چاپ، رشته `"Pancakes"` را نشان می‌دهد؛ یعنی نام `struct` موجود در لیستینگ 20-37. + +ماکروی `quote!` به ما اجازه می‌دهد کد Rust مورد نظر خود برای بازگرداندن را تعریف کنیم. کامپایلر به چیزی متفاوت از نتیجه مستقیم اجرای ماکروی `quote!` نیاز دارد، بنابراین باید آن را به یک `TokenStream` تبدیل کنیم. این کار را با فراخوانی متد `into` انجام می‌دهیم که این نمایش میانی را مصرف کرده و مقداری از نوع `TokenStream` مورد نیاز بازمی‌گرداند. + +ماکروی `quote!` همچنین برخی قابلیت‌های جالب الگوگذاری (templating) ارائه می‌دهد: می‌توانیم `#name` را وارد کنیم و `quote!` آن را با مقدار موجود در متغیر `name` جایگزین می‌کند. حتی می‌توانید تکرارهایی مشابه با نحوه کار ماکروهای معمولی انجام دهید. برای مقدمه‌ای جامع به [مستندات crate `quote`][quote-docs] مراجعه کنید. + +ما می‌خواهیم ماکروی رویه‌ای ما یک پیاده‌سازی از ویژگی `HelloMacro` برای نوعی که کاربر حاشیه‌نویسی کرده است تولید کند، که می‌توانیم با استفاده از `#name` به آن دسترسی پیدا کنیم. پیاده‌سازی ویژگی شامل یک تابع به نام `hello_macro` است که بدنه آن قابلیت مورد نظر ما، یعنی چاپ `Hello, Macro! My name is` و سپس نام نوع حاشیه‌نویسی‌شده، را ارائه می‌دهد. + +ماکروی `stringify!` که در این‌جا استفاده شده، یکی از ماکروهای توکار در Rust است. این ماکرو یک عبارت Rust، مانند `1 + 2` را می‌گیرد و در زمان کامپایل آن را به یک رشتهٔ متنی (string literal)، مانند `"1 + 2"` تبدیل می‌کند. این رفتار با ماکروهایی مانند `format!` یا `println!` متفاوت است؛ چرا که آن‌ها ابتدا مقدار عبارت را ارزیابی می‌کنند و سپس نتیجه را به یک `String` تبدیل می‌کنند. از آن‌جا که امکان دارد ورودی `#name` یک عبارت باشد که باید به صورت متنی چاپ شود، از `stringify!` استفاده می‌کنیم. همچنین استفاده از `stringify!` باعث صرفه‌جویی در حافظه می‌شود زیرا `#name` را در زمان کامپایل به یک رشتهٔ متنی تبدیل می‌کند. + +در این مرحله، اجرای دستور `cargo build` باید در هر دو crate یعنی `hello_macro` و `hello_macro_derive` با موفقیت کامل شود. حال بیایید این دو crate را به کدی که در لیستینگ 20-37 آمده متصل کنیم تا عملکرد ماکروی procedural را در عمل ببینیم! در دایرکتوری *projects* خود، یک پروژهٔ باینری جدید با دستور `cargo new pancakes` ایجاد کنید. سپس باید `hello_macro` و `hello_macro_derive` را به عنوان وابستگی در فایل *Cargo.toml* مربوط به crate پروژهٔ `pancakes` اضافه کنید. اگر قصد دارید نسخه‌های خود از `hello_macro` و `hello_macro_derive` را در [crates.io](https://crates.io/)<!-- ignore --> منتشر کنید، آن‌ها را به‌صورت وابستگی معمولی اضافه کنید؛ در غیر این صورت، می‌توانید آن‌ها را به‌صورت وابستگی مسیر (`path`) مانند نمونهٔ زیر مشخص کنید: ```toml {{#include ../listings/ch20-advanced-features/no-listing-21-pancakes/pancakes/Cargo.toml:6:8}} ``` -Put the code in Listing 20-37 into _src/main.rs_, and run `cargo run`: it -should print `Hello, Macro! My name is Pancakes!` The implementation of the -`HelloMacro` trait from the procedural macro was included without the -`pancakes` crate needing to implement it; the `#[derive(HelloMacro)]` added the -trait implementation. +کد موجود در لیستینگ 20-37 را در فایل *src/main.rs* قرار دهید و سپس دستور `cargo run` را اجرا کنید؛ باید خروجی زیر را مشاهده کنید: + +``` +Hello, Macro! My name is Pancakes! +``` + +پیاده‌سازی `trait` مربوط به `HelloMacro` توسط ماکروی procedural اضافه شده است، بدون این‌که crate مربوط به `pancakes` نیاز داشته باشد آن را خودش پیاده‌سازی کند؛ استفاده از `#[derive(HelloMacro)]` باعث شد پیاده‌سازی `trait` به کد اضافه شود. -Next, let’s explore how the other kinds of procedural macros differ from custom -`derive` macros. +اکنون، بیایید بررسی کنیم که سایر انواع ماکروهای procedural چه تفاوتی با ماکروهای سفارشی `derive` دارند. -### Attribute-Like Macros +### ماکروهای شبیه به صفت (Attribute-Like Macros) -Attribute-like macros are similar to custom `derive` macros, but instead of -generating code for the `derive` attribute, they allow you to create new -attributes. They’re also more flexible: `derive` only works for structs and -enums; attributes can be applied to other items as well, such as functions. -Here’s an example of using an attribute-like macro. Say you have an attribute -named `route` that annotates functions when using a web application framework: +ماکروهای شبیه به صفت شباهت زیادی به ماکروهای سفارشی `derive` دارند، با این تفاوت که به‌جای تولید کد برای صفت `derive`، به شما اجازه می‌دهند که صفت‌های جدید ایجاد کنید. این نوع ماکروها انعطاف‌پذیرتر نیز هستند: `derive` فقط برای `struct`ها و `enum`ها کار می‌کند؛ در حالی که صفات می‌توانند روی سایر آیتم‌ها نیز اعمال شوند، مانند توابع. در ادامه، نمونه‌ای از استفاده از یک ماکروی شبیه به صفت را مشاهده می‌کنید. فرض کنید صفتی به نام `route` دارید که برای علامت‌گذاری توابع در یک فریم‌ورک برنامه‌های وب استفاده می‌شود: ```rust,ignore #[route(GET, "/")] fn index() { ``` -This `#[route]` attribute would be defined by the framework as a procedural -macro. The signature of the macro definition function would look like this: +این ویژگی `#[route]` توسط فریم‌ورک به عنوان یک ماکروی رویه‌ای تعریف می‌شود. امضای تابع تعریف ماکرو به این صورت خواهد بود: ```rust,ignore #[proc_macro_attribute] pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream { ``` -Here, we have two parameters of type `TokenStream`. The first is for the -contents of the attribute: the `GET, "/"` part. The second is the body of the -item the attribute is attached to: in this case, `fn index() {}` and the rest -of the function’s body. +در اینجا، دو پارامتر از نوع `TokenStream` داریم. پارامتر اول برای محتوای ویژگی است: بخش `GET, "/"`. پارامتر دوم برای بدنه آیتمی است که ویژگی به آن متصل شده است: در این مورد، `fn index() {}` و باقی بدنه تابع. -Other than that, attribute-like macros work the same way as custom `derive` -macros: you create a crate with the `proc-macro` crate type and implement a -function that generates the code you want! +به‌جز این تفاوت، ماکروهای شبیه به صفت دقیقاً به همان روشی کار می‌کنند که ماکروهای سفارشی `derive` کار می‌کنند: شما یک crate با نوع `proc-macro` ایجاد می‌کنید و تابعی را پیاده‌سازی می‌کنید که کدی را تولید می‌کند که می‌خواهید! -### Function-Like Macros +### ماکروهای شبیه تابع (Function-Like Macros) -Function-like macros define macros that look like function calls. Similarly to -`macro_rules!` macros, they’re more flexible than functions; for example, they -can take an unknown number of arguments. However, `macro_rules!` macros can only -be defined using the match-like syntax we discussed in [“Declarative Macros with -`macro_rules!` for General Metaprogramming”][decl]<!-- ignore --> earlier. -Function-like macros take a `TokenStream` parameter, and their definition -manipulates that `TokenStream` using Rust code as the other two types of -procedural macros do. An example of a function-like macro is an `sql!` macro -that might be called like so: +ماکروهای شبیه تابع، ماکروهایی هستند که شبیه به فراخوانی تابع به نظر می‌رسند. مشابه ماکروهای `macro_rules!`، این ماکروها نسبت به توابع انعطاف‌پذیرتر هستند؛ برای مثال، می‌توانند تعداد نامشخصی از آرگومان‌ها را بپذیرند. با این حال، ماکروهای `macro_rules!` فقط می‌توانند با استفاده از نحوی مشابه `match` که قبلاً در بخش \[«ماکروهای اعلامی با `macro_rules!` برای فرا-برنامه‌نویسی عمومی»]\[decl]<!-- ignore --> بحث شد، تعریف شوند. ماکروهای شبیه تابع یک پارامتر از نوع `TokenStream` می‌گیرند و تعریف آن‌ها با استفاده از کد Rust، مانند دو نوع دیگر از ماکروهای procedural، آن `TokenStream` را پردازش می‌کند. برای مثال، یک ماکروی `sql!` را در نظر بگیرید که ممکن است به صورت زیر فراخوانی شود: ```rust,ignore let sql = sql!(SELECT * FROM posts WHERE id=1); ``` -This macro would parse the SQL statement inside it and check that it’s -syntactically correct, which is much more complex processing than a -`macro_rules!` macro can do. The `sql!` macro would be defined like this: +این ماکرو عبارت SQL داخل خود را تجزیه کرده و بررسی می‌کند که از نظر نحوی درست باشد، که پردازش بسیار پیچیده‌تری نسبت به آنچه یک ماکروی `macro_rules!` می‌تواند انجام دهد، دارد. ماکروی `sql!` به این صورت تعریف می‌شود: ```rust,ignore #[proc_macro] pub fn sql(input: TokenStream) -> TokenStream { ``` -This definition is similar to the custom `derive` macro’s signature: we receive -the tokens that are inside the parentheses and return the code we wanted to -generate. +این تعریف مشابه امضای ماکروی سفارشی `derive` است: توکن‌هایی را که داخل پرانتز قرار دارند دریافت می‌کنیم و کدی را که می‌خواهیم تولید کنیم بازمی‌گردانیم. -## Summary +## خلاصه -Whew! Now you have some Rust features in your toolbox that you likely won’t use -often, but you’ll know they’re available in very particular circumstances. -We’ve introduced several complex topics so that when you encounter them in -error message suggestions or in other people’s code, you’ll be able to -recognize these concepts and syntax. Use this chapter as a reference to guide -you to solutions. +ووف! حالا شما با برخی از ویژگی‌های Rust آشنا شده‌اید که احتمالاً زیاد از آن‌ها استفاده نخواهید کرد، اما می‌دانید که در شرایط خاصی در دسترس هستند. ما چندین موضوع پیچیده را معرفی کردیم تا زمانی که در پیام‌های خطا یا در کد دیگران با آن‌ها روبه‌رو شدید، بتوانید این مفاهیم و نحو را تشخیص دهید. از این فصل به‌عنوان یک مرجع استفاده کنید تا شما را به‌سمت راه‌حل‌ها راهنمایی کند. -Next, we’ll put everything we’ve discussed throughout the book into practice -and do one more project! +در ادامه، همه چیزهایی که در طول کتاب بحث کردیم را در عمل پیاده‌سازی می‌کنیم و یک پروژه دیگر انجام خواهیم داد! [ref]: ../reference/macros-by-example.html [tlborm]: https://veykril.github.io/tlborm/ diff --git a/src/ch21-00-final-project-a-web-server.md b/src/ch21-00-final-project-a-web-server.md index 0da101fc4c..1780314396 100644 --- a/src/ch21-00-final-project-a-web-server.md +++ b/src/ch21-00-final-project-a-web-server.md @@ -1,41 +1,33 @@ -# Final Project: Building a Multithreaded Web Server +# پروژه نهایی: ساخت یک وب سرور چندنخی -It’s been a long journey, but we’ve reached the end of the book. In this -chapter, we’ll build one more project together to demonstrate some of the -concepts we covered in the final chapters, as well as recap some earlier -lessons. +مسیر طولانی‌ای را طی کرده‌ایم، اما اکنون به انتهای کتاب رسیده‌ایم. در این فصل، یک پروژه دیگر را با هم می‌سازیم تا برخی از مفاهیمی که در فصل‌های پایانی پوشش داده‌ایم را نشان دهیم و همچنین درس‌های قبلی را مرور کنیم. -For our final project, we’ll make a web server that says “hello” and looks like -Figure 21-1 in a web browser. +برای پروژه نهایی، یک وب سرور ایجاد می‌کنیم که عبارت "hello" را نمایش دهد و در یک مرورگر وب شبیه شکل 21-1 به نظر برسد. -Here is our plan for building the web server: +![hello from rust](img/trpl21-01.png) + +<span class="caption">شکل 21-1: پروژه نهایی ما</span> -1. Learn a bit about TCP and HTTP. -2. Listen for TCP connections on a socket. -3. Parse a small number of HTTP requests. -4. Create a proper HTTP response. -5. Improve the throughput of our server with a thread pool. +برنامه ما برای ساخت وب سرور به این صورت است: + +<div dir="rtl"> + <ul> + <li>کمی درباره TCP و HTTP یاد می‌گیریم.</li> + <li>گوش دادن به اتصالات TCP روی یک سوکت را پیاده‌سازی می‌کنیم.</li> + <li>تعداد کمی از درخواست‌های HTTP را تجزیه می‌کنیم.</li> + <li>یک پاسخ HTTP مناسب ایجاد می‌کنیم.</li> + <li>با استفاده از یک مجموعه نخ (thread pool) توان عملیاتی سرور را بهبود می‌بخشیم.</li> + </ul> +</div> ![hello from rust](img/trpl21-01.png) -<span class="caption">Figure 21-1: Our final shared project</span> - -Before we get started, we should mention two details. First, the method we’ll -use won’t be the best way to build a web server with Rust. Community members -have published a number of production-ready crates available at -[crates.io](https://crates.io/) that provide more complete web server and thread -pool implementations than we’ll build. However, our intention in this chapter is -to help you learn, not to take the easy route. Because Rust is a systems -programming language, we can choose the level of abstraction we want to work -with and can go to a lower level than is possible or practical in other -languages. - -Second, we will not be using async and await here. Building a thread pool is a -big enough challenge on its own, without adding in building an async runtime! -However, we will note how async and await might be applicable to some of the -same problems we will see in this chapter. Ultimately, as we noted back in -Chapter 17, many async runtimes use thread pools for managing their work. - -We’ll therefore write the basic HTTP server and thread pool manually so you can -learn the general ideas and techniques behind the crates you might use in the -future. +<span class="caption">شکل ۲۱-۱: پروژه نهایی مشترک ما</span> + +پیش از شروع، باید به دو نکته اشاره کنیم. اول اینکه روشی که در این فصل استفاده خواهیم کرد، بهترین راه برای ساخت یک وب‌سرور با Rust نیست. اعضای جامعه کاربری، کرِیت‌هایی در سطح تولید (production-ready) منتشر کرده‌اند که در [crates.io](https://crates.io/) در دسترس‌اند و پیاده‌سازی‌های کامل‌تری از وب‌سرور و استخر نخ (thread pool) نسبت به چیزی که ما خواهیم ساخت ارائه می‌دهند. با این حال، هدف ما در این فصل آموزش دادن است، نه انتخاب ساده‌ترین مسیر. از آن‌جا که Rust یک زبان برنامه‌نویسی سیستمی است، می‌توانیم سطح انتزاعی که می‌خواهیم با آن کار کنیم را انتخاب کنیم و حتی به سطحی پایین‌تر برویم؛ چیزی که در زبان‌های دیگر یا ممکن نیست یا در عمل کاربردی ندارد. + +قبل از شروع، باید به دو نکته اشاره کنیم: اول، روشی که استفاده خواهیم کرد بهترین روش برای ساخت یک وب سرور با Rust نخواهد بود. اعضای جامعه Rust تعداد زیادی crate آماده تولید در [crates.io](https://crates.io/) منتشر کرده‌اند که پیاده‌سازی‌های کامل‌تری از وب سرور و مجموعه نخ نسبت به آنچه که ما خواهیم ساخت ارائه می‌دهند. با این حال، هدف ما در این فصل کمک به یادگیری شماست، نه انتخاب مسیر آسان. از آنجا که Rust یک زبان برنامه‌نویسی سیستمی است، می‌توانیم سطح انتزاعی که می‌خواهیم با آن کار کنیم را انتخاب کنیم و به سطح پایین‌تری از آنچه در زبان‌های دیگر ممکن یا عملی است برویم. + +دوم، ما اینجا از async و await استفاده نخواهیم کرد. ساخت یک مجموعه نخ به اندازه کافی چالش‌برانگیز است، بدون اینکه به ایجاد یک runtime async اضافه شود! با این حال، اشاره خواهیم کرد که async و await چگونه ممکن است برای برخی از همان مشکلاتی که در این فصل خواهیم دید کاربرد داشته باشند. در نهایت، همان‌طور که در فصل 17 ذکر کردیم، بسیاری از runtime‌های async از مجموعه نخ برای مدیریت کارهای خود استفاده می‌کنند. + +بنابراین، سرور HTTP ساده و مجموعه نخ را به صورت دستی خواهیم نوشت تا بتوانید ایده‌ها و تکنیک‌های کلی پشت جعبه‌ها (crates)یی که ممکن است در آینده استفاده کنید را یاد بگیرید. diff --git a/src/ch21-01-single-threaded.md b/src/ch21-01-single-threaded.md index 62657f950a..57fbe61540 100644 --- a/src/ch21-01-single-threaded.md +++ b/src/ch21-01-single-threaded.md @@ -1,28 +1,14 @@ -## Building a Single-Threaded Web Server +## ساخت یک وب سرور Single-Threaded -We’ll start by getting a single-threaded web server working. Before we begin, -let’s look at a quick overview of the protocols involved in building web -servers. The details of these protocols are beyond the scope of this book, but -a brief overview will give you the information you need. +ما با راه‌اندازی یک وب سرور Single-Threaded شروع خواهیم کرد. پیش از شروع، بیایید یک مرور سریع بر پروتکل‌های مرتبط با ساخت وب سرورها داشته باشیم. جزئیات این پروتکل‌ها خارج از محدوده این کتاب است، اما یک نمای کلی اطلاعات لازم را به شما می‌دهد. -The two main protocols involved in web servers are _Hypertext Transfer -Protocol_ _(HTTP)_ and _Transmission Control Protocol_ _(TCP)_. Both protocols -are _request-response_ protocols, meaning a _client_ initiates requests and a -_server_ listens to the requests and provides a response to the client. The -contents of those requests and responses are defined by the protocols. +دو پروتکل اصلی که در وب سرورها درگیر هستند، _Hypertext Transfer Protocol_ _(HTTP)_ و _Transmission Control Protocol_ _(TCP)_ هستند. هر دو پروتکل _request-response_ هستند، به این معنی که یک _client_ درخواست‌ها را آغاز می‌کند و یک _server_ به درخواست‌ها گوش می‌دهد و پاسخی به client ارائه می‌دهد. محتوای این درخواست‌ها و پاسخ‌ها توسط پروتکل‌ها تعریف شده است. -TCP is the lower-level protocol that describes the details of how information -gets from one server to another but doesn’t specify what that information is. -HTTP builds on top of TCP by defining the contents of the requests and -responses. It’s technically possible to use HTTP with other protocols, but in -the vast majority of cases, HTTP sends its data over TCP. We’ll work with the -raw bytes of TCP and HTTP requests and responses. +TCP یک پروتکل سطح پایین‌تر است که جزئیات چگونگی انتقال اطلاعات از یک سرور به سرور دیگر را توصیف می‌کند اما مشخص نمی‌کند که آن اطلاعات چیست. HTTP بر روی TCP ساخته شده است و محتوای درخواست‌ها و پاسخ‌ها را تعریف می‌کند. از لحاظ فنی امکان استفاده از HTTP با سایر پروتکل‌ها وجود دارد، اما در اکثر موارد، HTTP داده‌های خود را بر روی TCP ارسال می‌کند. ما با بایت‌های خام درخواست‌ها و پاسخ‌های TCP و HTTP کار خواهیم کرد. -### Listening to the TCP Connection +### گوش دادن به اتصال TCP -Our web server needs to listen to a TCP connection, so that’s the first part -we’ll work on. The standard library offers a `std::net` module that lets us do -this. Let’s make a new project in the usual fashion: +وب سرور ما نیاز دارد به اتصال TCP گوش دهد، بنابراین این اولین بخشی است که روی آن کار می‌کنیم. کتابخانه استاندارد یک ماژول `std::net` ارائه می‌دهد که به ما این امکان را می‌دهد این کار را انجام دهیم. بیایید یک پروژه جدید به روش معمول ایجاد کنیم: ```console $ cargo new hello @@ -30,11 +16,9 @@ $ cargo new hello $ cd hello ``` -Now enter the code in Listing 21-1 in _src/main.rs_ to start. This code will -listen at the local address `127.0.0.1:7878` for incoming TCP streams. When it -gets an incoming stream, it will print `Connection established!`. +حالا کد لیست ۲۱-۱ را در فایل _src/main.rs_ وارد کنید تا شروع کنیم. این کد به آدرس محلی `127.0.0.1:7878` برای جریان‌های ورودی TCP گوش می‌دهد. وقتی یک جریان ورودی دریافت می‌کند، پیام `Connection established!` را چاپ می‌کند. -<Listing number="21-1" file-name="src/main.rs" caption="Listening for incoming streams and printing a message when we receive a stream"> +<Listing number="21-1" file-name="src/main.rs" caption="گوش دادن به جریان‌های ورودی و چاپ یک پیام هنگام دریافت یک جریان"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-01/src/main.rs}} @@ -42,53 +26,17 @@ gets an incoming stream, it will print `Connection established!`. </Listing> -Using `TcpListener`, we can listen for TCP connections at the address -`127.0.0.1:7878`. In the address, the section before the colon is an IP address -representing your computer (this is the same on every computer and doesn’t -represent the authors’ computer specifically), and `7878` is the port. We’ve -chosen this port for two reasons: HTTP isn’t normally accepted on this port, so -our server is unlikely to conflict with any other web server you might have -running on your machine, and 7878 is _rust_ typed on a telephone. - -The `bind` function in this scenario works like the `new` function in that it -will return a new `TcpListener` instance. The function is called `bind` -because, in networking, connecting to a port to listen to is known as “binding -to a port.” - -The `bind` function returns a `Result<T, E>`, which indicates that it’s -possible for binding to fail. For example, if we ran two instances of our -program and so had two programs listening to the same port. Because we’re -writing a basic server just for learning purposes, we won’t worry about -handling these kinds of errors; instead, we use `unwrap` to stop the program if -errors happen. - -The `incoming` method on `TcpListener` returns an iterator that gives us a -sequence of streams (more specifically, streams of type `TcpStream`). A single -_stream_ represents an open connection between the client and the server. A -_connection_ is the name for the full request and response process in which a -client connects to the server, the server generates a response, and the server -closes the connection. As such, we will read from the `TcpStream` to see what -the client sent and then write our response to the stream to send data back to -the client. Overall, this `for` loop will process each connection in turn and -produce a series of streams for us to handle. - -For now, our handling of the stream consists of calling `unwrap` to terminate -our program if the stream has any errors; if there aren’t any errors, the -program prints a message. We’ll add more functionality for the success case in -the next listing. The reason we might receive errors from the `incoming` method -when a client connects to the server is that we’re not actually iterating over -connections. Instead, we’re iterating over _connection attempts_. The -connection might not be successful for a number of reasons, many of them -operating system specific. For example, many operating systems have a limit to -the number of simultaneous open connections they can support; new connection -attempts beyond that number will produce an error until some of the open -connections are closed. - -Let’s try running this code! Invoke `cargo run` in the terminal and then load -_127.0.0.1:7878_ in a web browser. The browser should show an error message -like “Connection reset” because the server isn’t currently sending back any -data. But when you look at your terminal, you should see several messages that -were printed when the browser connected to the server! +با استفاده از `TcpListener`، می‌توانیم به اتصالات TCP در آدرس `127.0.0.1:7878` گوش دهیم. در این آدرس، بخش قبل از دونقطه یک آدرس IP است که نمایانگر کامپیوتر شما است (این آدرس روی همه کامپیوترها یکسان است و نمایانگر کامپیوتر نویسندگان نیست) و `7878` پورت است. این پورت را به دو دلیل انتخاب کرده‌ایم: HTTP معمولاً روی این پورت پذیرفته نمی‌شود، بنابراین احتمالاً سرور ما با هیچ وب سرور دیگری که ممکن است روی دستگاه شما اجرا شود تداخل نخواهد داشت، و 7878 روی تلفن به صورت _rust_ تایپ می‌شود. + +تابع `bind` در این سناریو مانند تابع `new` عمل می‌کند به این صورت که یک نمونه جدید از `TcpListener` بازمی‌گرداند. این تابع `bind` نامیده می‌شود زیرا در شبکه، اتصال به یک پورت برای گوش دادن به آن به عنوان "binding to a port" شناخته می‌شود. + +تابع `bind` مقداری از نوع `Result<T, E>` برمی‌گرداند، که نشان می‌دهد امکان شکست در عملیات bind وجود دارد. برای مثال، اگر دو نمونه از برنامه‌ی ما به‌طور هم‌زمان اجرا شوند و هر دو بخواهند به یک پورت گوش دهند، این عملیات ممکن است شکست بخورد. از آن‌جا که ما در حال نوشتن یک سرور ساده فقط برای اهداف آموزشی هستیم، نگران مدیریت این نوع خطاها نخواهیم بود؛ در عوض، از تابع `unwrap` استفاده می‌کنیم تا در صورت بروز خطا، برنامه متوقف شود. + +متد `incoming` روی `TcpListener` یک iterator بازمی‌گرداند که به ما دنباله‌ای از جریان‌ها (_streams_) می‌دهد (به طور خاص، جریان‌هایی از نوع `TcpStream`). یک _stream_ نشان‌دهنده یک اتصال باز بین client و server است. یک _connection_ به فرآیند کامل درخواست و پاسخ گفته می‌شود که در آن یک client به سرور متصل می‌شود، سرور یک پاسخ تولید می‌کند، و سپس اتصال توسط سرور بسته می‌شود. بنابراین، ما از `TcpStream` برای خواندن آنچه client ارسال کرده استفاده می‌کنیم و سپس پاسخ خود را به جریان می‌نویسیم تا داده‌ها را به client بازگردانیم. به طور کلی، این حلقه `for` هر اتصال را به نوبت پردازش کرده و یک سری جریان‌ها را برای مدیریت به ما می‌دهد. + +فعلاً، مدیریت ما روی جریان به فراخوانی `unwrap` محدود می‌شود تا در صورتی که جریان دارای خطا باشد، برنامه متوقف شود. اگر خطایی وجود نداشته باشد، برنامه یک پیام چاپ می‌کند. در لیست بعدی، عملکرد بیشتری برای حالت موفقیت اضافه خواهیم کرد. دلیل اینکه ممکن است از متد `incoming` هنگام اتصال یک client به سرور خطا دریافت کنیم این است که ما در واقع روی اتصالات تکرار نمی‌کنیم، بلکه روی _تلاش‌های اتصال_ تکرار می‌کنیم. اتصال ممکن است به دلایل مختلف موفقیت‌آمیز نباشد که بسیاری از آن‌ها مربوط به سیستم‌عامل هستند. برای مثال، بسیاری از سیستم‌عامل‌ها محدودیتی برای تعداد اتصالات همزمان باز دارند؛ تلاش‌های اتصال جدیدی که بیش از این تعداد باشند، تا زمانی که برخی از اتصالات باز بسته نشوند، خطا تولید خواهند کرد. + +بیایید اجرای این کد را امتحان کنیم! دستور `cargo run` را در ترمینال اجرا کنید و سپس آدرس *127.0.0.1:7878* را در یک مرورگر وب بارگذاری کنید. مرورگر باید پیامی مانند «Connection reset» نمایش دهد، چون سرور در حال حاضر هیچ داده‌ای ارسال نمی‌کند. اما وقتی به ترمینال خود نگاه کنید، باید چندین پیام ببینید که هنگام اتصال مرورگر به سرور چاپ شده‌اند! ```text Running `target/debug/hello` @@ -97,42 +45,21 @@ Connection established! Connection established! ``` -Sometimes you’ll see multiple messages printed for one browser request; the -reason might be that the browser is making a request for the page as well as a -request for other resources, like the _favicon.ico_ icon that appears in the -browser tab. +گاهی ممکن است برای یک درخواست مرورگر، چندین پیام چاپ‌شده ببینید؛ دلیل این موضوع می‌تواند این باشد که مرورگر، علاوه بر درخواست صفحه، برای منابع دیگری مانند آیکون *favicon.ico* که در تب مرورگر نمایش داده می‌شود نیز درخواست ارسال می‌کند. -It could also be that the browser is trying to connect to the server multiple -times because the server isn’t responding with any data. When `stream` goes out -of scope and is dropped at the end of the loop, the connection is closed as -part of the `drop` implementation. Browsers sometimes deal with closed -connections by retrying, because the problem might be temporary. +همچنین ممکن است مرورگر چندین بار تلاش کند تا به سرور متصل شود، چون سرور هیچ داده‌ای پاسخ نمی‌دهد. وقتی `stream` از محدوده خارج شده و در انتهای حلقه `drop` می‌شود، اتصال به عنوان بخشی از پیاده‌سازی `drop` بسته می‌شود. مرورگرها گاهی با اتصال‌های بسته‌شده با تلاش مجدد برخورد می‌کنند، چون ممکن است مشکل موقتی باشد. -Browsers also sometimes open multiple connections to the server without sending -any requests, so that if they *do* later send requests, those requests can -happen faster. When this happens, our server will see each connection, -regardless of whether there are any requests over that connection. Many -versions of Chrome-based browsers do this, for example; you can disable that -optimization by using private browsing mode or using a different browser. +مرورگرها همچنین گاهی بدون ارسال هیچ درخواستی، چندین اتصال به سرور باز می‌کنند تا اگر بعداً بخواهند درخواستی ارسال کنند، آن درخواست‌ها سریع‌تر انجام شوند. وقتی این اتفاق می‌افتد، سرور ما هر اتصال را مشاهده می‌کند، صرف‌نظر از این‌که آیا درخواستی از طریق آن اتصال وجود دارد یا نه. بسیاری از نسخه‌های مرورگرهای مبتنی بر Chrome این کار را انجام می‌دهند؛ می‌توانید این بهینه‌سازی را با استفاده از حالت مرور خصوصی (private browsing) یا استفاده از مرورگر متفاوت غیرفعال کنید. -The important factor is that we’ve successfully gotten a handle to a TCP -connection! +نکته‌ی مهم این است که ما موفق شده‌ایم به یک اتصال TCP دسترسی پیدا کنیم! -Remember to stop the program by pressing <kbd>ctrl</kbd>-<kbd>C</kbd> when -you’re done running a particular version of the code. Then restart the program -by invoking the `cargo run` command after you’ve made each set of code changes -to make sure you’re running the newest code. +به یاد داشته باشید که پس از اجرای یک نسخه خاص از کد، برنامه را با فشردن <kbd>ctrl</kbd>+<kbd>C</kbd> متوقف کنید. سپس، پس از ایجاد هر مجموعه از تغییرات در کد، با اجرای دستور `cargo run` برنامه را دوباره اجرا کنید تا مطمئن شوید جدیدترین نسخه‌ی کد را اجرا می‌کنید. -### Reading the Request +### خواندن درخواست -Let’s implement the functionality to read the request from the browser! To -separate the concerns of first getting a connection and then taking some action -with the connection, we’ll start a new function for processing connections. In -this new `handle_connection` function, we’ll read data from the TCP stream and -print it so we can see the data being sent from the browser. Change the code to -look like Listing 21-2. +بیایید عملکرد خواندن درخواست از مرورگر را پیاده‌سازی کنیم! برای جدا کردن نگرانی‌ها از اتصال اولیه و سپس انجام برخی اقدامات با اتصال، یک تابع جدید برای پردازش اتصالات ایجاد می‌کنیم. در این تابع جدید `handle_connection`، داده‌ها را از جریان TCP می‌خوانیم و آن‌ها را چاپ می‌کنیم تا بتوانیم داده‌هایی که از مرورگر ارسال می‌شوند را ببینیم. کد را تغییر دهید تا شبیه لیست ۲۱-۲ شود. -<Listing number="21-2" file-name="src/main.rs" caption="Reading from the `TcpStream` and printing the data"> +<Listing number="21-2" file-name="src/main.rs" caption="خواندن از `TcpStream` و چاپ داده‌ها"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-02/src/main.rs}} @@ -140,38 +67,17 @@ look like Listing 21-2. </Listing> -We bring `std::io::prelude` and `std::io::BufReader` into scope to get access -to traits and types that let us read from and write to the stream. In the `for` -loop in the `main` function, instead of printing a message that says we made a -connection, we now call the new `handle_connection` function and pass the -`stream` to it. - -In the `handle_connection` function, we create a new `BufReader` instance that -wraps a reference to the `stream`. The `BufReader` adds buffering by managing -calls to the `std::io::Read` trait methods for us. - -We create a variable named `http_request` to collect the lines of the request -the browser sends to our server. We indicate that we want to collect these -lines in a vector by adding the `Vec<_>` type annotation. - -`BufReader` implements the `std::io::BufRead` trait, which provides the `lines` -method. The `lines` method returns an iterator of `Result<String, -std::io::Error>` by splitting the stream of data whenever it sees a newline -byte. To get each `String`, we map and `unwrap` each `Result`. The `Result` -might be an error if the data isn’t valid UTF-8 or if there was a problem -reading from the stream. Again, a production program should handle these errors -more gracefully, but we’re choosing to stop the program in the error case for -simplicity. - -The browser signals the end of an HTTP request by sending two newline -characters in a row, so to get one request from the stream, we take lines until -we get a line that is the empty string. Once we’ve collected the lines into the -vector, we’re printing them out using pretty debug formatting so we can take a -look at the instructions the web browser is sending to our server. - -Let’s try this code! Start the program and make a request in a web browser -again. Note that we’ll still get an error page in the browser, but our -program’s output in the terminal will now look similar to this: +ما `std::io::prelude` و `std::io::BufReader` را وارد دامنه می‌کنیم تا به ویژگی‌ها و نوع‌هایی که به ما اجازه خواندن و نوشتن از جریان را می‌دهند دسترسی داشته باشیم. در حلقه `for` در تابع `main`، به جای چاپ یک پیام که می‌گوید یک اتصال برقرار کردیم، حالا تابع جدید `handle_connection` را فراخوانی می‌کنیم و `stream` را به آن ارسال می‌کنیم. + +در تابع `handle_connection`، یک نمونه‌ی جدید از `BufReader` ایجاد می‌کنیم که یک رفرنس به `stream` را درون خود نگه می‌دارد. `BufReader` با مدیریت فراخوانی‌های متدهای trait مربوط به `std::io::Read`، به ما امکان می‌دهد تا به‌صورت بهینه‌تر از بافر استفاده کنیم. + +ما یک متغیر به نام `http_request` ایجاد می‌کنیم تا خطوط درخواست ارسالی مرورگر به سرور را جمع‌آوری کنیم. مشخص می‌کنیم که می‌خواهیم این خطوط را در یک بردار جمع‌آوری کنیم با اضافه کردن نوع `Vec<_>` به عنوان حاشیه‌نویسی. + +`BufReader` ویژگی `std::io::BufRead` را پیاده‌سازی می‌کند که متد `lines` را ارائه می‌دهد. متد `lines` یک iterator از نوع `Result<String, std::io::Error>` بازمی‌گرداند، که با هر بار مشاهده یک بایت newline جریان داده را تقسیم می‌کند. برای دریافت هر `String`، هر `Result` را map و `unwrap` می‌کنیم. اگر داده‌ها UTF-8 معتبری نباشند یا اگر مشکلی در خواندن از جریان وجود داشته باشد، ممکن است `Result` خطایی باشد. باز هم، یک برنامه تولیدی باید این خطاها را به صورت کارآمدتری مدیریت کند، اما برای سادگی، ما انتخاب می‌کنیم که در حالت خطا برنامه متوقف شود. + +مرورگر پایان یک درخواست HTTP را با ارسال دو کاراکتر newline متوالی نشان می‌دهد، بنابراین برای دریافت یک درخواست از جریان، خطوط را می‌گیریم تا زمانی که به یک خط خالی برسیم. پس از جمع‌آوری خطوط در بردار، آن‌ها را با استفاده از فرمت دیباگ زیبا چاپ می‌کنیم تا بتوانیم دستورالعمل‌هایی که مرورگر وب به سرور ما ارسال می‌کند را ببینیم. + +بیایید این کد را امتحان کنیم! برنامه را اجرا کنید و دوباره یک درخواست در مرورگر وب ارسال کنید. توجه داشته باشید که همچنان در مرورگر یک صفحه خطا خواهیم دید، اما خروجی برنامه در ترمینال اکنون مشابه این خواهد بود: ```console $ cargo run @@ -196,19 +102,13 @@ Request: [ ] ``` -Depending on your browser, you might get slightly different output. Now that -we’re printing the request data, we can see why we get multiple connections -from one browser request by looking at the path after `GET` in the first line -of the request. If the repeated connections are all requesting _/_, we know the -browser is trying to fetch _/_ repeatedly because it’s not getting a response -from our program. +بسته به مرورگری که استفاده می‌کنید، ممکن است خروجی کمی متفاوت دریافت کنید. اکنون که داده‌های درخواست را چاپ می‌کنیم، می‌توانیم دلیل دریافت چندین اتصال از یک درخواست مرورگر را با نگاه کردن به مسیر بعد از `GET` در خط اول درخواست متوجه شویم. اگر اتصالات تکراری همه _/_ را درخواست کنند، می‌دانیم که مرورگر سعی دارد _/_ را بارها و بارها درخواست کند زیرا پاسخی از برنامه ما دریافت نمی‌کند. -Let’s break down this request data to understand what the browser is asking of -our program. +بیایید این داده‌های درخواست را تجزیه کنیم تا متوجه شویم مرورگر از برنامه ما چه چیزی می‌خواهد. -### A Closer Look at an HTTP Request +### نگاهی دقیق‌تر به یک درخواست HTTP -HTTP is a text-based protocol, and a request takes this format: +HTTP یک پروتکل مبتنی بر متن است و یک درخواست فرمت زیر را دارد: ```text Method Request-URI HTTP-Version CRLF @@ -216,41 +116,23 @@ headers CRLF message-body ``` -The first line is the _request line_ that holds information about what the -client is requesting. The first part of the request line indicates the _method_ -being used, such as `GET` or `POST`, which describes how the client is making -this request. Our client used a `GET` request, which means it is asking for -information. +خط اول _خط درخواست_ است که اطلاعاتی درباره آنچه client درخواست می‌کند را نگه می‌دارد. قسمت اول خط درخواست، _method_ استفاده‌شده را نشان می‌دهد، مانند `GET` یا `POST`، که توضیح می‌دهد client چگونه این درخواست را انجام می‌دهد. client ما از یک درخواست `GET` استفاده کرده است، به این معنی که در حال درخواست اطلاعات است. -The next part of the request line is _/_, which indicates the _uniform resource -identifier_ _(URI)_ the client is requesting: a URI is almost, but not quite, -the same as a _uniform resource locator_ _(URL)_. The difference between URIs -and URLs isn’t important for our purposes in this chapter, but the HTTP spec -uses the term _URI_, so we can just mentally substitute _URL_ for _URI_ here. +بخش بعدی از خط درخواست، */* است که نشان‌دهنده‌ی *شناسه‌ی یکنواخت منبع* یا *(uniform resource identifier)* است که کلاینت آن را درخواست کرده است؛ یک URI تقریباً، اما نه کاملاً، همانند *نشانی یکنواخت منبع* یا *(uniform resource locator)* یا همان URL است. تفاوت بین URI و URL برای اهداف ما در این فصل اهمیت خاصی ندارد، اما مشخصات HTTP از اصطلاح *URI* استفاده می‌کند، بنابراین می‌توانیم در ذهن خود به‌جای URI از URL استفاده کنیم. -The last part is the HTTP version the client uses, and then the request line -ends in a CRLF sequence. (CRLF stands for _carriage return_ and _line feed_, -which are terms from the typewriter days!) The CRLF sequence can also be -written as `\r\n`, where `\r` is a carriage return and `\n` is a line feed. The -_CRLF sequence_ separates the request line from the rest of the request data. -Note that when the CRLF is printed, we see a new line start rather than `\r\n`. +آخرین بخش نسخه‌ی HTTP است که کلاینت استفاده می‌کند، و سپس خط درخواست با یک دنباله‌ی CRLF به پایان می‌رسد. (CRLF مخفف *carriage return* و *line feed* است، که اصطلاحاتی مربوط به دوران ماشین تحریر هستند!) دنباله‌ی CRLF همچنین به‌صورت `\r\n` نیز نوشته می‌شود، جایی که `\r` به معنای carriage return و `\n` به معنای line feed است. *دنباله‌ی CRLF* خط درخواست را از باقی داده‌های درخواست جدا می‌کند. توجه داشته باشید که هنگام چاپ CRLF، ما شروع یک خط جدید را می‌بینیم، نه `\r\n`. -Looking at the request line data we received from running our program so far, -we see that `GET` is the method, _/_ is the request URI, and `HTTP/1.1` is the -version. +با نگاه کردن به داده‌های خط درخواست که تاکنون از اجرای برنامه خود دریافت کرده‌ایم، می‌بینیم که `GET` متد است، _/_ شناسه URI درخواست‌شده است، و `HTTP/1.1` نسخه است. -After the request line, the remaining lines starting from `Host:` onward are -headers. `GET` requests have no body. +بعد از خط درخواست، خطوط باقی‌مانده از `Host:` به بعد هدرها هستند. درخواست‌های `GET` بدنه ندارند. -Try making a request from a different browser or asking for a different -address, such as _127.0.0.1:7878/test_, to see how the request data changes. +سعی کنید یک درخواست از مرورگری دیگر یا آدرسی متفاوت، مانند _127.0.0.1:7878/test_، ارسال کنید تا ببینید داده‌های درخواست چگونه تغییر می‌کنند. -Now that we know what the browser is asking for, let’s send back some data! +اکنون که می‌دانیم مرورگر چه چیزی می‌خواهد، بیایید داده‌ای را به آن بازگردانیم! -### Writing a Response +### نوشتن یک پاسخ -We’re going to implement sending data in response to a client request. -Responses have the following format: +ما قصد داریم ارسال داده در پاسخ به یک درخواست client را پیاده‌سازی کنیم. پاسخ‌ها فرمت زیر را دارند: ```text HTTP-Version Status-Code Reason-Phrase CRLF @@ -258,26 +140,17 @@ headers CRLF message-body ``` -The first line is a _status line_ that contains the HTTP version used in the -response, a numeric status code that summarizes the result of the request, and -a reason phrase that provides a text description of the status code. After the -CRLF sequence are any headers, another CRLF sequence, and the body of the -response. +خط اول یک _خط وضعیت_ است که شامل نسخه HTTP استفاده‌شده در پاسخ، یک کد وضعیت عددی که نتیجه درخواست را خلاصه می‌کند، و یک عبارت دلیل که توضیح متنی برای کد وضعیت ارائه می‌دهد. پس از دنباله CRLF، هر هدر و سپس یک دنباله CRLF دیگر و بدنه پاسخ قرار می‌گیرد. -Here is an example response that uses HTTP version 1.1, and has a status code of -200, an OK reason phrase, no headers, and no body: +در اینجا یک مثال پاسخ آورده شده است که از نسخه HTTP 1.1 استفاده می‌کند، کد وضعیت 200 دارد، عبارت دلیل "OK" است، هیچ هدر و بدنه‌ای ندارد: ```text HTTP/1.1 200 OK\r\n\r\n ``` -The status code 200 is the standard success response. The text is a tiny -successful HTTP response. Let’s write this to the stream as our response to a -successful request! From the `handle_connection` function, remove the -`println!` that was printing the request data and replace it with the code in -Listing 21-3. +کد وضعیت 200 پاسخ استاندارد موفقیت است. این متن یک پاسخ HTTP کوچک و موفقیت‌آمیز است. بیایید این را به عنوان پاسخ خود به یک درخواست موفق به جریان بنویسیم! از تابع `handle_connection`، `println!` که داده‌های درخواست را چاپ می‌کرد، حذف کنید و آن را با کد موجود در لیست ۲۱-۳ جایگزین کنید. -<Listing number="21-3" file-name="src/main.rs" caption="Writing a tiny successful HTTP response to the stream"> +<Listing number="21-3" file-name="src/main.rs" caption="نوشتن یک پاسخ HTTP کوچک و موفقیت‌آمیز به جریان"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-03/src/main.rs:here}} @@ -285,27 +158,15 @@ Listing 21-3. </Listing> -The first new line defines the `response` variable that holds the success -message’s data. Then we call `as_bytes` on our `response` to convert the string -data to bytes. The `write_all` method on `stream` takes a `&[u8]` and sends -those bytes directly down the connection. Because the `write_all` operation -could fail, we use `unwrap` on any error result as before. Again, in a real -application you would add error handling here. +خط جدید اول متغیر `response` را تعریف می‌کند که داده پیام موفقیت را در خود نگه می‌دارد. سپس با فراخوانی `as_bytes` روی `response` داده رشته‌ای را به بایت‌ها تبدیل می‌کنیم. متد `write_all` روی `stream` یک `&[u8]` می‌گیرد و آن بایت‌ها را مستقیماً به اتصال ارسال می‌کند. از آنجا که عملیات `write_all` ممکن است شکست بخورد، مانند قبل، روی هر نتیجه خطا از `unwrap` استفاده می‌کنیم. باز هم، در یک برنامه واقعی باید اینجا مدیریت خطا اضافه کنید. -With these changes, let’s run our code and make a request. We’re no longer -printing any data to the terminal, so we won’t see any output other than the -output from Cargo. When you load _127.0.0.1:7878_ in a web browser, you should -get a blank page instead of an error. You’ve just handcoded receiving an HTTP -request and sending a response! +با این تغییرات، بیایید کد خود را اجرا کنیم و یک درخواست ارسال کنیم. دیگر هیچ داده‌ای به ترمینال چاپ نمی‌کنیم، بنابراین هیچ خروجی‌ای به غیر از خروجی Cargo نخواهید دید. وقتی آدرس _127.0.0.1:7878_ را در یک مرورگر وب بارگذاری می‌کنید، باید یک صفحه خالی به جای یک خطا دریافت کنید. شما اکنون دریافت یک درخواست HTTP و ارسال یک پاسخ را به صورت دستی کدنویسی کرده‌اید! -### Returning Real HTML +### بازگرداندن HTML واقعی (Returning Real HTML) -Let’s implement the functionality for returning more than a blank page. Create -the new file _hello.html_ in the root of your project directory, not in the -_src_ directory. You can input any HTML you want; Listing 21-4 shows one -possibility. +بیایید عملکرد بازگرداندن چیزی بیش از یک صفحه خالی را پیاده‌سازی کنیم. فایل جدیدی به نام _hello.html_ در ریشه دایرکتوری پروژه خود ایجاد کنید، نه در دایرکتوری _src_. می‌توانید هر HTML که می‌خواهید وارد کنید؛ لیست ۲۱-۴ یک نمونه را نشان می‌دهد. -<Listing number="21-4" file-name="hello.html" caption="A sample HTML file to return in a response"> +<Listing number="21-4" file-name="hello.html" caption="یک فایل نمونه HTML برای بازگرداندن در پاسخ"> ```html {{#include ../listings/ch21-web-server/listing-21-05/hello.html}} @@ -313,12 +174,9 @@ possibility. </Listing> -This is a minimal HTML5 document with a heading and some text. To return this -from the server when a request is received, we’ll modify `handle_connection` as -shown in Listing 21-5 to read the HTML file, add it to the response as a body, -and send it. +این یک سند HTML5 حداقلی با یک عنوان و مقداری متن است. برای بازگرداندن این فایل از سرور هنگام دریافت یک درخواست، کد `handle_connection` را همان‌طور که در لیست ۲۱-۵ نشان داده شده است تغییر می‌دهیم تا فایل HTML را بخواند، آن را به عنوان بدنه پاسخ اضافه کند و ارسال کند. -<Listing number="21-5" file-name="src/main.rs" caption="Sending the contents of *hello.html* as the body of the response"> +<Listing number="21-5" file-name="src/main.rs" caption="ارسال محتوای *hello.html* به عنوان بدنه پاسخ"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-05/src/main.rs:here}} @@ -326,38 +184,19 @@ and send it. </Listing> -We’ve added `fs` to the `use` statement to bring the standard library’s -filesystem module into scope. The code for reading the contents of a file to a -string should look familiar; we used it when we read the contents of a file for -our I/O project in Listing 12-4. +ما `fs` را به دستور `use` اضافه کرده‌ایم تا ماژول سیستم فایل کتابخانه استاندارد را وارد دامنه کنیم. کدی که محتوای یک فایل را به یک رشته می‌خواند باید آشنا به نظر برسد؛ ما در فصل ۱۲ زمانی که محتوای یک فایل را برای پروژه ورودی/خروجی خود خواندیم از آن استفاده کردیم (لیست ۱۲-۴). -Next, we use `format!` to add the file’s contents as the body of the success -response. To ensure a valid HTTP response, we add the `Content-Length` header -which is set to the size of our response body, in this case the size of -`hello.html`. +سپس، از `format!` برای اضافه کردن محتوای فایل به عنوان بدنه پاسخ موفقیت استفاده می‌کنیم. برای اطمینان از یک پاسخ HTTP معتبر، هدر `Content-Length` را اضافه می‌کنیم که به اندازه بدنه پاسخ ما تنظیم شده است، که در این مورد اندازه فایل `hello.html` است. -Run this code with `cargo run` and load _127.0.0.1:7878_ in your browser; you -should see your HTML rendered! +این کد را با دستور `cargo run` اجرا کنید و آدرس _127.0.0.1:7878_ را در مرورگر خود بارگذاری کنید؛ باید HTML خود را که به درستی رندر شده است ببینید! -Currently, we’re ignoring the request data in `http_request` and just sending -back the contents of the HTML file unconditionally. That means if you try -requesting _127.0.0.1:7878/something-else_ in your browser, you’ll still get -back this same HTML response. At the moment, our server is very limited and -does not do what most web servers do. We want to customize our responses -depending on the request and only send back the HTML file for a well-formed -request to _/_. +در حال حاضر، ما داده‌های درخواست در `http_request` را نادیده می‌گیریم و فقط محتوای فایل HTML را بدون شرط بازمی‌گردانیم. این بدان معناست که اگر در مرورگر خود آدرس _127.0.0.1:7878/something-else_ را درخواست کنید، همچنان همین پاسخ HTML را دریافت خواهید کرد. در این لحظه، سرور ما بسیار محدود است و کارهایی که اکثر وب سرورها انجام می‌دهند را انجام نمی‌دهد. ما می‌خواهیم پاسخ‌های خود را بر اساس درخواست سفارشی کنیم و فقط فایل HTML را برای یک درخواست خوش‌ساخت به _/_ بازگردانیم. -### Validating the Request and Selectively Responding +### اعتبارسنجی درخواست و پاسخ‌دهی انتخابی -Right now, our web server will return the HTML in the file no matter what the -client requested. Let’s add functionality to check that the browser is -requesting _/_ before returning the HTML file, and return an error if the -browser requests anything else. For this we need to modify `handle_connection`, -as shown in Listing 21-6. This new code checks the content of the request -received against what we know a request for _/_ looks like and adds `if` and -`else` blocks to treat requests differently. +در حال حاضر، وب سرور ما محتوای فایل HTML را بدون توجه به درخواست client بازمی‌گرداند. بیایید عملکردی اضافه کنیم تا بررسی کند که مرورگر _/_ را درخواست کرده باشد، سپس فایل HTML را بازگرداند و اگر مرورگر چیز دیگری درخواست کرد، یک خطا بازگرداند. برای این کار باید تابع `handle_connection` را همان‌طور که در لیست ۲۱-۶ نشان داده شده است تغییر دهیم. این کد جدید محتوای درخواست دریافتی را با آنچه که می‌دانیم یک درخواست برای _/_ باید به نظر برسد مقایسه می‌کند و بلوک‌های `if` و `else` را اضافه می‌کند تا درخواست‌ها به صورت متفاوتی مدیریت شوند. -<Listing number="21-6" file-name="src/main.rs" caption="Handling requests to */* differently from other requests"> +<Listing number="21-6" file-name="src/main.rs" caption="مدیریت درخواست‌های */* به صورت متفاوت با سایر درخواست‌ها"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-06/src/main.rs:here}} @@ -365,32 +204,17 @@ received against what we know a request for _/_ looks like and adds `if` and </Listing> -We’re only going to be looking at the first line of the HTTP request, so rather -than reading the entire request into a vector, we’re calling `next` to get the -first item from the iterator. The first `unwrap` takes care of the `Option` and -stops the program if the iterator has no items. The second `unwrap` handles the -`Result` and has the same effect as the `unwrap` that was in the `map` added in -Listing 21-2. +ما فقط به خط اول درخواست HTTP نگاه خواهیم کرد، بنابراین به جای خواندن کل درخواست در یک بردار، از `next` استفاده می‌کنیم تا اولین آیتم از iterator را بگیریم. اولین `unwrap` مقدار `Option` را مدیریت می‌کند و اگر iterator هیچ آیتمی نداشته باشد برنامه را متوقف می‌کند. دومین `unwrap` مقدار `Result` را مدیریت می‌کند و همان اثری را دارد که `unwrap` در `map` اضافه‌شده در لیست ۲۱-۲ داشت. -Next, we check the `request_line` to see if it equals the request line of a GET -request to the _/_ path. If it does, the `if` block returns the contents of our -HTML file. +سپس، مقدار `request_line` را بررسی می‌کنیم تا ببینیم آیا برابر با خط درخواست یک درخواست GET به مسیر _/_ است یا خیر. اگر این‌طور باشد، بلوک `if` محتوای فایل HTML ما را بازمی‌گرداند. -If the `request_line` does _not_ equal the GET request to the _/_ path, it -means we’ve received some other request. We’ll add code to the `else` block in -a moment to respond to all other requests. +اگر مقدار `request_line` برابر با درخواست GET به مسیر _/_ نباشد، به این معنی است که یک درخواست دیگر دریافت کرده‌ایم. در بلوک `else` کدی اضافه خواهیم کرد تا به سایر درخواست‌ها پاسخ دهد. -Run this code now and request _127.0.0.1:7878_; you should get the HTML in -_hello.html_. If you make any other request, such as -_127.0.0.1:7878/something-else_, you’ll get a connection error like those you -saw when running the code in Listing 21-1 and Listing 21-2. +این کد را اجرا کنید و آدرس _127.0.0.1:7878_ را درخواست کنید؛ باید HTML موجود در فایل _hello.html_ را دریافت کنید. اگر درخواست دیگری مانند _127.0.0.1:7878/something-else_ ارسال کنید، یک خطای اتصال مشابه آنچه در اجرای کدهای لیست ۲۱-۱ و ۲۱-۲ دیدید دریافت خواهید کرد. -Now let’s add the code in Listing 21-7 to the `else` block to return a response -with the status code 404, which signals that the content for the request was -not found. We’ll also return some HTML for a page to render in the browser -indicating the response to the end user. +حالا کد موجود در لیست ۲۱-۷ را به بلوک `else` اضافه کنید تا پاسخی با کد وضعیت 404 بازگرداند، که نشان می‌دهد محتوای درخواست‌شده پیدا نشد. همچنین مقداری HTML برای یک صفحه خطا بازمی‌گردانیم تا در مرورگر به کاربر نهایی نمایش داده شود. -<Listing number="21-7" file-name="src/main.rs" caption="Responding with status code 404 and an error page if anything other than */* was requested"> +<Listing number="21-7" file-name="src/main.rs" caption="پاسخ‌دهی با کد وضعیت 404 و یک صفحه خطا اگر چیزی به غیر از */* درخواست شود"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-07/src/main.rs:here}} @@ -398,13 +222,9 @@ indicating the response to the end user. </Listing> -Here, our response has a status line with status code 404 and the reason phrase -`NOT FOUND`. The body of the response will be the HTML in the file _404.html_. -You’ll need to create a _404.html_ file next to _hello.html_ for the error -page; again feel free to use any HTML you want, or use the example HTML in -Listing 21-8. +در اینجا، پاسخ ما یک خط وضعیت با کد وضعیت 404 و عبارت دلیل `NOT FOUND` دارد. بدنه پاسخ HTML موجود در فایل _404.html_ خواهد بود. باید فایل _404.html_ را در کنار فایل _hello.html_ برای صفحه خطا ایجاد کنید؛ باز هم، می‌توانید هر HTML که می‌خواهید استفاده کنید یا از HTML نمونه موجود در لیست ۲۱-۸ استفاده کنید. -<Listing number="21-8" file-name="404.html" caption="Sample content for the page to send back with any 404 response"> +<Listing number="21-8" file-name="404.html" caption="محتوای نمونه برای صفحه‌ای که با هر پاسخ 404 بازگردانده می‌شود"> ```html {{#include ../listings/ch21-web-server/listing-21-07/404.html}} @@ -412,22 +232,13 @@ Listing 21-8. </Listing> -With these changes, run your server again. Requesting _127.0.0.1:7878_ should -return the contents of _hello.html_, and any other request, like -_127.0.0.1:7878/foo_, should return the error HTML from _404.html_. +با این تغییرات، سرور خود را دوباره اجرا کنید. درخواست آدرس _127.0.0.1:7878_ باید محتوای فایل _hello.html_ را بازگرداند، و هر درخواست دیگری مانند _127.0.0.1:7878/foo_ باید HTML خطا از فایل _404.html_ را بازگرداند. -### A Touch of Refactoring +### کمی بازسازی (Refactoring) -At the moment, the `if` and `else` blocks have a lot of repetition: they’re both -reading files and writing the contents of the files to the stream. The only -differences are the status line and the filename. Let’s make the code more -concise by pulling out those differences into separate `if` and `else` lines -that will assign the values of the status line and the filename to variables; we -can then use those variables unconditionally in the code to read the file and -write the response. Listing 21-9 shows the resultant code after replacing the -large `if` and `else` blocks. +در حال حاضر، بلوک‌های `if` و `else` مقدار زیادی تکرار دارند: هر دو فایل‌ها را می‌خوانند و محتوای فایل‌ها را به جریان می‌نویسند. تنها تفاوت‌ها خط وضعیت و نام فایل هستند. بیایید کد را مختصرتر کنیم و این تفاوت‌ها را به خطوط جداگانه `if` و `else` انتقال دهیم که مقادیر خط وضعیت و نام فایل را به متغیرها اختصاص دهند؛ سپس می‌توانیم از این متغیرها به طور شرطی برای خواندن فایل و نوشتن پاسخ استفاده کنیم. لیست ۲۱-۹ کد نتیجه‌شده پس از جایگزینی بلوک‌های بزرگ `if` و `else` را نشان می‌دهد. -<Listing number="21-9" file-name="src/main.rs" caption="Refactoring the `if` and `else` blocks to contain only the code that differs between the two cases"> +<Listing number="21-9" file-name="src/main.rs" caption="بازسازی بلوک‌های `if` و `else` برای شامل شدن تنها کدی که بین دو حالت متفاوت است"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-09/src/main.rs:here}} @@ -435,23 +246,10 @@ large `if` and `else` blocks. </Listing> -Now the `if` and `else` blocks only return the appropriate values for the -status line and filename in a tuple; we then use destructuring to assign these -two values to `status_line` and `filename` using a pattern in the `let` -statement, as discussed in Chapter 19. - -The previously duplicated code is now outside the `if` and `else` blocks and -uses the `status_line` and `filename` variables. This makes it easier to see -the difference between the two cases, and it means we have only one place to -update the code if we want to change how the file reading and response writing -work. The behavior of the code in Listing 21-9 will be the same as that in -Listing 21-7. - -Awesome! We now have a simple web server in approximately 40 lines of Rust code -that responds to one request with a page of content and responds to all other -requests with a 404 response. - -Currently, our server runs in a single thread, meaning it can only serve one -request at a time. Let’s examine how that can be a problem by simulating some -slow requests. Then we’ll fix it so our server can handle multiple requests at -once. +اکنون بلوک‌های `if` و `else` تنها مقادیر مناسب برای خط وضعیت و نام فایل را در یک تاپل بازمی‌گردانند؛ سپس با استفاده از یک الگو در دستور `let`، این دو مقدار به `status_line` و `filename` تخصیص داده می‌شوند، همان‌طور که در فصل ۱۹ بحث شد. + +کدی که قبلاً تکراری بود اکنون خارج از بلوک‌های `if` و `else` قرار دارد و از متغیرهای `status_line` و `filename` استفاده می‌کند. این کار تشخیص تفاوت بین دو حالت را آسان‌تر می‌کند و به این معنی است که اگر بخواهیم نحوه خواندن فایل و نوشتن پاسخ را تغییر دهیم، تنها یک مکان برای به‌روزرسانی کد داریم. رفتار کد در لیست ۲۱-۹ با لیست ۲۱-۷ یکسان خواهد بود. + +عالی! اکنون یک وب سرور ساده با تقریباً ۴۰ خط کد Rust داریم که به یک درخواست با یک صفحه محتوا پاسخ می‌دهد و به تمام درخواست‌های دیگر یک پاسخ 404 می‌دهد. + +در حال حاضر، سرور ما در یک Thread اجرا می‌شود، به این معنی که تنها می‌تواند یک درخواست را در یک زمان سرویس دهد. بیایید بررسی کنیم که چگونه این موضوع می‌تواند مشکل‌ساز شود، با شبیه‌سازی برخی درخواست‌های کند. سپس سرور را طوری بهبود می‌دهیم که بتواند چندین درخواست را همزمان مدیریت کند. \ No newline at end of file diff --git a/src/ch21-02-multithreaded.md b/src/ch21-02-multithreaded.md index 1cf59dc55f..472590c8d7 100644 --- a/src/ch21-02-multithreaded.md +++ b/src/ch21-02-multithreaded.md @@ -1,24 +1,20 @@ -## Turning Our Single-Threaded Server into a Multithreaded Server +## تبدیل سرور Single-Threaded به یک سرور Multithreaded -Right now, the server will process each request in turn, meaning it won’t -process a second connection until the first is finished processing. If the -server received more and more requests, this serial execution would be less and -less optimal. If the server receives a request that takes a long time to -process, subsequent requests will have to wait until the long request is -finished, even if the new requests can be processed quickly. We’ll need to fix -this, but first we’ll look at the problem in action. +در حال حاضر، سرور هر درخواست را به‌صورت ترتیبی پردازش می‌کند، +یعنی تا زمانی که پردازش درخواست اول به پایان نرسد، درخواست دوم پردازش نخواهد شد. +اگر سرور درخواست‌های بیشتری دریافت کند، این اجرای سریالی به مرور زمان کمتر بهینه خواهد بود. +اگر سروری درخواستی دریافت کند که پردازش آن زمان زیادی ببرد، +درخواست‌های بعدی باید تا پایان پردازش آن درخواست طولانی صبر کنند، +حتی اگر درخواست‌های جدید بتوانند سریع‌تر پردازش شوند. +ما باید این مشکل را برطرف کنیم، اما ابتدا مشکل را به صورت عملی بررسی می‌کنیم. -<!-- Old headings. Do not remove or links may break. --> -<a id="simulating-a-slow-request-in-the-current-server-implementation"></a> - -### Simulating a Slow Request +### شبیه‌سازی یک درخواست کند -We’ll look at how a slow-processing request can affect other requests made to -our current server implementation. Listing 21-10 implements handling a request -to _/sleep_ with a simulated slow response that will cause the server to sleep -for five seconds before responding. +می‌خواهیم ببینیم چگونه یک درخواست با پردازش کند می‌تواند بر درخواست‌های دیگر به سرور فعلی ما تأثیر بگذارد. +لیستینگ 21-10 نحوه‌ی رسیدگی به درخواستی به مسیر _/sleep_ را پیاده‌سازی می‌کند +که با یک پاسخ شبیه‌سازی شده‌ی کند، باعث می‌شود سرور پنج ثانیه قبل از پاسخ دادن بخوابد. -<Listing number="21-10" file-name="src/main.rs" caption="Simulating a slow request by sleeping for five seconds"> +<Listing number="21-10" file-name="src/main.rs" caption="شبیه‌سازی یک درخواست کند با خوابیدن به مدت پنج ثانیه"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-10/src/main.rs:here}} @@ -26,92 +22,67 @@ for five seconds before responding. </Listing> -We switched from `if` to `match` now that we have three cases. We need to -explicitly match on a slice of `request_line` to pattern-match against the -string literal values; `match` doesn’t do automatic referencing and -dereferencing, like the equality method does. - -The first arm is the same as the `if` block from Listing 21-9. The second arm -matches a request to _/sleep_. When that request is received, the server will -sleep for five seconds before rendering the successful HTML page. The third arm -is the same as the `else` block from Listing 21-9. - -You can see how primitive our server is: real libraries would handle the -recognition of multiple requests in a much less verbose way! - -Start the server using `cargo run`. Then open two browser windows: one for -_http://127.0.0.1:7878_ and the other for _http://127.0.0.1:7878/sleep_. If -you enter the _/_ URI a few times, as before, you’ll see it respond quickly. -But if you enter _/sleep_ and then load _/_, you’ll see that _/_ waits until -`sleep` has slept for its full five seconds before loading. - -There are multiple techniques we could use to avoid requests backing up behind -a slow request, including using async as we did Chapter 17; the one we’ll -implement is a thread pool. - -### Improving Throughput with a Thread Pool - -A _thread pool_ is a group of spawned threads that are waiting and ready to -handle a task. When the program receives a new task, it assigns one of the -threads in the pool to the task, and that thread will process the task. The -remaining threads in the pool are available to handle any other tasks that come -in while the first thread is processing. When the first thread is done -processing its task, it’s returned to the pool of idle threads, ready to handle -a new task. A thread pool allows you to process connections concurrently, -increasing the throughput of your server. - -We’ll limit the number of threads in the pool to a small number to protect us -from DoS attacks; if we had our program create a new thread for each request as -it came in, someone making 10 million requests to our server could create havoc -by using up all our server’s resources and grinding the processing of requests -to a halt. - -Rather than spawning unlimited threads, then, we’ll have a fixed number of -threads waiting in the pool. Requests that come in are sent to the pool for -processing. The pool will maintain a queue of incoming requests. Each of the -threads in the pool will pop off a request from this queue, handle the request, -and then ask the queue for another request. With this design, we can process up -to _`N`_ requests concurrently, where _`N`_ is the number of threads. If each -thread is responding to a long-running request, subsequent requests can still -back up in the queue, but we’ve increased the number of long-running requests -we can handle before reaching that point. - -This technique is just one of many ways to improve the throughput of a web -server. Other options you might explore are the fork/join model, the -single-threaded async I/O model, and the multithreaded async I/O model. If -you’re interested in this topic, you can read more about other solutions and -try to implement them; with a low-level language like Rust, all of these -options are possible. - -Before we begin implementing a thread pool, let’s talk about what using the -pool should look like. When you’re trying to design code, writing the client -interface first can help guide your design. Write the API of the code so it’s -structured in the way you want to call it; then implement the functionality -within that structure rather than implementing the functionality and then -designing the public API. - -Similar to how we used test-driven development in the project in Chapter 12, -we’ll use compiler-driven development here. We’ll write the code that calls the -functions we want, and then we’ll look at errors from the compiler to determine -what we should change next to get the code to work. Before we do that, however, -we’ll explore the technique we’re not going to use as a starting point. +حالا که سه حالت داریم، به جای `if` از `match` استفاده کرده‌ایم. +باید صریحاً روی یک `slice` از `request_line` تطبیق الگو (pattern matching) انجام دهیم +تا بتوانیم با مقادیر رشته‌ای literal تطبیق دهیم؛ +چون `match` مانند متد برابری به‌صورت خودکار رفرنس‌گذاری و dereference نمی‌کند. -<!-- Old headings. Do not remove or links may break. --> +بازوی اول مشابه بلاک `if` در لیستینگ 21-9 است. +بازوی دوم با درخواستی به مسیر */sleep* مطابقت دارد. +وقتی این درخواست دریافت شود، سرور به مدت پنج ثانیه می‌خوابد +و سپس صفحه‌ی HTML موفقیت‌آمیز را رندر می‌کند. +بازوی سوم همانند بلاک `else` در لیستینگ 21-9 است. + +می‌توانید ببینید که سرور ما چقدر ابتدایی است: کتابخانه‌های واقعی مدیریت تشخیص درخواست‌های متعدد را به روشی بسیار کمتر پرحرف انجام می‌دهند! + +سرور را با دستور `cargo run` اجرا کنید. +سپس دو پنجره‌ی مرورگر باز کنید: یکی برای آدرس _http://127.0.0.1:7878_ و دیگری برای _http://127.0.0.1:7878/sleep_. +اگر چند بار آدرس _/_ را وارد کنید، مانند قبل پاسخ سریع دریافت خواهید کرد. +اما اگر ابتدا آدرس _/sleep_ را وارد کنید و سپس آدرس _/_ را بارگذاری کنید، +خواهید دید که _/_ تا پایان پنج ثانیه خوابیدن `sleep` منتظر می‌ماند و سپس بارگذاری می‌شود. + +### بهبود توان عملیاتی با یک Thread Pool + +یک _Thread Pool_ گروهی از Threadهای ایجادشده است که منتظر و آماده برای مدیریت یک وظیفه هستند. وقتی برنامه یک وظیفه جدید دریافت می‌کند، یکی از Threadهای موجود در Pool به وظیفه اختصاص داده می‌شود و آن Thread وظیفه را پردازش می‌کند. Threadهای باقی‌مانده در Pool در دسترس هستند تا هر وظیفه دیگری که وارد شود را در حالی که Thread اول وظیفه خود را پردازش می‌کند، مدیریت کنند. وقتی Thread اول پردازش وظیفه خود را به پایان می‌رساند، به Pool Threadهای بیکار بازمی‌گردد و آماده برای مدیریت یک وظیفه جدید است. یک Thread Pool به شما امکان می‌دهد اتصالات را به صورت همزمان پردازش کنید و توان عملیاتی سرور خود را افزایش دهید. + +ما تعداد Threadهای موجود در Pool را به یک عدد کوچک محدود خواهیم کرد تا از حملات Denial of Service (DoS) محافظت کنیم؛ اگر برنامه ما برای هر درخواست جدید یک Thread ایجاد کند، کسی که ۱۰ میلیون درخواست به سرور ما ارسال کند می‌تواند با استفاده از تمام منابع سرور، پردازش درخواست‌ها را متوقف کند. + +برای محافظت در برابر حملات DoS، تعداد threadهای موجود در thread pool را محدود می‌کنیم؛ +زیرا اگر برنامه‌ی ما برای هر درخواست یک thread جدید بسازد، +کسی که ۱۰ میلیون درخواست به سرور ما ارسال کند می‌تواند با مصرف همه‌ی منابع سرور، +عملیات پردازش درخواست‌ها را کاملاً متوقف کند. + +به جای ایجاد threadهای نامحدود، یک تعداد ثابت thread در pool خواهیم داشت که منتظر می‌مانند. +درخواست‌های ورودی به این pool ارسال می‌شوند تا پردازش شوند. +pool صفی از درخواست‌های ورودی را نگه می‌دارد. +هر یک از threadهای pool یک درخواست را از صف بیرون می‌کشد، آن را پردازش می‌کند، +و سپس درخواست بعدی را از صف دریافت می‌کند. +با این طراحی، می‌توانیم تا *`N`* درخواست را به‌صورت همزمان پردازش کنیم، +که *`N`* برابر با تعداد threadها است. +اگر هر thread در حال پاسخ دادن به یک درخواست طولانی باشد، +درخواست‌های بعدی می‌توانند در صف منتظر بمانند، +اما ما تعداد درخواست‌های طولانی که می‌توانیم قبل از رسیدن به این نقطه پردازش کنیم را افزایش داده‌ایم. + +این تکنیک یکی از روش‌های متعددی است که برای افزایش throughput یک وب سرور وجود دارد. +گزینه‌های دیگری که می‌توانید بررسی کنید عبارت‌اند از مدل fork/join، +مدل async I/O تک‌نخی، و مدل async I/O چندنخی. +اگر به این موضوع علاقه‌مند هستید، می‌توانید درباره‌ی راه‌حل‌های دیگر مطالعه کنید و سعی کنید آن‌ها را پیاده‌سازی کنید؛ +با زبانی سطح پایین مانند Rust، تمام این گزینه‌ها ممکن هستند. + +مشابه روش توسعه مبتنی بر تست که در پروژه فصل ۱۲ استفاده کردیم، اینجا از توسعه مبتنی بر کامپایلر استفاده می‌کنیم. کدی را که توابع مورد نظرمان را فراخوانی می‌کند، می‌نویسیم و سپس به خطاهای کامپایلر نگاه می‌کنیم تا مشخص کنیم چه تغییراتی باید انجام دهیم تا کد کار کند. با این حال، پیش از انجام این کار، روش دیگری را که قرار نیست استفاده کنیم، به عنوان نقطه شروع بررسی خواهیم کرد. <a id="code-structure-if-we-could-spawn-a-thread-for-each-request"></a> -#### Spawning a Thread for Each Request +#### ایجاد یک thread برای هر درخواست -First, let’s explore how our code might look if it did create a new thread for -every connection. As mentioned earlier, this isn’t our final plan due to the -problems with potentially spawning an unlimited number of threads, but it is a -starting point to get a working multithreaded server first. Then we’ll add the -thread pool as an improvement, and contrasting the two solutions will be easier. +ابتدا بیایید ببینیم کد ما چگونه خواهد بود اگر برای هر اتصال یک thread جدید ایجاد کند. +همان‌طور که قبلاً گفته شد، این برنامه‌ی نهایی ما نیست به دلیل مشکلات احتمالی ایجاد تعداد نامحدود thread، +اما نقطه‌ی شروع خوبی برای داشتن یک سرور چندنخی عملی است. +سپس به‌عنوان بهبود، thread pool را اضافه خواهیم کرد و مقایسه‌ی این دو راه‌حل ساده‌تر خواهد بود. -Listing 21-11 shows the changes to make to `main` to spawn a new thread to -handle each stream within the `for` loop. +لیستینگ 21-11 تغییرات لازم در تابع `main` را نشان می‌دهد تا برای هر stream در حلقه‌ی `for` یک thread جدید ایجاد کند و آن را مدیریت نماید. -<Listing number="21-11" file-name="src/main.rs" caption="Spawning a new thread for each stream"> +<Listing number="21-11" file-name="src/main.rs" caption="ایجاد یک Thread جدید برای هر جریان"> ```rust,no_run {{#rustdoc_include ../listings/ch21-web-server/listing-21-11/src/main.rs:here}} @@ -119,16 +90,9 @@ handle each stream within the `for` loop. </Listing> -As you learned in Chapter 16, `thread::spawn` will create a new thread and then -run the code in the closure in the new thread. If you run this code and load -_/sleep_ in your browser, then _/_ in two more browser tabs, you’ll indeed see -that the requests to _/_ don’t have to wait for _/sleep_ to finish. However, as -we mentioned, this will eventually overwhelm the system because you’d be making -new threads without any limit. +همان‌طور که در فصل ۱۶ یاد گرفتید، `thread::spawn` یک Thread جدید ایجاد کرده و سپس کد موجود در کلوزر را در Thread جدید اجرا می‌کند. اگر این کد را اجرا کنید و در مرورگر خود _/sleep_ را باز کنید، سپس _/_ را در دو تب دیگر باز کنید، خواهید دید که درخواست‌های _/_ لازم نیست منتظر پایان درخواست _/sleep_ باشند. با این حال، همان‌طور که ذکر شد، این روش در نهایت سیستم را تحت فشار قرار می‌دهد زیرا شما تعداد نامحدودی Thread بدون محدودیت ایجاد می‌کنید. -You may also recall from Chapter 17 that this is exactly the kind of situation -where async and await really shine! Keep that in mind as we build the thread -pool and think about how things would look different or the same with async. +ممکن است به یاد بیاورید که این دقیقاً همان شرایطی است که async و await در آن می‌درخشند! این نکته را در ذهن داشته باشید در حالی که Thread Pool را می‌سازیم و به این فکر کنید که چگونه این شرایط با async متفاوت یا مشابه خواهد بود. <!-- Old headings. Do not remove or links may break. --> @@ -136,12 +100,13 @@ pool and think about how things would look different or the same with async. #### Creating a Finite Number of Threads -We want our thread pool to work in a similar, familiar way so that switching -from threads to a thread pool doesn’t require large changes to the code that -uses our API. Listing 21-12 shows the hypothetical interface for a `ThreadPool` -struct we want to use instead of `thread::spawn`. +می‌خواهیم thread pool ما به روشی مشابه و آشنا کار کند، +به‌طوری که تغییر از استفاده‌ی مستقیم از threadها به استفاده از thread pool +نیاز به تغییرات بزرگ در کدی که از API ما استفاده می‌کند نداشته باشد. +لیستینگ 21-12 رابط فرضی ساختار `ThreadPool` را نشان می‌دهد +که می‌خواهیم به جای `thread::spawn` از آن استفاده کنیم. -<Listing number="21-12" file-name="src/main.rs" caption="Our ideal `ThreadPool` interface"> +<Listing number="21-12" file-name="src/main.rs" caption="رابط ایده‌آل برای `ThreadPool`"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch21-web-server/listing-21-12/src/main.rs:here}} @@ -149,37 +114,35 @@ struct we want to use instead of `thread::spawn`. </Listing> -We use `ThreadPool::new` to create a new thread pool with a configurable number -of threads, in this case four. Then, in the `for` loop, `pool.execute` has a -similar interface as `thread::spawn` in that it takes a closure the pool should -run for each stream. We need to implement `pool.execute` so it takes the -closure and gives it to a thread in the pool to run. This code won’t yet -compile, but we’ll try so that the compiler can guide us in how to fix it. - -<!-- Old headings. Do not remove or links may break. --> +ما با استفاده از `ThreadPool::new` یک thread pool جدید با تعداد قابل تنظیم thread ایجاد می‌کنیم، +در این مثال تعداد چهار thread است. +سپس در حلقه‌ی `for`، متد `pool.execute` رابطی مشابه با `thread::spawn` دارد، +که یک closure می‌گیرد و pool باید آن را برای هر stream اجرا کند. +ما باید `pool.execute` را پیاده‌سازی کنیم تا این closure را بگیرد +و به یک thread در pool بدهد تا اجرا شود. +این کد هنوز کامپایل نخواهد شد، اما این کار را انجام می‌دهیم تا کامپایلر ما را در رفع خطاها راهنمایی کند. <a id="building-the-threadpool-struct-using-compiler-driven-development"></a> -#### Building `ThreadPool` Using Compiler-Driven Development +#### ساخت `ThreadPool` با استفاده از توسعه‌ی مبتنی بر خطاهای کامپایلر -Make the changes in Listing 21-12 to _src/main.rs_, and then let’s use the -compiler errors from `cargo check` to drive our development. Here is the first -error we get: +تغییرات لیستینگ 21-12 را در فایل *src/main.rs* اعمال کنید، +سپس اجازه دهید خطاهای کامپایلر از دستور `cargo check` روند توسعه‌ی ما را هدایت کنند. +در اینجا اولین خطایی که دریافت می‌کنیم آمده است: ```console {{#include ../listings/ch21-web-server/listing-21-12/output.txt}} ``` -Great! This error tells us we need a `ThreadPool` type or module, so we’ll -build one now. Our `ThreadPool` implementation will be independent of the kind -of work our web server is doing. So let’s switch the `hello` crate from a -binary crate to a library crate to hold our `ThreadPool` implementation. After -we change to a library crate, we could also use the separate thread pool -library for any work we want to do using a thread pool, not just for serving -web requests. +عالی! این خطا به ما می‌گوید که به یک نوع یا ماژول `ThreadPool` نیاز داریم، +پس اکنون یکی می‌سازیم. پیاده‌سازی `ThreadPool` ما مستقل از نوع کاری است که وب‌سرور انجام می‌دهد. +بنابراین، بیایید crate `hello` را از یک crate دودویی به یک crate کتابخانه‌ای تبدیل کنیم +تا پیاده‌سازی `ThreadPool` را در آن قرار دهیم. +پس از تبدیل به crate کتابخانه‌ای، می‌توانیم از این کتابخانه‌ی thread pool جداگانه برای هر کاری که می‌خواهیم با thread pool انجام دهیم استفاده کنیم، +نه فقط برای سرویس‌دهی به درخواست‌های وب. -Create a _src/lib.rs_ file that contains the following, which is the simplest -definition of a `ThreadPool` struct that we can have for now: +یک فایل *src/lib.rs* ایجاد کنید که شامل کد زیر باشد، +که ساده‌ترین تعریف ممکن برای `ThreadPool` است که فعلاً می‌توانیم داشته باشیم: <Listing file-name="src/lib.rs"> @@ -189,9 +152,8 @@ definition of a `ThreadPool` struct that we can have for now: </Listing> - -Then edit the _main.rs_ file to bring `ThreadPool` into scope from the library -crate by adding the following code to the top of _src/main.rs_: +سپس فایل *main.rs* را ویرایش کنید تا با افزودن کد زیر به بالای فایل *src/main.rs*، +`ThreadPool` را از crate کتابخانه‌ای وارد حوزه (scope) کنید: <Listing file-name="src/main.rs"> @@ -201,18 +163,13 @@ crate by adding the following code to the top of _src/main.rs_: </Listing> -This code still won’t work, but let’s check it again to get the next error that -we need to address: +این کد همچنان کار نخواهد کرد، اما بیایید دوباره آن را بررسی کنیم تا خطای بعدی که باید برطرف کنیم را ببینیم: ```console {{#include ../listings/ch21-web-server/no-listing-01-define-threadpool-struct/output.txt}} ``` -This error indicates that next we need to create an associated function named -`new` for `ThreadPool`. We also know that `new` needs to have one parameter -that can accept `4` as an argument and should return a `ThreadPool` instance. -Let’s implement the simplest `new` function that will have those -characteristics: +این خطا نشان می‌دهد که باید تابع وابسته‌ای به نام `new` برای `ThreadPool` ایجاد کنیم. همچنین می‌دانیم که `new` باید یک پارامتر داشته باشد که بتواند مقدار `4` را به عنوان آرگومان بپذیرد و یک نمونه از `ThreadPool` بازگرداند. بیایید ساده‌ترین تابع `new` که این خصوصیات را دارد پیاده‌سازی کنیم: <Listing file-name="src/lib.rs"> @@ -222,33 +179,30 @@ characteristics: </Listing> -We chose `usize` as the type of the `size` parameter because we know that a -negative number of threads doesn’t make any sense. We also know we’ll use this -`4` as the number of elements in a collection of threads, which is what the -`usize` type is for, as discussed in [“Integer Types”][integer-types]<!-- ignore ---> in Chapter 3. +ما نوع پارامتر `size` را `usize` انتخاب کردیم چون می‌دانیم تعداد منفی thread منطقی نیست. +همچنین می‌دانیم که این مقدار `4` را به‌عنوان تعداد عناصر در یک مجموعه از threadها استفاده خواهیم کرد، +که برای همین منظور نوع `usize` مناسب است، +همان‌طور که در بخش [“انواع عدد صحیح”][integer-types]<!-- ignore --> در فصل ۳ توضیح داده شده است. -Let’s check the code again: +بیایید دوباره کد را بررسی کنیم: ```console {{#include ../listings/ch21-web-server/no-listing-02-impl-threadpool-new/output.txt}} ``` -Now the error occurs because we don’t have an `execute` method on `ThreadPool`. -Recall from [“Creating a Finite Number of -Threads”](#creating-a-finite-number-of-threads)<!-- ignore --> that we decided -our thread pool should have an interface similar to `thread::spawn`. In -addition, we’ll implement the `execute` function so it takes the closure it’s -given and gives it to an idle thread in the pool to run. - -We’ll define the `execute` method on `ThreadPool` to take a closure as a -parameter. Recall from [“Moving Captured Values Out of the Closure and the `Fn` -Traits”][fn-traits]<!-- ignore --> in Chapter 13 that we can take closures as -parameters with three different traits: `Fn`, `FnMut`, and `FnOnce`. We need to -decide which kind of closure to use here. We know we’ll end up doing something -similar to the standard library `thread::spawn` implementation, so we can look -at what bounds the signature of `thread::spawn` has on its parameter. The -documentation shows us the following: +اکنون خطا رخ می‌دهد چون متد `execute` روی `ThreadPool` تعریف نشده است. +به یاد بیاورید از بخش [“ایجاد تعداد محدودی thread”](#creating-a-finite-number-of-threads)<!-- ignore --> +که تصمیم گرفتیم رابط کاربری thread pool ما شبیه به `thread::spawn` باشد. +علاوه بر این، متد `execute` را پیاده‌سازی خواهیم کرد تا closure دریافت‌شده را بگیرد +و آن را به یک thread بیکار در pool بدهد تا اجرا کند. + +متد `execute` را روی `ThreadPool` تعریف می‌کنیم تا یک closure را به‌عنوان پارامتر بگیرد. +به یاد بیاورید از بخش [“انتقال مقادیر گرفته‌شده از closure و traitهای `Fn`”][fn-traits]<!-- ignore --> در فصل ۱۳ +که می‌توانیم closureها را با سه trait مختلف به‌عنوان پارامتر بگیریم: `Fn`، `FnMut` و `FnOnce`. +باید تصمیم بگیریم در اینجا از کدام نوع closure استفاده کنیم. +می‌دانیم که قرار است کاری مشابه پیاده‌سازی `thread::spawn` در کتابخانه استاندارد انجام دهیم، +پس می‌توانیم به محدودیت‌هایی که امضای تابع `thread::spawn` روی پارامترش دارد نگاه کنیم. +مستندات به ما موارد زیر را نشان می‌دهد: ```rust,ignore pub fn spawn<F, T>(f: F) -> JoinHandle<T> @@ -258,19 +212,9 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T> T: Send + 'static, ``` -The `F` type parameter is the one we’re concerned with here; the `T` type -parameter is related to the return value, and we’re not concerned with that. We -can see that `spawn` uses `FnOnce` as the trait bound on `F`. This is probably -what we want as well, because we’ll eventually pass the argument we get in -`execute` to `spawn`. We can be further confident that `FnOnce` is the trait we -want to use because the thread for running a request will only execute that -request’s closure one time, which matches the `Once` in `FnOnce`. +نوع پارامتر `F` همان چیزی است که در اینجا به آن توجه داریم؛ پارامتر نوع `T` مربوط به مقدار بازگشتی است و ما به آن توجه نداریم. می‌توانیم ببینیم که `spawn` از `FnOnce` به عنوان محدودیت ویژگی روی `F` استفاده می‌کند. این احتمالاً چیزی است که ما نیز می‌خواهیم، زیرا در نهایت آرگومان دریافتی در `execute` را به `spawn` پاس می‌دهیم. ما اطمینان بیشتری داریم که `FnOnce` همان ویژگی مورد نظر ما است، زیرا Thread برای اجرای یک درخواست فقط Closure مربوط به آن درخواست را یک بار اجرا می‌کند، که با "Once" در `FnOnce` مطابقت دارد. -The `F` type parameter also has the trait bound `Send` and the lifetime bound -`'static`, which are useful in our situation: we need `Send` to transfer the -closure from one thread to another and `'static` because we don’t know how long -the thread will take to execute. Let’s create an `execute` method on -`ThreadPool` that will take a generic parameter of type `F` with these bounds: +پارامتر نوع `F` همچنین دارای محدودیت ویژگی `Send` و محدودیت طول عمر `'static` است، که در وضعیت ما مفید هستند: ما به `Send` نیاز داریم تا Closure را از یک Thread به Thread دیگر منتقل کنیم و به `'static` نیاز داریم زیرا نمی‌دانیم اجرای Thread چه مدت طول می‌کشد. بیایید یک متد `execute` روی `ThreadPool` ایجاد کنیم که یک پارامتر عمومی از نوع `F` با این محدودیت‌ها بپذیرد: <Listing file-name="src/lib.rs"> @@ -280,45 +224,37 @@ the thread will take to execute. Let’s create an `execute` method on </Listing> -We still use the `()` after `FnOnce` because this `FnOnce` represents a closure -that takes no parameters and returns the unit type `()`. Just like function -definitions, the return type can be omitted from the signature, but even if we -have no parameters, we still need the parentheses. +ما همچنان از `()` پس از `FnOnce` استفاده می‌کنیم زیرا این `FnOnce` نشان‌دهنده یک Closure است که هیچ پارامتری نمی‌گیرد و نوع `()` را بازمی‌گرداند. درست مانند تعریف توابع، می‌توان نوع بازگشتی را از امضا حذف کرد، اما حتی اگر هیچ پارامتری نداشته باشیم، همچنان به پرانتزها نیاز داریم. -Again, this is the simplest implementation of the `execute` method: it does -nothing, but we’re only trying to make our code compile. Let’s check it again: +دوباره، این ساده‌ترین پیاده‌سازی متد `execute` است: هیچ کاری انجام نمی‌دهد، +اما هدف ما فقط این است که کدمان کامپایل شود. +بیایید دوباره آن را بررسی کنیم: ```console {{#include ../listings/ch21-web-server/no-listing-03-define-execute/output.txt}} ``` -It compiles! But note that if you try `cargo run` and make a request in the -browser, you’ll see the errors in the browser that we saw at the beginning of -the chapter. Our library isn’t actually calling the closure passed to `execute` -yet! +کد کامپایل می‌شود! اما توجه داشته باشید که اگر `cargo run` را اجرا کنید و در مرورگر یک درخواست ارسال کنید، خطاهایی را در مرورگر خواهید دید که در ابتدای فصل دیده بودیم. کتابخانه ما هنوز Closure پاس‌داده‌شده به `execute` را فراخوانی نمی‌کند! -> Note: A saying you might hear about languages with strict compilers, such as -> Haskell and Rust, is “if the code compiles, it works.” But this saying is not -> universally true. Our project compiles, but it does absolutely nothing! If we -> were building a real, complete project, this would be a good time to start -> writing unit tests to check that the code compiles _and_ has the behavior we -> want. +> نکته: یک ضرب‌المثل درباره زبان‌هایی با کامپایلرهای سخت‌گیر، مانند Haskell و Rust، این است که "اگر کد کامپایل شود، کار می‌کند." اما این ضرب‌المثل همیشه درست نیست. پروژه ما کامپایل می‌شود، اما هیچ کاری انجام نمی‌دهد! اگر در حال ساخت یک پروژه واقعی و کامل بودیم، اکنون زمان خوبی برای شروع نوشتن تست‌های واحد بود تا بررسی کنیم که کد هم کامپایل می‌شود _و_ رفتار مورد نظر ما را دارد. -Consider: what would be different here if we were going to execute a future -instead of a closure? +فرض کنید: اگر قرار بود به جای closure، یک future اجرا کنیم، +چه تفاوت‌هایی در اینجا وجود داشت؟ -#### Validating the Number of Threads in `new` +#### اعتبارسنجی تعداد Threadها در `new` -We aren’t doing anything with the parameters to `new` and `execute`. Let’s -implement the bodies of these functions with the behavior we want. To start, -let’s think about `new`. Earlier we chose an unsigned type for the `size` -parameter because a pool with a negative number of threads makes no sense. -However, a pool with zero threads also makes no sense, yet zero is a perfectly -valid `usize`. We’ll add code to check that `size` is greater than zero before -we return a `ThreadPool` instance and have the program panic if it receives a -zero by using the `assert!` macro, as shown in Listing 21-13. +فعلاً با پارامترهای `new` و `execute` کاری انجام نمی‌دهیم. +بیایید بدنه‌ی این توابع را با رفتار مورد نظر پیاده‌سازی کنیم. +برای شروع، به تابع `new` فکر کنیم. +قبلاً برای پارامتر `size` نوع بدون علامت (unsigned) را انتخاب کردیم، +چون یک pool با تعداد منفی thread منطقی نیست. +اما یک pool با صفر thread نیز منطقی نیست، +با این‌که صفر یک مقدار معتبر از نوع `usize` است. +ما کدی اضافه خواهیم کرد که بررسی کند مقدار `size` بزرگ‌تر از صفر باشد، +و اگر صفر دریافت شد، با استفاده از ماکروی `assert!` برنامه panic کند، +همان‌طور که در لیستینگ 21-13 نشان داده شده است. -<Listing number="21-13" file-name="src/lib.rs" caption="Implementing `ThreadPool::new` to panic if `size` is zero"> +<Listing number="21-13" file-name="src/lib.rs" caption="پیاده‌سازی `ThreadPool::new` برای توقف برنامه در صورت صفر بودن `size`"> ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-13/src/lib.rs:here}} @@ -326,29 +262,17 @@ zero by using the `assert!` macro, as shown in Listing 21-13. </Listing> -We’ve also added some documentation for our `ThreadPool` with doc comments. -Note that we followed good documentation practices by adding a section that -calls out the situations in which our function can panic, as discussed in -Chapter 14. Try running `cargo doc --open` and clicking the `ThreadPool` struct -to see what the generated docs for `new` look like! +ما همچنین برخی مستندات برای `ThreadPool` خود با استفاده از نظرات داکیومنت (_doc comments_) اضافه کرده‌ایم. توجه داشته باشید که ما از اصول خوب مستندسازی پیروی کرده‌ایم و بخشی را اضافه کرده‌ایم که شرایطی که تابع ما ممکن است به وحشت بیفتد (_panic_) را توضیح می‌دهد، همان‌طور که در فصل ۱۴ مورد بحث قرار گرفت. دستور `cargo doc --open` را اجرا کنید و روی ساختار `ThreadPool` کلیک کنید تا ببینید مستندات تولیدشده برای `new` چگونه به نظر می‌رسند! -Instead of adding the `assert!` macro as we’ve done here, we could change `new` -into `build` and return a `Result` like we did with `Config::build` in the I/O -project in Listing 12-9. But we’ve decided in this case that trying to create a -thread pool without any threads should be an unrecoverable error. If you’re -feeling ambitious, try to write a function named `build` with the following -signature to compare with the `new` function: +به جای اضافه کردن ماکروی `assert!` همان‌طور که اینجا انجام دادیم، می‌توانستیم `new` را به `build` تغییر دهیم و یک `Result` بازگردانیم، مانند آنچه با `Config::build` در پروژه I/O در لیست ۱۲-۹ انجام دادیم. اما در این مورد تصمیم گرفته‌ایم که تلاش برای ایجاد یک Thread Pool بدون هیچ Threadی باید یک خطای غیرقابل بازیابی باشد. اگر احساس جاه‌طلبی می‌کنید، سعی کنید تابعی به نام `build` با امضای زیر بنویسید تا با تابع `new` مقایسه کنید: ```rust,ignore pub fn build(size: usize) -> Result<ThreadPool, PoolCreationError> { ``` -#### Creating Space to Store the Threads +#### ایجاد فضایی برای ذخیره Threadها -Now that we have a way to know we have a valid number of threads to store in -the pool, we can create those threads and store them in the `ThreadPool` struct -before returning the struct. But how do we “store” a thread? Let’s take another -look at the `thread::spawn` signature: +اکنون که روشی برای اطمینان از تعداد معتبر Threadهایی که در Pool ذخیره می‌شوند داریم، می‌توانیم این Threadها را ایجاد کرده و آن‌ها را در ساختار `ThreadPool` قبل از بازگرداندن ساختار ذخیره کنیم. اما چگونه می‌توانیم یک Thread را "ذخیره" کنیم؟ بیایید دوباره به امضای `thread::spawn` نگاه کنیم: ```rust,ignore pub fn spawn<F, T>(f: F) -> JoinHandle<T> @@ -358,18 +282,11 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T> T: Send + 'static, ``` -The `spawn` function returns a `JoinHandle<T>`, where `T` is the type that the -closure returns. Let’s try using `JoinHandle` too and see what happens. In our -case, the closures we’re passing to the thread pool will handle the connection -and not return anything, so `T` will be the unit type `()`. +تابع `spawn` یک `JoinHandle<T>` بازمی‌گرداند، جایی که `T` نوعی است که Closure بازمی‌گرداند. بیایید ما هم از `JoinHandle` استفاده کنیم و ببینیم چه اتفاقی می‌افتد. در مورد ما، Closureهایی که به Thread Pool ارسال می‌کنیم اتصال را مدیریت کرده و چیزی بازنمی‌گردانند، بنابراین `T` برابر با نوع واحد `()` خواهد بود. -The code in Listing 21-14 will compile but doesn’t create any threads yet. -We’ve changed the definition of `ThreadPool` to hold a vector of -`thread::JoinHandle<()>` instances, initialized the vector with a capacity of -`size`, set up a `for` loop that will run some code to create the threads, and -returned a `ThreadPool` instance containing them. +کد موجود در لیست ۲۱-۱۴ کامپایل می‌شود اما هنوز هیچ Threadی ایجاد نمی‌کند. ما تعریف `ThreadPool` را تغییر داده‌ایم تا یک بردار از نمونه‌های `thread::JoinHandle<()>` را نگه دارد، بردار را با ظرفیتی برابر با `size` مقداردهی اولیه کرده‌ایم، یک حلقه `for` تنظیم کرده‌ایم که کدی برای ایجاد Threadها اجرا می‌کند، و یک نمونه از `ThreadPool` که آن‌ها را در خود دارد بازمی‌گرداند. -<Listing number="21-14" file-name="src/lib.rs" caption="Creating a vector for `ThreadPool` to hold the threads"> +<Listing number="21-14" file-name="src/lib.rs" caption="ایجاد یک بردار برای `ThreadPool` برای نگهداری Threadها"> ```rust,ignore,not_desired_behavior {{#rustdoc_include ../listings/ch21-web-server/listing-21-14/src/lib.rs:here}} @@ -377,68 +294,56 @@ returned a `ThreadPool` instance containing them. </Listing> -We’ve brought `std::thread` into scope in the library crate because we’re -using `thread::JoinHandle` as the type of the items in the vector in -`ThreadPool`. +ما `std::thread` را در crate کتابخانه‌ای وارد حوزه کرده‌ایم، +چون از `thread::JoinHandle` به‌عنوان نوع آیتم‌های موجود در بردار داخل `ThreadPool` استفاده می‌کنیم. -Once a valid size is received, our `ThreadPool` creates a new vector that can -hold `size` items. The `with_capacity` function performs the same task as -`Vec::new` but with an important difference: it pre-allocates space in the -vector. Because we know we need to store `size` elements in the vector, doing -this allocation up front is slightly more efficient than using `Vec::new`, -which resizes itself as elements are inserted. +پس از دریافت مقدار معتبر برای `size`، `ThreadPool` ما یک بردار جدید ایجاد می‌کند +که می‌تواند `size` آیتم را در خود نگه دارد. +تابع `with_capacity` همان کاری را انجام می‌دهد که `Vec::new` انجام می‌دهد، +اما با یک تفاوت مهم: فضای لازم را از پیش در بردار تخصیص می‌دهد. +از آن‌جایی که می‌دانیم باید `size` عنصر در بردار ذخیره کنیم، +انجام این تخصیص پیشاپیش کمی کارآمدتر از استفاده از `Vec::new` است، +که هنگام وارد کردن عناصر، اندازه‌ی خود را تغییر می‌دهد. -When you run `cargo check` again, it should succeed. +وقتی دوباره `cargo check` را اجرا کنید، باید با موفقیت انجام شود. <!-- Old headings. Do not remove or links may break. --> <a id ="a-worker-struct-responsible-for-sending-code-from-the-threadpool-to-a-thread"></a> -#### Sending Code from the `ThreadPool` to a Thread - -We left a comment in the `for` loop in Listing 21-14 regarding the creation of -threads. Here, we’ll look at how we actually create threads. The standard -library provides `thread::spawn` as a way to create threads, and -`thread::spawn` expects to get some code the thread should run as soon as the -thread is created. However, in our case, we want to create the threads and have -them _wait_ for code that we’ll send later. The standard library’s -implementation of threads doesn’t include any way to do that; we have to -implement it manually. - -We’ll implement this behavior by introducing a new data structure between the -`ThreadPool` and the threads that will manage this new behavior. We’ll call -this data structure _Worker_, which is a common term in pooling -implementations. The `Worker` picks up code that needs to be run and runs the -code in its thread. - -Think of people working in the kitchen at a restaurant: the workers wait until -orders come in from customers, and then they’re responsible for taking those -orders and filling them. - -Instead of storing a vector of `JoinHandle<()>` instances in the thread pool, -we’ll store instances of the `Worker` struct. Each `Worker` will store a single -`JoinHandle<()>` instance. Then we’ll implement a method on `Worker` that will -take a closure of code to run and send it to the already running thread for -execution. We’ll also give each `Worker` an `id` so we can distinguish between -the different instances of `Worker` in the pool when logging or debugging. - -Here is the new process that will happen when we create a `ThreadPool`. We’ll -implement the code that sends the closure to the thread after we have `Worker` -set up in this way: - -1. Define a `Worker` struct that holds an `id` and a `JoinHandle<()>`. -2. Change `ThreadPool` to hold a vector of `Worker` instances. -3. Define a `Worker::new` function that takes an `id` number and returns a - `Worker` instance that holds the `id` and a thread spawned with an empty - closure. -4. In `ThreadPool::new`, use the `for` loop counter to generate an `id`, create - a new `Worker` with that `id`, and store the `Worker` in the vector. - -If you’re up for a challenge, try implementing these changes on your own before -looking at the code in Listing 21-15. - -Ready? Here is Listing 21-15 with one way to make the preceding modifications. - -<Listing number="21-15" file-name="src/lib.rs" caption="Modifying `ThreadPool` to hold `Worker` instances instead of holding threads directly"> +#### ساختار `Worker` مسئول ارسال کد از `ThreadPool` به یک Thread + +در حلقه `for` در لیست ۲۱-۱۴، نظری در مورد ایجاد Threadها گذاشتیم. در اینجا بررسی خواهیم کرد که چگونه واقعاً Threadها را ایجاد می‌کنیم. کتابخانه استاندارد `thread::spawn` را به عنوان روشی برای ایجاد Threadها ارائه می‌دهد، و `thread::spawn` انتظار دارد کدی دریافت کند که Thread بلافاصله پس از ایجاد اجرا کند. با این حال، در مورد ما، می‌خواهیم Threadها را ایجاد کنیم و آن‌ها را منتظر نگه داریم تا کدی که بعداً ارسال می‌کنیم را اجرا کنند. پیاده‌سازی Threadها در کتابخانه استاندارد هیچ راهی برای انجام این کار ارائه نمی‌دهد؛ بنابراین باید آن را به صورت دستی پیاده‌سازی کنیم. + +این رفتار را با معرفی یک ساختار داده‌ی جدید بین `ThreadPool` و threadها پیاده‌سازی می‌کنیم که این رفتار جدید را مدیریت کند. +این ساختار داده را *Worker* می‌نامیم که اصطلاح رایجی در پیاده‌سازی‌های pooling است. +`Worker` کدی که باید اجرا شود را دریافت می‌کند و آن را در thread خودش اجرا می‌کند. + +این را مانند افرادی در آشپزخانه‌ی یک رستوران تصور کنید: +کارگران منتظر می‌مانند تا سفارش‌ها از مشتریان برسد، +سپس مسئول پذیرش و آماده‌سازی آن سفارش‌ها هستند. + +به جای نگه‌داشتن یک بردار از نمونه‌های `JoinHandle<()>` در thread pool، +نمونه‌های `Worker` را ذخیره خواهیم کرد. +هر `Worker` یک نمونه‌ی تک `JoinHandle<()>` نگه می‌دارد. +سپس متدی روی `Worker` پیاده‌سازی می‌کنیم که یک closure از کد برای اجرا بگیرد +و آن را به thread در حال اجرای مربوطه برای اجرا ارسال کند. +همچنین به هر `Worker` یک `id` اختصاص می‌دهیم تا بتوانیم هنگام لاگ‌گیری یا اشکال‌زدایی، +بین نمونه‌های مختلف `Worker` در pool تمایز قائل شویم. + +این فرآیند جدیدی است که هنگام ایجاد یک `ThreadPool` اتفاق می‌افتد. کدی که Closure را به Thread ارسال می‌کند، پس از تنظیم `Worker` به این شکل پیاده‌سازی خواهد شد: + +۱. یک `struct` به نام `Worker` تعریف کنید که شامل یک فیلد `id` و یک `JoinHandle<()>` باشد. +۲. ساختار `ThreadPool` را تغییر دهید تا یک بردار از نمونه‌های `Worker` نگه دارد. +۳. تابعی به نام `Worker::new` تعریف کنید که یک شماره‌ی `id` بگیرد و یک نمونه `Worker` بازگرداند +که شامل آن `id` و یک thread ساخته شده با یک closure خالی باشد. +۴. در تابع `ThreadPool::new`، از شمارنده حلقه‌ی `for` برای تولید `id` استفاده کنید، +یک `Worker` جدید با آن `id` بسازید و آن را در بردار ذخیره کنید. + +اگر آماده یک چالش هستید، سعی کنید این تغییرات را خودتان پیاده‌سازی کنید قبل از اینکه به کد موجود در لیست ۲۱-۱۵ نگاه کنید. + +آماده‌اید؟ در اینجا لیست ۲۱-۱۵ با یک روش برای انجام اصلاحات قبلی آورده شده است. + +<Listing number="21-15" file-name="src/lib.rs" caption="تغییر `ThreadPool` برای نگهداری نمونه‌های `Worker` به جای نگهداری مستقیم Threadها"> ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-15/src/lib.rs:here}} @@ -446,59 +351,31 @@ Ready? Here is Listing 21-15 with one way to make the preceding modifications. </Listing> -We’ve changed the name of the field on `ThreadPool` from `threads` to `workers` -because it’s now holding `Worker` instances instead of `JoinHandle<()>` -instances. We use the counter in the `for` loop as an argument to -`Worker::new`, and we store each new `Worker` in the vector named `workers`. - -External code (like our server in _src/main.rs_) doesn’t need to know the -implementation details regarding using a `Worker` struct within `ThreadPool`, -so we make the `Worker` struct and its `new` function private. The -`Worker::new` function uses the `id` we give it and stores a `JoinHandle<()>` -instance that is created by spawning a new thread using an empty closure. - -> Note: If the operating system can’t create a thread because there aren’t -> enough system resources, `thread::spawn` will panic. That will cause our -> whole server to panic, even though the creation of some threads might -> succeed. For simplicity’s sake, this behavior is fine, but in a production -> thread pool implementation, you’d likely want to use -> [`std::thread::Builder`][builder]<!-- ignore --> and its -> [`spawn`][builder-spawn]<!-- ignore --> method that returns `Result` instead. - -This code will compile and will store the number of `Worker` instances we -specified as an argument to `ThreadPool::new`. But we’re _still_ not processing -the closure that we get in `execute`. Let’s look at how to do that next. - -#### Sending Requests to Threads via Channels - -The next problem we’ll tackle is that the closures given to `thread::spawn` do -absolutely nothing. Currently, we get the closure we want to execute in the -`execute` method. But we need to give `thread::spawn` a closure to run when we -create each `Worker` during the creation of the `ThreadPool`. - -We want the `Worker` structs that we just created to fetch the code to run from -a queue held in the `ThreadPool` and send that code to its thread to run. - -The channels we learned about in Chapter 16—a simple way to communicate between -two threads—would be perfect for this use case. We’ll use a channel to function -as the queue of jobs, and `execute` will send a job from the `ThreadPool` to -the `Worker` instances, which will send the job to its thread. Here is the plan: - -1. The `ThreadPool` will create a channel and hold on to the sender. -2. Each `Worker` will hold on to the receiver. -3. We’ll create a new `Job` struct that will hold the closures we want to send - down the channel. -4. The `execute` method will send the job it wants to execute through the - sender. -5. In its thread, the `Worker` will loop over its receiver and execute the - closures of any jobs it receives. - -Let’s start by creating a channel in `ThreadPool::new` and holding the sender -in the `ThreadPool` instance, as shown in Listing 21-16. The `Job` struct -doesn’t hold anything for now but will be the type of item we’re sending down -the channel. - -<Listing number="21-16" file-name="src/lib.rs" caption="Modifying `ThreadPool` to store the sender of a channel that transmits `Job` instances"> +ما نام فیلد موجود در `ThreadPool` را از `threads` به `workers` تغییر داده‌ایم زیرا اکنون نمونه‌های `Worker` را نگه می‌دارد، نه نمونه‌های `JoinHandle<()>`. از شمارنده حلقه `for` به عنوان آرگومان برای `Worker::new` استفاده می‌کنیم و هر `Worker` جدید را در بردار به نام `workers` ذخیره می‌کنیم. + +کد خارجی (مانند سرور ما در _src/main.rs_) نیازی ندارد جزئیات پیاده‌سازی مربوط به استفاده از ساختار `Worker` در داخل `ThreadPool` را بداند، بنابراین ساختار `Worker` و تابع `new` آن را خصوصی می‌کنیم. تابع `Worker::new` از `id` داده‌شده استفاده کرده و یک نمونه `JoinHandle<()>` ایجاد می‌کند که با ایجاد یک Thread جدید با یک Closure خالی تولید می‌شود. + +> نکته: اگر سیستم‌عامل نتواند به دلیل کمبود منابع سیستم، یک Thread ایجاد کند، `thread::spawn` به وحشت خواهد افتاد (_panic_). این باعث می‌شود کل سرور ما به وحشت بیفتد، حتی اگر ایجاد برخی Threadها موفق باشد. برای سادگی، این رفتار مشکلی ندارد، اما در یک پیاده‌سازی تولیدی برای Thread Pool، احتمالاً از [`std::thread::Builder`][builder]<!-- ignore --> و متد [`spawn`][builder-spawn]<!-- ignore --> که یک `Result` بازمی‌گرداند، استفاده می‌کنید. + +این کد کامپایل خواهد شد و تعداد نمونه‌های `Worker` را که به عنوان آرگومان به `ThreadPool::new` مشخص کرده‌ایم ذخیره می‌کند. اما ما _هنوز_ Closureی که در `execute` دریافت می‌کنیم را پردازش نمی‌کنیم. بیایید بررسی کنیم چگونه این کار را انجام دهیم. + +#### ارسال درخواست‌ها به Threadها از طریق Channelها + +مشکل بعدی که به آن می‌پردازیم این است که Closureهایی که به `thread::spawn` داده شده‌اند، هیچ کاری انجام نمی‌دهند. در حال حاضر، Closureی که می‌خواهیم اجرا کنیم را در متد `execute` دریافت می‌کنیم. اما نیاز داریم که یک Closure به `thread::spawn` بدهیم تا در هنگام ایجاد هر `Worker` در حین ایجاد `ThreadPool` اجرا شود. + +می‌خواهیم ساختارهای `Worker` که به تازگی ایجاد کرده‌ایم، کدی را که باید اجرا شود از یک صف که در `ThreadPool` نگهداری می‌شود دریافت کرده و آن کد را به Thread خود برای اجرا ارسال کنند. + +Channelهایی که در فصل ۱۶ یاد گرفتیم—راهی ساده برای ارتباط بین دو Thread—برای این مورد استفاده مناسب هستند. ما از یک Channel به عنوان صف کارها استفاده خواهیم کرد و `execute` یک کار را از `ThreadPool` به نمونه‌های `Worker` ارسال می‌کند، که این کار را به Thread خود ارسال می‌کنند. برنامه به شرح زیر خواهد بود: + +1. `ThreadPool` یک Channel ایجاد کرده و نگهدارنده sender آن خواهد بود. +2. هر `Worker` نگهدارنده receiver خواهد بود. +3. یک ساختار `Job` جدید ایجاد خواهیم کرد که Closureهایی که می‌خواهیم از طریق Channel ارسال کنیم را نگه می‌دارد. +4. متد `execute` کاری که می‌خواهد اجرا کند را از طریق sender ارسال خواهد کرد. +5. در Thread خود، `Worker` بر receiver خود حلقه زده و Closureهای هر کاری که دریافت می‌کند را اجرا خواهد کرد. + +بیایید با ایجاد یک Channel در `ThreadPool::new` و نگهداری sender در نمونه `ThreadPool` شروع کنیم، همان‌طور که در لیست ۲۱-۱۶ نشان داده شده است. ساختار `Job` در حال حاضر چیزی نگه نمی‌دارد، اما نوع آیتمی خواهد بود که از طریق Channel ارسال می‌کنیم. + +<Listing number="21-16" file-name="src/lib.rs" caption="تغییر `ThreadPool` برای ذخیره sender یک Channel که نمونه‌های `Job` را منتقل می‌کند"> ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-16/src/lib.rs:here}} @@ -506,15 +383,14 @@ the channel. </Listing> -In `ThreadPool::new`, we create our new channel and have the pool hold the -sender. This will successfully compile. +در `ThreadPool::new`، یک Channel جدید ایجاد می‌کنیم و Pool نگهدارنده sender خواهد بود. این کد با موفقیت کامپایل می‌شود. -Let’s try passing a receiver of the channel into each `Worker` as the thread -pool creates the channel. We know we want to use the receiver in the thread that -the `Worker` instances spawn, so we’ll reference the `receiver` parameter in the -closure. The code in Listing 21-17 won’t quite compile yet. +بیایید هنگام ایجاد کانال توسط thread pool، `receiver` کانال را به هر `Worker` ارسال کنیم. +می‌دانیم که می‌خواهیم از `receiver` در threadی که نمونه‌های `Worker` ایجاد می‌کنند استفاده کنیم، +پس در closure به پارامتر `receiver` رفرنس می‌دهیم. +کد موجود در لیستینگ 21-17 هنوز به‌طور کامل کامپایل نمی‌شود. -<Listing number="21-17" file-name="src/lib.rs" caption="Passing the receiver to each `Worker`"> +<Listing number="21-17" file-name="src/lib.rs" caption="ارسال `receiver` به هر `Worker`"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch21-web-server/listing-21-17/src/lib.rs:here}} @@ -522,34 +398,33 @@ closure. The code in Listing 21-17 won’t quite compile yet. </Listing> -We’ve made some small and straightforward changes: we pass the receiver into -`Worker::new`, and then we use it inside the closure. +ما تغییرات کوچک و واضحی ایجاد کرده‌ایم: receiver را به `Worker::new` ارسال کرده‌ایم و سپس از آن در داخل Closure استفاده کرده‌ایم. + +هنگامی که تلاش می‌کنیم این کد را بررسی کنیم، با این خطا مواجه می‌شویم: -When we try to check this code, we get this error: ```console {{#include ../listings/ch21-web-server/listing-21-17/output.txt}} ``` -The code is trying to pass `receiver` to multiple `Worker` instances. This -won’t work, as you’ll recall from Chapter 16: the channel implementation that -Rust provides is multiple _producer_, single _consumer_. This means we can’t -just clone the consuming end of the channel to fix this code. We also don’t -want to send a message multiple times to multiple consumers; we want one list -of messages with multiple `Worker` instances such that each message gets -processed once. +کد در تلاش است تا `receiver` را به چند نمونه‌ی مختلف از `Worker` ارسال کند. +این کار عملی نیست، همان‌طور که در فصل ۱۶ یاد گرفتیم: پیاده‌سازی کانال در Rust به صورت multiple _producer_ و single _consumer_ است. +یعنی نمی‌توانیم انتهای مصرف‌کننده‌ی کانال را clone کنیم تا این کد را اصلاح کنیم. +همچنین نمی‌خواهیم یک پیام را چند بار به چند مصرف‌کننده ارسال کنیم؛ +هدف این است که یک لیست از پیام‌ها داشته باشیم که چند نمونه `Worker` آن را دریافت کنند، +طوری که هر پیام فقط یک بار پردازش شود. -Additionally, taking a job off the channel queue involves mutating the -`receiver`, so the threads need a safe way to share and modify `receiver`; -otherwise, we might get race conditions (as covered in Chapter 16). +علاوه بر این، برداشتن یک کار از صف کانال شامل تغییر `receiver` می‌شود، بنابراین Threadها به یک روش امن برای اشتراک و تغییر `receiver` نیاز دارند؛ در غیر این صورت، ممکن است با شرایط رقابتی (_race conditions_) مواجه شویم (همان‌طور که در فصل ۱۶ توضیح داده شد). -Recall the thread-safe smart pointers discussed in Chapter 16: to share -ownership across multiple threads and allow the threads to mutate the value, we -need to use `Arc<Mutex<T>>`. The `Arc` type will let multiple `Worker` instances -own the receiver, and `Mutex` will ensure that only one `Worker` gets a job from -the receiver at a time. Listing 21-18 shows the changes we need to make. +به یاد بیاورید اشاره‌گرهای هوشمند ایمن در برابر thread که در فصل ۱۶ بحث شدند: +برای اشتراک مالکیت بین چند thread و اجازه دادن به تغییر مقدار توسط threadها، +باید از `Arc<Mutex<T>>` استفاده کنیم. +نوع `Arc` اجازه می‌دهد چند نمونه‌ی `Worker` مالک `receiver` باشند، +و `Mutex` تضمین می‌کند که در هر لحظه فقط یک `Worker` بتواند از `receiver` یک کار دریافت کند. +لیستینگ 21-18 تغییراتی را که باید انجام دهیم نشان می‌دهد. + +<Listing number="21-18" file-name="src/lib.rs" caption="اشتراک‌گذاری receiver بین نمونه‌های `Worker` با استفاده از `Arc` و `Mutex`"> -<Listing number="21-18" file-name="src/lib.rs" caption="Sharing the receiver among the `Worker` instances using `Arc` and `Mutex`"> ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-18/src/lib.rs:here}} @@ -557,22 +432,22 @@ the receiver at a time. Listing 21-18 shows the changes we need to make. </Listing> -In `ThreadPool::new`, we put the receiver in an `Arc` and a `Mutex`. For each -new `Worker`, we clone the `Arc` to bump the reference count so the `Worker` -instances can share ownership of the receiver. +در تابع `ThreadPool::new`، `receiver` را داخل یک `Arc` و یک `Mutex` قرار می‌دهیم. +برای هر نمونه‌ی جدید از `Worker`، `Arc` را clone می‌کنیم تا شمارنده‌ی رفرنس افزایش یابد، +به‌طوری که نمونه‌های `Worker` بتوانند مالکیت مشترک `receiver` را داشته باشند. -With these changes, the code compiles! We’re getting there! +با این تغییرات، کد کامپایل می‌شود! به نتیجه نزدیک‌تر می‌شویم! -#### Implementing the `execute` Method +#### پیاده‌سازی متد `execute` -Let’s finally implement the `execute` method on `ThreadPool`. We’ll also change -`Job` from a struct to a type alias for a trait object that holds the type of -closure that `execute` receives. As discussed in [“Creating Type Synonyms with -Type Aliases”][creating-type-synonyms-with-type-aliases]<!-- ignore --> in -Chapter 20, type aliases allow us to make long types shorter for ease of use. -Look at Listing 21-19. +بیایید در نهایت متد `execute` را روی `ThreadPool` پیاده‌سازی کنیم. +همچنین `Job` را از یک `struct` به یک type alias برای یک trait object تبدیل می‌کنیم +که نوع closure ای را که `execute` دریافت می‌کند نگه می‌دارد. +همان‌طور که در بخش [“ایجاد مترادف‌های نوع با type alias”][creating-type-synonyms-with-type-aliases]<!-- ignore --> در فصل ۲۰ بحث شد، +type alias به ما اجازه می‌دهد تا انواع طولانی را کوتاه‌تر کنیم و استفاده از آن‌ها را آسان‌تر سازیم. +به لیستینگ 21-19 نگاه کنید. -<Listing number="21-19" file-name="src/lib.rs" caption="Creating a `Job` type alias for a `Box` that holds each closure and then sending the job down the channel"> +<Listing number="21-19" file-name="src/lib.rs" caption="ایجاد یک نام مستعار `Job` برای یک `Box` که هر Closure را نگه می‌دارد و سپس ارسال کار از طریق کانال"> ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-19/src/lib.rs:here}} @@ -580,22 +455,15 @@ Look at Listing 21-19. </Listing> -After creating a new `Job` instance using the closure we get in `execute`, we -send that job down the sending end of the channel. We’re calling `unwrap` on -`send` for the case that sending fails. This might happen if, for example, we -stop all our threads from executing, meaning the receiving end has stopped -receiving new messages. At the moment, we can’t stop our threads from -executing: our threads continue executing as long as the pool exists. The -reason we use `unwrap` is that we know the failure case won’t happen, but the -compiler doesn’t know that. +پس از ایجاد یک نمونه جدید `Job` با استفاده از Closureی که در `execute` دریافت می‌کنیم، آن کار را از طریق بخش ارسال‌کننده کانال ارسال می‌کنیم. ما برای حالتی که ارسال شکست بخورد، روی `send` از `unwrap` استفاده می‌کنیم. این حالت ممکن است رخ دهد، اگر مثلاً همه Threadهای ما از اجرا متوقف شوند، به این معنی که بخش دریافت‌کننده دیگر پیام‌های جدید را دریافت نمی‌کند. در حال حاضر، نمی‌توانیم Threadهای خود را از اجرا متوقف کنیم: Threadهای ما تا زمانی که Pool وجود دارد اجرا می‌شوند. دلیل استفاده از `unwrap` این است که می‌دانیم حالت شکست رخ نخواهد داد، اما کامپایلر این موضوع را نمی‌داند. -But we’re not quite done yet! In the `Worker`, our closure being passed to -`thread::spawn` still only _references_ the receiving end of the channel. -Instead, we need the closure to loop forever, asking the receiving end of the -channel for a job and running the job when it gets one. Let’s make the change -shown in Listing 21-20 to `Worker::new`. +اما هنوز کار تمام نشده است! در `Worker`، closure که به `thread::spawn` داده می‌شود +هنوز فقط به انتهای دریافت‌کننده‌ی کانال _رفرنس_ می‌دهد. +در عوض، نیاز داریم که closure به‌طور پیوسته در حلقه‌ای بی‌نهایت اجرا شود، +از انتهای دریافت‌کننده‌ی کانال درخواست کار کند و هرگاه کار دریافت کرد آن را اجرا نماید. +بیایید تغییرات نشان‌داده شده در لیستینگ 21-20 را در `Worker::new` اعمال کنیم. -<Listing number="21-20" file-name="src/lib.rs" caption="Receiving and executing the jobs in the `Worker` instance’s thread"> +<Listing number="21-20" file-name="src/lib.rs" caption="دریافت و اجرای کارها در thread نمونه‌ی `Worker`"> ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-20/src/lib.rs:here}} @@ -603,25 +471,13 @@ shown in Listing 21-20 to `Worker::new`. </Listing> -Here, we first call `lock` on the `receiver` to acquire the mutex, and then we -call `unwrap` to panic on any errors. Acquiring a lock might fail if the mutex -is in a _poisoned_ state, which can happen if some other thread panicked while -holding the lock rather than releasing the lock. In this situation, calling -`unwrap` to have this thread panic is the correct action to take. Feel free to -change this `unwrap` to an `expect` with an error message that is meaningful to -you. +در اینجا، ابتدا `lock` را روی `receiver` فراخوانی می‌کنیم تا mutex را به دست آوریم، و سپس `unwrap` را فراخوانی می‌کنیم تا در صورت بروز هرگونه خطا، برنامه متوقف شود. به دست آوردن یک قفل ممکن است شکست بخورد اگر mutex در یک وضعیت _poisoned_ باشد، که ممکن است اتفاق بیفتد اگر یک Thread دیگر در حالی که قفل را نگه داشته است به جای آزاد کردن آن متوقف شده باشد. در این شرایط، فراخوانی `unwrap` برای متوقف کردن این Thread اقدام درستی است. می‌توانید این `unwrap` را به یک `expect` با یک پیام خطای معنادار برای خود تغییر دهید. -If we get the lock on the mutex, we call `recv` to receive a `Job` from the -channel. A final `unwrap` moves past any errors here as well, which might occur -if the thread holding the sender has shut down, similar to how the `send` -method returns `Err` if the receiver shuts down. +اگر قفل روی mutex را به دست آوریم، `recv` را فراخوانی می‌کنیم تا یک `Job` را از کانال دریافت کنیم. یک `unwrap` نهایی نیز در اینجا هر گونه خطا را برطرف می‌کند، که ممکن است رخ دهد اگر Threadی که sender را نگه داشته است خاموش شود، مشابه نحوه‌ای که متد `send` در صورت خاموش شدن receiver یک `Err` بازمی‌گرداند. -The call to `recv` blocks, so if there is no job yet, the current thread will -wait until a job becomes available. The `Mutex<T>` ensures that only one -`Worker` thread at a time is trying to request a job. +فراخوانی `recv` مسدود می‌شود، بنابراین اگر هنوز هیچ کاری وجود نداشته باشد، Thread فعلی منتظر می‌ماند تا یک کار در دسترس قرار گیرد. `Mutex<T>` تضمین می‌کند که در هر لحظه فقط یک Thread `Worker` در تلاش برای درخواست یک کار است. -Our thread pool is now in a working state! Give it a `cargo run` and make some -requests: +Thread Pool ما اکنون در وضعیت کاری قرار دارد! دستور `cargo run` را اجرا کنید و چندین درخواست ارسال کنید: <!-- manual-regeneration cd listings/ch21-web-server/listing-21-20 @@ -668,27 +524,18 @@ Worker 0 got a job; executing. Worker 2 got a job; executing. ``` -Success! We now have a thread pool that executes connections asynchronously. -There are never more than four threads created, so our system won’t get -overloaded if the server receives a lot of requests. If we make a request to -_/sleep_, the server will be able to serve other requests by having another -thread run them. +موفقیت! اکنون یک Thread Pool داریم که اتصالات را به صورت همزمان اجرا می‌کند. هرگز بیش از چهار Thread ایجاد نمی‌شود، بنابراین اگر سرور درخواست‌های زیادی دریافت کند، سیستم ما بارگذاری بیش از حد نخواهد شد. اگر یک درخواست به _/sleep_ ارسال کنیم، سرور می‌تواند با استفاده از یک Thread دیگر به سایر درخواست‌ها پاسخ دهد. -> Note: If you open _/sleep_ in multiple browser windows simultaneously, they -> might load one at a time in five-second intervals. Some web browsers execute -> multiple instances of the same request sequentially for caching reasons. This -> limitation is not caused by our web server. +> توجه: اگر مسیر */sleep* را هم‌زمان در چندین پنجره‌ی مرورگر باز کنید، ممکن است درخواست‌ها به‌صورت پشت سر هم و با فاصله‌های پنج ثانیه‌ای بارگذاری شوند. +> برخی مرورگرها به دلایل کش، چندین نمونه از یک درخواست مشابه را به‌صورت ترتیبی اجرا می‌کنند. +> این محدودیت ناشی از سرور وب ما نیست. -This is a good time to pause and consider how the code in Listings 21-18, 21-19, -and 21-20 would be different if we were using futures instead of a closure for -the work to be done. What types would change? How would the method signatures be -different, if at all? What parts of the code would stay the same? +این زمان خوبی است که مکث کنیم و بررسی کنیم چگونه کدهای لیست‌های ۲۱-۱۸، ۲۱-۱۹ و ۲۱-۲۰ اگر به جای Closure از futures برای انجام کار استفاده می‌کردیم، متفاوت می‌بود. چه نوع‌هایی تغییر می‌کردند؟ آیا امضاهای متدها تغییر می‌کردند؟ کدام بخش‌های کد همان‌گونه باقی می‌ماندند؟ -After learning about the `while let` loop in Chapter 17 and Chapter 19, you -might be wondering why we didn’t write the `Worker` thread code as shown in -Listing 21-21. +بعد از آشنایی با حلقه‌ی `while let` در فصل‌های ۱۷ و ۱۹، +ممکن است این سؤال برایتان پیش آمده باشد که چرا کد thread مربوط به `Worker` را مانند آنچه در لیستینگ 21-21 نشان داده شده ننوشته‌ایم. -<Listing number="21-21" file-name="src/lib.rs" caption="An alternative implementation of `Worker::new` using `while let`"> +<Listing number="21-21" file-name="src/lib.rs" caption="یک پیاده‌سازی جایگزین برای `Worker::new` با استفاده از `while let`"> ```rust,ignore,not_desired_behavior {{#rustdoc_include ../listings/ch21-web-server/listing-21-21/src/lib.rs:here}} @@ -696,27 +543,18 @@ Listing 21-21. </Listing> -This code compiles and runs but doesn’t result in the desired threading -behavior: a slow request will still cause other requests to wait to be -processed. The reason is somewhat subtle: the `Mutex` struct has no public -`unlock` method because the ownership of the lock is based on the lifetime of -the `MutexGuard<T>` within the `LockResult<MutexGuard<T>>` that the `lock` -method returns. At compile time, the borrow checker can then enforce the rule -that a resource guarded by a `Mutex` cannot be accessed unless we hold the -lock. However, this implementation can also result in the lock being held -longer than intended if we aren’t mindful of the lifetime of the -`MutexGuard<T>`. - -The code in Listing 21-20 that uses `let job = -receiver.lock().unwrap().recv().unwrap();` works because with `let`, any -temporary values used in the expression on the right-hand side of the equal -sign are immediately dropped when the `let` statement ends. However, `while -let` (and `if let` and `match`) does not drop temporary values until the end of -the associated block. In Listing 21-21, the lock remains held for the duration -of the call to `job()`, meaning other `Worker` instances cannot receive jobs. +این کد کامپایل می‌شود و اجرا می‌شود، اما منجر به رفتار مورد نظر برای threading نمی‌شود: یک درخواست کند همچنان باعث می‌شود سایر درخواست‌ها برای پردازش منتظر بمانند. دلیل آن کمی ظریف است: ساختار `Mutex` متد عمومی `unlock` ندارد، زیرا مالکیت قفل بر اساس طول عمر `MutexGuard<T>` درون `LockResult<MutexGuard<T>>` که متد `lock` بازمی‌گرداند است. در زمان کامپایل، بررسی‌کننده وام می‌تواند این قانون را اعمال کند که منبعی که توسط یک `Mutex` محافظت می‌شود نمی‌تواند دسترسی پیدا کند مگر اینکه قفل را نگه داشته باشیم. با این حال، این پیاده‌سازی همچنین می‌تواند منجر به نگه‌داشتن قفل بیش از حد انتظار شود اگر به طول عمر `MutexGuard<T>` توجه نکنیم. + +کدی که در لیستینگ 21-20 با عبارت +`let job = receiver.lock().unwrap().recv().unwrap();` نوشته شده است، کار می‌کند زیرا در استفاده از `let`، +هر مقدار موقتی که در سمت راست علامت مساوی به کار رفته باشد، +بلافاصله پس از پایان دستور `let` رها (drop) می‌شود. +اما در `while let` (و همچنین `if let` و `match`) مقدارهای موقتی تا پایان بلاک مربوطه رها نمی‌شوند. +در لیستینگ 21-21، قفل (`lock`) تا پایان فراخوانی `job()` نگه داشته می‌شود، +که این یعنی سایر نمونه‌های `Worker` نمی‌توانند در آن مدت کار دریافت کنند. [creating-type-synonyms-with-type-aliases]: ch20-03-advanced-types.html#creating-type-synonyms-with-type-aliases [integer-types]: ch03-02-data-types.html#integer-types [fn-traits]: ch13-01-closures.html#moving-captured-values-out-of-the-closure-and-the-fn-traits -[builder]: ../std/thread/struct.Builder.html -[builder-spawn]: ../std/thread/struct.Builder.html#method.spawn +[builder]: https://doc.rust-lang.org/std/thread/struct.Builder.html +[builder-spawn]: https://doc.rust-lang.org/std/thread/struct.Builder.html#method.spawn diff --git a/src/ch21-03-graceful-shutdown-and-cleanup.md b/src/ch21-03-graceful-shutdown-and-cleanup.md index aee7cca3ac..ce0f0aedf4 100644 --- a/src/ch21-03-graceful-shutdown-and-cleanup.md +++ b/src/ch21-03-graceful-shutdown-and-cleanup.md @@ -1,32 +1,16 @@ -## Graceful Shutdown and Cleanup +## خاموشی و پاک‌سازی منظم -The code in Listing 21-20 is responding to requests asynchronously through the -use of a thread pool, as we intended. We get some warnings about the `workers`, -`id`, and `thread` fields that we’re not using in a direct way that reminds us -we’re not cleaning up anything. When we use the less elegant -<kbd>ctrl</kbd>-<kbd>C</kbd> method to halt the main thread, all other threads -are stopped immediately as well, even if they’re in the middle of serving a -request. +کدی که در فهرست 21-20 آمده است، همان‌طور که انتظار داشتیم، با استفاده از یک thread pool به‌صورت asynchronous به درخواست‌ها پاسخ می‌دهد. در این میان، هشدارهایی در مورد فیلدهای `workers`، `id` و `thread` دریافت می‌کنیم که به‌طور مستقیم از آن‌ها استفاده نمی‌شود و این موضوع به ما یادآوری می‌کند که عملیات پاک‌سازی یا مدیریت پایانی انجام نشده است. زمانی که از روش نه‌چندان ظریف <kbd>Ctrl</kbd>+<kbd>C</kbd> برای متوقف کردن thread اصلی استفاده می‌کنیم، تمام threadهای دیگر نیز بلافاصله متوقف می‌شوند، حتی اگر در حال پردازش یک درخواست باشند. -Next, then, we’ll implement the `Drop` trait to call `join` on each of the -threads in the pool so they can finish the requests they’re working on before -closing. Then we’ll implement a way to tell the threads they should stop -accepting new requests and shut down. To see this code in action, we’ll modify -our server to accept only two requests before gracefully shutting down its -thread pool. +سپس، ما `Drop` trait را پیاده‌سازی خواهیم کرد تا `join` را روی هر یک از نخ‌های موجود در مجموعه نخ فراخوانی کنیم تا بتوانند درخواست‌هایی که در حال کار روی آن‌ها هستند را قبل از بسته‌شدن تکمیل کنند. سپس روشی برای اطلاع به نخ‌ها که نباید درخواست‌های جدید بپذیرند و باید خاموش شوند، پیاده‌سازی خواهیم کرد. برای مشاهده عملکرد این کد، سرور خود را تغییر می‌دهیم تا فقط دو درخواست را قبل از خاموشی منظم مجموعه نخ‌ها بپذیرد. -One thing to notice as we go: none of this affects the parts of the code that -handle executing the closures, so everything here would be just the same if we -were using a thread pool for an async runtime. +چیزی که باید توجه داشته باشید این است که هیچ‌کدام از این موارد بخش‌هایی از کد را که مدیریت اجرای closureها را بر عهده دارند، تحت تأثیر قرار نمی‌دهند، بنابراین همه چیز در اینجا همان‌طور باقی می‌ماند اگر از یک مجموعه نخ برای یک runtime غیرهمزمان استفاده می‌کردیم. -### Implementing the `Drop` Trait on `ThreadPool` +### پیاده‌سازی `Drop` Trait روی `ThreadPool` -Let’s start with implementing `Drop` on our thread pool. When the pool is -dropped, our threads should all join to make sure they finish their work. -Listing 21-22 shows a first attempt at a `Drop` implementation; this code won’t -quite work yet. +بیایید با پیاده‌سازی `Drop` روی مجموعه نخ شروع کنیم. وقتی مجموعه نخ حذف می‌شود، تمام نخ‌های ما باید به یکدیگر ملحق شوند تا مطمئن شویم کار خود را تکمیل می‌کنند. لیستینگ 21-22 اولین تلاش برای پیاده‌سازی `Drop` را نشان می‌دهد؛ این کد هنوز به درستی کار نخواهد کرد. -<Listing number="21-22" file-name="src/lib.rs" caption="Joining each thread when the thread pool goes out of scope"> +<Listing number="21-22" file-name="src/lib.rs" caption="ملحق کردن هر نخ وقتی مجموعه نخ از محدوده خارج می‌شود"> ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch21-web-server/listing-21-22/src/lib.rs:here}} @@ -34,43 +18,22 @@ quite work yet. </Listing> -First we loop through each of the thread pool `workers`. We use `&mut` for this -because `self` is a mutable reference, and we also need to be able to mutate -`worker`. For each `worker`, we print a message saying that this particular -`Worker` instance is shutting down, and then we call `join` on that `Worker` -instance’s thread. If the call to `join` fails, we use `unwrap` to make Rust -panic and go into an ungraceful shutdown. +ابتدا، از میان تمام `worker`های موجود در thread pool یک حلقه اجرا می‌کنیم. از `&mut` استفاده می‌کنیم، زیرا `self` یک ارجاع قابل‌تغییر است و همچنین باید بتوانیم `worker` را نیز تغییر دهیم. برای هر `worker`، پیامی چاپ می‌کنیم که نشان دهد این نمونه‌ی خاص از `Worker` در حال خاموش شدن است، و سپس روی thread مربوط به همان `Worker` تابع `join` را فراخوانی می‌کنیم. اگر فراخوانی `join` با شکست مواجه شود، از `unwrap` استفاده می‌کنیم تا باعث panic در برنامه شود و خاموش شدن برنامه به‌صورت نامناسب انجام گیرد. -Here is the error we get when we compile this code: + +اینجا خطایی که هنگام کامپایل این کد دریافت می‌کنیم آمده است: ```console {{#include ../listings/ch21-web-server/listing-21-22/output.txt}} ``` -The error tells us we can’t call `join` because we only have a mutable borrow of -each `worker` and `join` takes ownership of its argument. To solve this issue, -we need to move the thread out of the `Worker` instance that owns `thread` so -`join` can consume the thread. One way to do this is by taking the same approach -we did in Listing 18-15. If `Worker` held an `Option<thread::JoinHandle<()>>`, -we could call the `take` method on the `Option` to move the value out of the -`Some` variant and leave a `None` variant in its place. In other words, a -`Worker` that is running would have a `Some` variant in `thread`, and when we -wanted to clean up a `Worker`, we’d replace `Some` with `None` so the `Worker` -wouldn’t have a thread to run. - -However, the _only_ time this would come up would be when dropping the `Worker`. -In exchange, we’d have to deal with an `Option<thread::JoinHandle<()>>` anywhere -we accessed `worker.thread`. Idiomatic Rust uses `Option` quite a bit, but when -you find yourself wrapping something you know will always be present in an -`Option` as a workaround like this, it’s a good idea to look for alternative -approaches to make your code cleaner and less error-prone. - -In this case, a better alternative exists: the `Vec::drain` method. It accepts -a range parameter to specify which items to remove from the vector and returns -an iterator of those items. Passing the `..` range syntax will remove *every* -value from the vector. - -So we need to update the `ThreadPool` `drop` implementation like this: +این خطا به ما می‌گوید که نمی‌توانیم `join` را فراخوانی کنیم زیرا فقط یک قرض قابل تغییر از هر `worker` داریم و `join` مالکیت آرگومان خود را می‌گیرد. برای حل این مشکل، باید نخ را از نمونه `Worker` که مالک `thread` است خارج کنیم تا `join` بتواند نخ را مصرف کند. یک راه برای انجام این کار استفاده از همان رویکردی است که در لیستینگ 18-15 استفاده کردیم. اگر `Worker` یک `Option<thread::JoinHandle<()>>` نگه می‌داشت، می‌توانستیم با استفاده از متد `take` مقدار را از نوع `Some` به نوع `None` منتقل کنیم. + +با این حال، تنها زمانی که این مسئله مطرح می‌شود زمانی است که `Worker` حذف می‌شود. در عوض، باید با یک `Option<thread::JoinHandle<()>>` در همه جا سر و کار داشته باشیم. Rust ایدئوماتیک اغلب از `Option` استفاده می‌کند، اما زمانی که متوجه شوید چیزی را در `Option` قرار می‌دهید به عنوان یک راه‌حل موقت، حتی اگر بدانید آن مورد همیشه حضور دارد، ایده خوبی است که به دنبال روش‌های جایگزین باشید. + +در این حالت، یک جایگزین بهتر استفاده از متد `Vec::drain` است. این متد یک پارامتر محدوده می‌گیرد تا مشخص کند کدام آیتم‌ها باید از `Vec` حذف شوند و یک تکرارگر از آن آیتم‌ها بازمی‌گرداند. استفاده از `..` برای محدوده تمام مقادیر را از `Vec` حذف خواهد کرد. + +بنابراین باید پیاده‌سازی `drop` در `ThreadPool` را به این صورت به‌روزرسانی کنیم: <Listing file-name="src/lib.rs"> @@ -80,46 +43,65 @@ So we need to update the `ThreadPool` `drop` implementation like this: </Listing> -This resolves the compiler error and does not require any other changes to our -code. Note that, because drop can be called when panicking, the unwrap -could also panic and cause a double panic, which immediately crashes the -program and ends any cleanup in progress. This is fine for an example program, -but isn’t recommended for production code. - -### Signaling to the Threads to Stop Listening for Jobs - -With all the changes we’ve made, our code compiles without any warnings. -However, the bad news is that this code doesn’t function the way we want it to -yet. The key is the logic in the closures run by the threads of the `Worker` -instances: at the moment, we call `join`, but that won’t shut down the threads, -because they `loop` forever looking for jobs. If we try to drop our -`ThreadPool` with our current implementation of `drop`, the main thread will -block forever, waiting for the first thread to finish. - -To fix this problem, we’ll need a change in the `ThreadPool` `drop` -implementation and then a change in the `Worker` loop. - -First we’ll change the `ThreadPool` `drop` implementation to explicitly drop -the `sender` before waiting for the threads to finish. Listing 21-23 shows the -changes to `ThreadPool` to explicitly drop `sender`. Unlike with the thread, -here we _do_ need to use an `Option` to be able to move `sender` out of -`ThreadPool` with `Option::take`. +این کار خطای کامپایلر را برطرف می‌کند و نیازی +به هیچ تغییر دیگری در کد ما ندارد. توجه داشته باشید +که از آن‌جا که drop می‌تواند هنگام panic فراخوانی شود، +تابع unwrap نیز ممکن است panic ایجاد کند و +باعث double panic شود که در نتیجه، برنامه +بلافاصله crash می‌کند و هرگونه عملیات پاک‌سازی +در حال انجام را متوقف می‌سازد. این موضوع برای یک +برنامه‌ی نمونه قابل قبول است، اما برای کدهای +محصولی توصیه نمی‌شود. + + +### علامت‌دهی به نخ‌ها برای توقف گوش دادن به وظایف + +با تمام تغییراتی که اعمال کرده‌ایم، کد ما بدون هیچ +هشداری کامپایل می‌شود. اما خبر بد این است که +این کد هنوز آن‌طور که می‌خواهیم عمل نمی‌کند. نکته‌ی +کلیدی در منطق closureهایی است که توسط threadهای +نمونه‌های `Worker` اجرا می‌شوند: در حال حاضر +ما تابع `join` را فراخوانی می‌کنیم، اما این باعث +خاموش شدن threadها نمی‌شود، چون آن‌ها +به‌صورت بی‌پایان در حال `loop` برای یافتن job هستند. +اگر سعی کنیم `ThreadPool` را با پیاده‌سازی فعلی +تابع `drop` حذف کنیم، thread اصلی برای همیشه +در حالت انتظار باقی می‌ماند تا اولین thread به پایان برسد. + +برای حل این مشکل، باید تغییری در پیاده‌سازی `drop` در `ThreadPool` و سپس تغییری در حلقه `Worker` ایجاد کنیم. + +ابتدا پیاده‌سازی تابع `drop` برای `ThreadPool` را +تغییر می‌دهیم تا پیش از منتظر ماندن برای پایان یافتن +threadها، به‌صورت صریح `sender` را حذف کند. +فهرست 21-23 تغییرات اعمال‌شده روی `ThreadPool` را +نشان می‌دهد که در آن `sender` به‌طور صریح +حذف می‌شود. برخلاف thread، در این‌جا نیاز داریم +که از یک `Option` استفاده کنیم تا بتوانیم +`sender` را با استفاده از `Option::take` از +ساختار `ThreadPool` بیرون بکشیم. <Listing number="21-23" file-name="src/lib.rs" caption="Explicitly dropping `sender` before joining the `Worker` threads"> + ```rust,noplayground,not_desired_behavior {{#rustdoc_include ../listings/ch21-web-server/listing-21-23/src/lib.rs:here}} ``` </Listing> -Dropping `sender` closes the channel, which indicates no more messages will be -sent. When that happens, all the calls to `recv` that the `Worker` instances do -in the infinite loop will return an error. In Listing 21-24, we change the -`Worker` loop to gracefully exit the loop in that case, which means the threads -will finish when the `ThreadPool` `drop` implementation calls `join` on them. +حذف کردن `sender` باعث بسته شدن channel می‌شود، +که این موضوع نشان می‌دهد دیگر هیچ پیامی ارسال +نخواهد شد. در این حالت، تمام فراخوانی‌های `recv` +که نمونه‌های `Worker` درون حلقه‌ی بی‌نهایت انجام +می‌دهند با خطا بازمی‌گردند. در فهرست 21-24، +حلقه‌ی `Worker` را طوری تغییر می‌دهیم که +در چنین حالتی به‌صورت مناسب از حلقه خارج شود، +که به این معناست threadها زمانی پایان می‌یابند که +تابع `drop` مربوط به `ThreadPool` تابع `join` را +روی آن‌ها فراخوانی کند. + +<Listing number="21-24" file-name="src/lib.rs" caption="خروج صریح از حلقه زمانی که `recv` با خطا بازمی‌گردد"> -<Listing number="21-24" file-name="src/lib.rs" caption="Explicitly breaking out of the loop when `recv` returns an error"> ```rust,noplayground {{#rustdoc_include ../listings/ch21-web-server/listing-21-24/src/lib.rs:here}} @@ -127,10 +109,11 @@ will finish when the `ThreadPool` `drop` implementation calls `join` on them. </Listing> -To see this code in action, let’s modify `main` to accept only two requests -before gracefully shutting down the server, as shown in Listing 21-25. +برای دیدن این کد در عمل، بیایید `main` را تغییر دهیم تا فقط دو درخواست را قبل از خاموش‌شدن منظم سرور بپذیرد، همان‌طور که در لیستینگ 21-25 نشان داده شده است. + + +<Listing number="21-25" file-name="src/main.rs" caption="خاموش کردن سرور پس از پاسخ‌گویی به دو درخواست از طریق خروج از حلقه"> -<Listing number="21-25" file-name="src/main.rs" caption="Shutting down the server after serving two requests by exiting the loop"> ```rust,ignore {{#rustdoc_include ../listings/ch21-web-server/listing-21-25/src/main.rs:here}} @@ -138,16 +121,11 @@ before gracefully shutting down the server, as shown in Listing 21-25. </Listing> -You wouldn’t want a real-world web server to shut down after serving only two -requests. This code just demonstrates that the graceful shutdown and cleanup is -in working order. +شما نمی‌خواهید یک سرور وب واقعی پس از فقط دو درخواست خاموش شود. این کد فقط نشان می‌دهد که خاموشی منظم و پاک‌سازی به درستی کار می‌کند. -The `take` method is defined in the `Iterator` trait and limits the iteration -to the first two items at most. The `ThreadPool` will go out of scope at the -end of `main`, and the `drop` implementation will run. +متد `take` که در trait `Iterator` تعریف شده است، تکرار را به حداکثر دو آیتم محدود می‌کند. `ThreadPool` در انتهای `main` از محدوده خارج می‌شود و پیاده‌سازی `drop` اجرا خواهد شد. -Start the server with `cargo run`, and make three requests. The third request -should error, and in your terminal you should see output similar to this: +سرور را با دستور `cargo run` راه‌اندازی کنید و سه درخواست ارسال کنید. درخواست سوم باید با خطا مواجه شود و در ترمینال خود باید خروجی مشابه زیر را ببینید: <!-- manual-regeneration cd listings/ch21-web-server/listing-21-25 @@ -178,28 +156,38 @@ Shutting down worker 2 Shutting down worker 3 ``` -You might see a different ordering of `Worker` IDs and messages printed. We can -see how this code works from the messages: `Worker` instances 0 and 3 got the -first two requests. The server stopped accepting connections after the second -connection, and the `Drop` implementation on `ThreadPool` starts executing -before `Worker` 3 even starts its job. Dropping the `sender` disconnects all the -`Worker` instances and tells them to shut down. The `Worker` instances each -print a message when they disconnect, and then the thread pool calls `join` to -wait for each `Worker` thread to finish. - -Notice one interesting aspect of this particular execution: the `ThreadPool` -dropped the `sender`, and before any `Worker` received an error, we tried to -join `Worker` 0. `Worker` 0 had not yet gotten an error from `recv`, so the main -thread blocked, waiting for `Worker` 0 to finish. In the meantime, `Worker` 3 -received a job and then all threads received an error. When `Worker` 0 finished, -the main thread waited for the rest of the `Worker` instances to finish. At that -point, they had all exited their loops and stopped. - -Congrats! We’ve now completed our project; we have a basic web server that uses -a thread pool to respond asynchronously. We’re able to perform a graceful -shutdown of the server, which cleans up all the threads in the pool. - -Here’s the full code for reference: +ممکن است ترتیب نمایش شناسه‌های `Worker` و +پیام‌های چاپ‌شده متفاوت باشد. از طریق این پیام‌ها +می‌توانیم بفهمیم کد چگونه کار می‌کند: نمونه‌های +`Worker` با شناسه‌های 0 و 3 اولین دو درخواست را +دریافت کرده‌اند. سرور پس از اتصال دوم، +پذیرفتن ارتباط‌های جدید را متوقف کرده و پیاده‌سازی +`Drop` برای `ThreadPool` پیش از آن‌که `Worker` 3 +کار خود را آغاز کند اجرا شده است. حذف کردن +`sender` باعث قطع ارتباط تمامی نمونه‌های +`Worker` می‌شود و به آن‌ها اطلاع می‌دهد که +باید خاموش شوند. هر `Worker` هنگام قطع اتصال، +پیامی چاپ می‌کند و سپس thread pool تابع `join` +را فراخوانی می‌کند تا منتظر پایان thread مربوط به +هر `Worker` بماند. + +به نکته‌ای جالب در این اجرای خاص توجه کنید: +`ThreadPool` ابتدا `sender` را حذف کرده و پیش از +آن‌که هیچ‌کدام از `Worker`ها خطایی دریافت کنند، +تلاش کرده‌ایم تا `Worker` 0 را join کنیم. در آن لحظه، +`Worker` 0 هنوز خطایی از `recv` دریافت نکرده +بود، بنابراین thread اصلی منتظر ماند تا `Worker` 0 +به کار خود پایان دهد. در این فاصله، `Worker` 3 +یک job دریافت کرد و سپس همه‌ی threadها +خطا دریافت کردند. وقتی `Worker` 0 به پایان رسید، +thread اصلی منتظر پایان سایر `Worker`ها ماند. +در آن لحظه، همه‌ی آن‌ها از حلقه‌ی خود خارج شده +و متوقف شده بودند. + + +تبریک می‌گویم! پروژه خود را کامل کردید؛ ما یک سرور وب ساده داریم که از یک مجموعه نخ برای پاسخ‌دهی غیرهمزمان استفاده می‌کند. ما توانستیم سرور را به صورت منظم خاموش کنیم و تمام نخ‌ها در مجموعه را پاک‌سازی کنیم. + +در اینجا کد کامل برای مرجع آورده شده است: <Listing file-name="src/main.rs"> @@ -217,21 +205,22 @@ Here’s the full code for reference: </Listing> -We could do more here! If you want to continue enhancing this project, here are -some ideas: +می‌توانستیم بیشتر اینجا انجام دهیم! اگر می‌خواهید این پروژه را بیشتر گسترش دهید، اینجا چند ایده آمده است: + +- مستندات بیشتری به `ThreadPool` و متدهای عمومی آن اضافه کنید. +- تست‌هایی برای عملکرد کتابخانه اضافه کنید. +- فراخوانی‌های `unwrap` را به مدیریت خطای قوی‌تر تغییر دهید. +- از `ThreadPool` برای انجام برخی کارها به غیر از ارائه درخواست‌های وب استفاده کنید. +- یک crate مجموعه نخ از [crates.io](https://crates.io/) پیدا کنید و یک سرور وب مشابه با استفاده از آن crate پیاده‌سازی کنید. سپس API و مقاومت آن را با مجموعه نخی که ما پیاده‌سازی کردیم مقایسه کنید. -- Add more documentation to `ThreadPool` and its public methods. -- Add tests of the library’s functionality. -- Change calls to `unwrap` to more robust error handling. -- Use `ThreadPool` to perform some task other than serving web requests. -- Find a thread pool crate on [crates.io](https://crates.io/) and implement a - similar web server using the crate instead. Then compare its API and - robustness to the thread pool we implemented. +## خلاصه -## Summary +آفرین! شما تا پایان این کتاب پیش آمده‌اید! +از اینکه در این سفر با ما همراه بودید صمیمانه +سپاسگزاریم. اکنون آماده‌اید تا پروژه‌های Rust +خودتان را پیاده‌سازی کنید و در پروژه‌های دیگران +نیز مشارکت داشته باشید. فراموش نکنید که +جامعه‌ای گرم و صمیمی از دیگر Rustaceanها +وجود دارد که با آغوش باز آماده‌اند در مسیر +یادگیری Rust به شما کمک کنند. -Well done! You’ve made it to the end of the book! We want to thank you for -joining us on this tour of Rust. You’re now ready to implement your own Rust -projects and help with other people’s projects. Keep in mind that there is a -welcoming community of other Rustaceans who would love to help you with any -challenges you encounter on your Rust journey. diff --git a/src/foreword.md b/src/foreword.md index f108b65a67..3363086697 100644 --- a/src/foreword.md +++ b/src/foreword.md @@ -1,41 +1,15 @@ -# Foreword +# پیش‌گفتار -It wasn’t always so clear, but the Rust programming language is fundamentally -about _empowerment_: no matter what kind of code you are writing now, Rust -empowers you to reach farther, to program with confidence in a wider variety of -domains than you did before. +همیشه این‌قدر واضح نبود، اما زبان برنامه‌نویسی راست اساساً درباره _توانمندسازی_ است: فرقی نمی‌کند چه نوع کدی اکنون می‌نویسید، راست به شما این قدرت را می‌دهد که فراتر بروید، با اعتمادبه‌نفس در طیف وسیع‌تری از حوزه‌ها برنامه‌نویسی کنید. -Take, for example, “systems-level” work that deals with low-level details of -memory management, data representation, and concurrency. Traditionally, this -realm of programming is seen as arcane, accessible only to a select few who -have devoted the necessary years learning to avoid its infamous pitfalls. And -even those who practice it do so with caution, lest their code be open to -exploits, crashes, or corruption. +به‌عنوان مثال، کارهای “در سطح سیستم” که با جزئیات سطح پایین مدیریت حافظه، نمایش داده‌ها و همروندی سر و کار دارند. به طور سنتی، این حوزه از برنامه‌نویسی پیچیده و فقط برای عده معدودی قابل دسترسی است که سال‌های لازم را برای اجتناب از مشکلات معروف آن صرف کرده‌اند. حتی کسانی که در این زمینه فعالیت می‌کنند نیز با احتیاط عمل می‌کنند تا کد آن‌ها در معرض بهره‌برداری، خرابی یا خرابی داده‌ها قرار نگیرد. -Rust breaks down these barriers by eliminating the old pitfalls and providing a -friendly, polished set of tools to help you along the way. Programmers who need -to “dip down” into lower-level control can do so with Rust, without taking on -the customary risk of crashes or security holes, and without having to learn -the fine points of a fickle toolchain. Better yet, the language is designed to -guide you naturally towards reliable code that is efficient in terms of speed -and memory usage. +راست این موانع را از بین می‌برد و با حذف مشکلات قدیمی و ارائه مجموعه‌ای دوستانه و صیقل‌خورده از ابزارها به شما کمک می‌کند. برنامه‌نویسانی که نیاز دارند به کنترل‌های سطح پایین‌تر “فرو روند”، می‌توانند این کار را با راست انجام دهند، بدون پذیرش خطر معمول خرابی‌ها یا مشکلات امنیتی و بدون نیاز به یادگیری جزئیات ابزارهای پیچیده. بهتر از آن، این زبان طوری طراحی شده است که شما را به‌صورت طبیعی به سمت کدی قابل‌اطمینان و کارآمد از نظر سرعت و استفاده از حافظه هدایت می‌کند. -Programmers who are already working with low-level code can use Rust to raise -their ambitions. For example, introducing parallelism in Rust is a relatively -low-risk operation: the compiler will catch the classical mistakes for you. And -you can tackle more aggressive optimizations in your code with the confidence -that you won’t accidentally introduce crashes or vulnerabilities. +برنامه‌نویسانی که قبلاً با کد سطح پایین کار می‌کنند، می‌توانند با راست جاه‌طلبی‌های خود را افزایش دهند. به‌عنوان مثال، معرفی همروندی در راست عملی نسبتاً کم‌خطر است: کامپایلر اشتباهات کلاسیک را برای شما می‌گیرد. و شما می‌توانید با اطمینان به این که به‌طور تصادفی خرابی‌ها یا آسیب‌پذیری‌ها را معرفی نمی‌کنید، بهینه‌سازی‌های جسورانه‌تری را در کد خود پیاده کنید. -But Rust isn’t limited to low-level systems programming. It’s expressive and -ergonomic enough to make CLI apps, web servers, and many other kinds of code -quite pleasant to write — you’ll find simple examples of both later in the -book. Working with Rust allows you to build skills that transfer from one -domain to another; you can learn Rust by writing a web app, then apply those -same skills to target your Raspberry Pi. +اما راست محدود به برنامه‌نویسی سیستم‌های سطح پایین نیست. این زبان به قدری بیانگر و راحت است که نوشتن برنامه‌های خط فرمان (CLI)، سرورهای وب و بسیاری از انواع دیگر کدها را دلپذیر می‌کند — نمونه‌های ساده‌ای از هر دو را در بخش‌های بعدی کتاب خواهید یافت. کار با راست به شما این امکان را می‌دهد که مهارت‌هایی بسازید که از یک حوزه به حوزه دیگر قابل‌انتقال باشند؛ می‌توانید راست را با نوشتن یک برنامه وب یاد بگیرید و سپس همان مهارت‌ها را برای هدف قرار دادن رزبری پای خود به کار ببرید. -This book fully embraces the potential of Rust to empower its users. It’s a -friendly and approachable text intended to help you level up not just your -knowledge of Rust, but also your reach and confidence as a programmer in -general. So dive in, get ready to learn—and welcome to the Rust community! +این کتاب پتانسیل راست برای توانمندسازی کاربرانش را به طور کامل در آغوش می‌گیرد. این متنی دوستانه و قابل‌دسترس است که قصد دارد نه تنها دانش شما در مورد راست، بلکه دامنه و اعتمادبه‌نفس شما را به‌عنوان یک برنامه‌نویس به طور کلی ارتقا دهد. پس وارد شوید، آماده یادگیری باشید — و به جامعه راست خوش آمدید! -— Nicholas Matsakis and Aaron Turon +— نیکولاس ماتساکیس و آرون تورون diff --git a/src/title-page.md b/src/title-page.md index 872e108204..b2ad85dba5 100644 --- a/src/title-page.md +++ b/src/title-page.md @@ -1,27 +1,19 @@ -# The Rust Programming Language +# زبان برنامه‌نویسی راست -_by Steve Klabnik, Carol Nichols, and Chris Krycho, with contributions from the -Rust Community_ +_نوشته استیو کلابنیک، کارول نیکولز، و کریس کریچو، با مشارکت اعضای جامعه راست_ -This version of the text assumes you’re using Rust 1.85.0 (released 2025-02-17) -or later with `edition = "2024"` in the Cargo.toml file of all projects to -configure them to use Rust 2024 edition idioms. See the [“Installation” section -of Chapter 1][install]<!-- ignore --> to install or update Rust. +این نسخه از متن فرض را بر این می‌گذارد که شما از Rust نسخه 1.85.0 (منتشرشده در تاریخ ۲۰۲۵/۰۲/۱۷) یا جدیدتر استفاده می‌کنید و در فایل Cargo.toml تمامی پروژه‌ها مقدار edition = "2024" را برای پیکربندی به‌کار برده‌اید تا از نگارش ۲۰۲۴ Rust و شیوه‌های مرسوم آن بهره‌مند شوید. برای نصب یا به‌روزرسانی Rust به [بخش «نصب» در فصل ۱][install] مراجعه کنید. -The HTML format is available online at -[https://doc.rust-lang.org/stable/book/](https://doc.rust-lang.org/stable/book/) -and offline with installations of Rust made with `rustup`; run `rustup doc ---book` to open. +فرمت HTML به‌صورت آنلاین در دسترس است در +[https://doc.rust-lang.org/stable/book/](https://doc.rust-lang.org/stable/book/) +و به‌صورت آفلاین با نصب‌های راست که با `rustup` انجام شده‌اند؛ دستور `rustup doc --book` را اجرا کنید تا باز شود. -Several community [translations] are also available. +چندین [ترجمه] جامعه نیز در دسترس است. -This text is available in [paperback and ebook format from No Starch -Press][nsprust]. +این متن در [فرمت کاغذی و الکترونیکی از انتشارات No Starch Press][nsprust] نیز موجود است. [install]: ch01-01-installation.html [nsprust]: https://nostarch.com/rust-programming-language-2nd-edition [translations]: appendix-06-translation.html -> **🚨 Want a more interactive learning experience? Try out a different version -> of the Rust Book, featuring: quizzes, highlighting, visualizations, and -> more**: <https://rust-book.cs.brown.edu> +> **🚨 می‌خواهید تجربه یادگیری تعاملی‌تری داشته باشید؟ نسخه دیگری از کتاب راست را امتحان کنید که شامل: آزمون‌ها، برجسته‌سازی‌ها، تجسم‌ها، و موارد دیگر است**: <https://rust-book.cs.brown.edu> diff --git a/style-guide-EN.md b/style-guide-EN.md new file mode 100644 index 0000000000..a1ad18f908 --- /dev/null +++ b/style-guide-EN.md @@ -0,0 +1,40 @@ +# Style Guide + +## Prose + +<div align="center"> + +[English](style-guide-EN.md) | [فارسی](style-guide.md) + +</div> + +- Prefer title case for chapter/section headings, ex: `## Generating a Secret + Number` rather than `## Generating a secret number`. +- Prefer italics over single quotes when calling out a term, ex: `is an + *associated function* of` rather than `is an ‘associated function’ of`. +- When talking about a method in prose, DO NOT include the parentheses, ex: + `read_line` rather than `read_line()`. +- Hard wrap at 80 chars +- Prefer not mixing code and not-code in one word, ex: ``Remember when we wrote + `use std::io`?`` rather than ``Remember when we `use`d `std::io`?`` + +## Code + +- Add the file name before markdown blocks to make it clear which file we're + talking about, when applicable. +- When making changes to code, make it clear which parts of the code changed + and which stayed the same... not sure how to do this yet +- Split up long lines as appropriate to keep them under 80 chars if possible +- Use `bash` syntax highlighting for command line output code blocks + +## Links + +Once all the scripts are done: + +- If a link shouldn't be printed, mark it to be ignored + - This includes all "Chapter XX" intra-book links, which _should_ be links + for the HTML version +- Make intra-book links and stdlib API doc links relative so they work whether + the book is read offline or on docs.rust-lang.org +- Use markdown links and keep in mind that they will be changed into `text at + *url*` in print, so word them in a way that it reads well in that format diff --git a/style-guide.md b/style-guide.md index 04dc805ca5..4eddaddf96 100644 --- a/style-guide.md +++ b/style-guide.md @@ -1,34 +1,48 @@ -# Style Guide - -## Prose - -- Prefer title case for chapter/section headings, ex: `## Generating a Secret - Number` rather than `## Generating a secret number`. -- Prefer italics over single quotes when calling out a term, ex: `is an - *associated function* of` rather than `is an ‘associated function’ of`. -- When talking about a method in prose, DO NOT include the parentheses, ex: - `read_line` rather than `read_line()`. -- Hard wrap at 80 chars -- Prefer not mixing code and not-code in one word, ex: ``Remember when we wrote - `use std::io`?`` rather than ``Remember when we `use`d `std::io`?`` - -## Code - -- Add the file name before markdown blocks to make it clear which file we're - talking about, when applicable. -- When making changes to code, make it clear which parts of the code changed - and which stayed the same... not sure how to do this yet -- Split up long lines as appropriate to keep them under 80 chars if possible -- Use `bash` syntax highlighting for command line output code blocks - -## Links - -Once all the scripts are done: - -- If a link shouldn't be printed, mark it to be ignored - - This includes all "Chapter XX" intra-book links, which _should_ be links - for the HTML version -- Make intra-book links and stdlib API doc links relative so they work whether - the book is read offline or on docs.rust-lang.org -- Use markdown links and keep in mind that they will be changed into `text at - *url*` in print, so word them in a way that it reads well in that format +<div dir="rtl"> + +# راهنمای سبک + +<div align="center"> + +[English](style-guide-EN.md) | [فارسی](style-guide.md) + +</div> + +## نثر + +<ul dir="rtl"> + <li>برای عنوان‌های فصل/بخش از حروف بزرگ برای کلمات اصلی استفاده کنید، مثال: + `## تولید یک عدد مخفی` به جای `## تولید یک عدد مخفی`.</li> + <li>در زمان اشاره به یک اصطلاح، از ایتالیک به جای نقل قول استفاده کنید، مثال: + `یک *تابع وابسته* است` به جای `یک ‘تابع وابسته’ است`.</li> + <li>وقتی درباره یک متد در متن صحبت می‌کنید، از پرانتزها استفاده نکنید، مثال: + `read_line` به جای `read_line()`.</li> + <li>خطوط را در ۸۰ کاراکتر محدود کنید.</li> + <li>از ترکیب کد و غیرکد در یک کلمه خودداری کنید، مثال: + ``به یاد دارید وقتی نوشتیم `use std::io`؟`` به جای + ``به یاد دارید وقتی `use`d `std::io`؟``.</li> +</ul> + +## کد + +<ul dir="rtl"> + <li>قبل از بلوک‌های markdown نام فایل را اضافه کنید تا مشخص شود درباره کدام فایل صحبت می‌کنیم، در صورت لزوم.</li> + <li>وقتی تغییراتی در کد انجام می‌دهید، مشخص کنید کدام قسمت‌های کد تغییر کرده‌اند و کدام قسمت‌ها ثابت مانده‌اند... هنوز مطمئن نیستم چگونه این را انجام دهیم.</li> + <li>خطوط طولانی را به طور مناسب تقسیم کنید تا در صورت امکان زیر ۸۰ کاراکتر باقی بمانند.</li> + <li>برای بلوک‌های کد خروجی خط فرمان از برجسته‌سازی `bash` استفاده کنید.</li> +</ul> + +## لینک‌ها + +<ul dir="rtl"> + <li>هنگامی که همه اسکریپت‌ها تکمیل شدند: + <ul dir="rtl"> + <li>اگر نباید یک لینک چاپ شود، آن را برای نادیده گرفتن علامت‌گذاری کنید</li> + <li>این شامل همه لینک‌های "فصل XX" درون‌کتاب می‌شود، که _باید_ لینک باشند برای نسخه HTML</li> + </ul> + </li> + <li>لینک‌های درون‌کتاب و لینک‌های مستندات API کتابخانه استاندارد را نسبی کنید تا در حالت آفلاین یا روی docs.rust-lang.org کار کنند.</li> + <li>از لینک‌های markdown استفاده کنید و به یاد داشته باشید که آن‌ها به `متن در *url*` در چاپ تبدیل می‌شوند، بنابراین آن‌ها را طوری بنویسید که در این قالب خوانا باشند.</li> +</ul> + +</div> diff --git a/theme/custom.css b/theme/custom.css new file mode 100644 index 0000000000..2b7233c55a --- /dev/null +++ b/theme/custom.css @@ -0,0 +1,17 @@ +@import url("https://fonts.googleapis.com/css2?family=Vazirmatn&display=swap"); + + +code { + direction: ltr; + text-align: left; +} + +.content { + text-align: justify; + direction: rtl; + font-family: "Vazirmatn", Arial, sans-serif; +} + +.sidebar-scrollbox { + font-family: "Vazirmatn", Arial, sans-serif; +} diff --git a/theme/listing.css b/theme/listing.css index 40ae35a5fb..bbcde4db4c 100644 --- a/theme/listing.css +++ b/theme/listing.css @@ -1,8 +1,12 @@ figure.listing { + direction: ltr; + text-align: left; margin: 0; } .listing figcaption { + direction: ltr; + text-align: left; font-size: 0.8em; font-weight: 600; }