Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/web3-plus/rust-start
We try to learn the Rust programming language together in a simple beginner's way on the weekends.
https://github.com/web3-plus/rust-start
cargo course cpp git github learning memory memory-safety programming programming-language programming-languages rust rust-lang rustlang selfstudy type-safety typesafety zig
Last synced: 11 days ago
JSON representation
We try to learn the Rust programming language together in a simple beginner's way on the weekends.
- Host: GitHub
- URL: https://github.com/web3-plus/rust-start
- Owner: WEB3-PLUS
- Created: 2025-01-16T19:26:38.000Z (26 days ago)
- Default Branch: main
- Last Pushed: 2025-01-31T11:01:24.000Z (11 days ago)
- Last Synced: 2025-01-31T12:18:33.262Z (11 days ago)
- Topics: cargo, course, cpp, git, github, learning, memory, memory-safety, programming, programming-language, programming-languages, rust, rust-lang, rustlang, selfstudy, type-safety, typesafety, zig
- Language: Makefile
- Homepage:
- Size: 13.3 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
### 1. **ایجاد پروژه با Cargo**
برای شروع یک پروژه جدید در Rust، میتوانید از دستور زیر استفاده کنید:
```bash
cargo new projectName
```این دستور یک دایرکتوری جدید به نام `projectName` ایجاد میکند و در آن یک پروژه جدید با ساختار اولیه و فایلهای لازم (از جمله `Cargo.toml` و یک فایل `main.rs`) ایجاد میکند.
### 2. **ساخت پروژه**
برای ساخت پروژه، از دستور زیر استفاده میشود:
```bash
cargo build
```این دستور کد شما را کامپایل میکند و فایلهای اجرایی را در دایرکتوری `target/debug` ذخیره میکند. این دایرکتوری حاوی باینریهایی است که برای توسعه و تست استفاده میشوند.
### 3. **اجرای پروژه**
برای اجرای برنامهی Rust، میتوانید از یکی از دو روش زیر استفاده کنید:
- **اجرای مستقیم باینری:**
```bash
./target/debug/projectName
```این روش به شما امکان میدهد تا باینری ساخته شده را به صورت مستقیم اجرا کنید.
- **استفاده از Cargo:**
```bash
cargo run
```
این دستور به طور خودکار کد شما را کامپایل کرده و سپس آن را اجرا میکند. این روش برای توسعهدهندگان بسیار راحتتر است، زیرا نیازی به یادآوری مسیر باینری نیست.### 4. **بررسی کد بدون تولید باینری**
اگر میخواهید کد خود را بررسی کنید و مطمئن شوید که بدون خطا کامپایل میشود، میتوانید از دستور زیر استفاده کنید:
```bash
cargo check
```این دستور کد شما را سریعاً بررسی میکند، اما باینری تولید نمیکند. این ویژگی برای شناسایی سریع خطاها و مشکلات در کد بسیار مفید است.
### 5. **ساخت پروژه برای انتشار**
زمانی که پروژه شما آمادهی انتشار است و میخواهید آن را با بهینهسازیهای لازم کامپایل کنید، میتوانید از دستور زیر استفاده کنید:
```bash
cargo build --release
```این دستور کد شما را با بهینهسازیهای خاصی کامپایل میکند و باینری تولید شده را در دایرکتوری `target/release` ذخیره میکند. باینریهای تولید شده در این دایرکتوری معمولاً سریعتر و بهینهتر هستند و برای استفاده در محیطهای تولید توصیه میشوند.
### 6. **بنچمارکگیری**
اگر قصد دارید زمان اجرای کد خود را بنچمارک کنید، مهم است که از باینری موجود در `target/release` استفاده کنید. این باینری بهینهسازی شده است و نتایج دقیقتری از عملکرد کد شما ارائه میدهد.
### کد کامل بازی حدس عدد
```rust
use rand::Rng; // برای تولید اعداد تصادفی
use std::cmp::Ordering; // برای مقایسه مقادیر
use std::io; // برای ورودی و خروجیfn main() {
println!("guess the number:");// تولید یک عدد تصادفی بین 1 تا 100
let secret_number = rand::thread_rng().gen_range(1..=100);// چاپ عدد مخفی (برای تست)
println!("the secret number is: {secret_number}");loop {
println!("please input your guess");let mut guess = String::new(); // ایجاد یک رشته خالی برای ورودی کاربر
io::stdin()
.read_line(&mut guess) // خواندن ورودی کاربر
.expect("failed to read line"); // مدیریت خطاprintln!("you guessed: {guess}"); // چاپ حدس کاربر
// تبدیل ورودی کاربر به عدد
let guess: u32 = match guess.trim().parse() {
Ok(num) => num, // اگر تبدیل موفق بود، عدد را ذخیره کن
Err(_) => continue, // اگر خطا بود، حلقه را ادامه بده
};// مقایسه حدس کاربر با عدد مخفی
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"), // اگر حدس کمتر بود
Ordering::Greater => println!("Too big!"), // اگر حدس بیشتر بود
Ordering::Equal => {
println!("You win!"); // اگر حدس برابر بود
return; // پایان بازی
}
}
}
}
```### 1. **وارد کردن کتابخانهها**
```rust
use rand::Rng; // برای تولید اعداد تصادفی
use std::cmp::Ordering; // برای مقایسه مقادیر
use std::io; // برای ورودی و خروجی
```- **`use rand::Rng;`**: این خط ماژول `Rng` از کتابخانه `rand` را وارد میکند که برای تولید اعداد تصادفی استفاده میشود. این ماژول توابعی را برای تولید اعداد تصادفی فراهم میکند.
- **`use std::cmp::Ordering;`**: ماژول `Ordering` شامل سه حالت `Less`, `Greater`, و `Equal` است که برای مقایسه مقادیر استفاده میشود. این مقادیر به ما اجازه میدهند تا بفهمیم یک عدد کمتر، بیشتر یا برابر با عدد دیگر است.
- **`use std::io;`**: ماژول `io` برای کار با ورودی و خروجی در Rust استفاده میشود. ما از آن برای خواندن ورودی کاربر استفاده خواهیم کرد.### 2. **تابع اصلی**
```rust
fn main() {
println!("guess the number:");
```- **`fn main()`**: این تابع نقطه شروع هر برنامه Rust است. هر برنامه Rust باید یک تابع `main` داشته باشد.
- **`println!`**: این ماکرو برای چاپ متن به کنسول استفاده میشود. در اینجا، به کاربر اعلام میشود که باید عددی را حدس بزند.### 3. **تولید عدد تصادفی**
```rust
let secret_number = rand::thread_rng().gen_range(1..=100);
```- **`rand::thread_rng()`**: این تابع یک ژنراتور تصادفی برای ترد فعلی ایجاد میکند. این ژنراتور برای تولید اعداد تصادفی استفاده میشود.
- **`gen_range(1..=100)`**: این تابع یک عدد تصادفی در بازه 1 تا 100 (شامل هر دو) تولید میکند. علامت `..=` نشاندهنده این است که انتهای بازه نیز شامل میشود.### 4. **چاپ عدد مخفی (برای تست)**
```rust
println!("the secret number is: {secret_number}");
```- این خط عدد مخفی را چاپ میکند. این کار برای تست مفید است، اما در یک بازی واقعی باید این خط را حذف کرد تا کاربر نتواند عدد مخفی را ببیند.
### 5. **حلقه اصلی بازی**
```rust
loop {
println!("please input your guess");
```- **`loop { ... }`**: این یک حلقه بینهایت است که به کاربر اجازه میدهد تا حدسهای خود را وارد کند. حلقه تا زمانی که کاربر برنده نشود یا برنامه به صورت دستی متوقف نشود، ادامه خواهد داشت.
### 6. **خواندن ورودی کاربر**
```rust
let mut guess = String::new(); // ایجاد یک رشته خالی برای ورودی کاربر
io::stdin()
.read_line(&mut guess) // خواندن ورودی کاربر
.expect("failed to read line"); // مدیریت خطا
```- **`let mut guess = String::new();`**: یک رشته خالی به نام `guess` ایجاد میشود. `mut` به این معنی است که این متغیر قابل تغییر است.
- **`io::stdin().read_line(&mut guess)`**: این خط ورودی کاربر را از کنسول میخواند و آن را در متغیر `guess` ذخیره میکند. `&mut guess` به این معناست که ما به تابع اجازه میدهیم تا محتوای این متغیر را تغییر دهد.
- **`.expect("failed to read line")`**: این متد در صورت بروز خطا، یک پیام خطا چاپ میکند و برنامه را متوقف میکند. این کار برای اطمینان از این است که ورودی به درستی خوانده شده است.### 7. **چاپ حدس کاربر**
```rust
println!("you guessed: {guess}"); // چاپ حدس کاربر
```- این خط حدس کاربر را چاپ میکند. این کار برای تأیید به کاربر است که ورودی او به درستی خوانده شده است.
### 8. **تبدیل ورودی به عدد**
```rust
let guess: u32 = match guess.trim().parse() {
Ok(num) => num, // اگر تبدیل موفق بود، عدد را ذخیره کن
Err(_) => continue, // اگر خطا بود، حلقه را ادامه بده
};
```- **`guess.trim()`**: این متد فضای خالی را از ابتدا و انتهای رشته حذف میکند. این کار برای جلوگیری از خطا در تبدیل رشته به عدد ضروری است.
- **`.parse()`**: این متد سعی میکند رشته را به نوع عددی تبدیل کند. در اینجا ما از `u32` (عدد صحیح غیر منفی) استفاده میکنیم.
- **`match`**: این ساختار برای بررسی نتیجهی `parse()` استفاده میشود:
- **`Ok(num)`**: اگر تبدیل موفق باشد، عدد در متغیر `num` ذخیره میشود و به متغیر `guess` نسبت داده میشود.
- **`Err(_)`**: اگر تبدیل با خطا مواجه شود (به عنوان مثال، اگر کاربر چیزی غیر از عدد وارد نکند)، حلقه دوباره شروع میشود و از کاربر خواسته میشود که دوباره حدس بزند.### 9. **مقایسه حدس با عدد مخفی**
```rust
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"), // اگر حدس کمتر بود
Ordering::Greater => println!("Too big!"), // اگر حدس بیشتر بود
Ordering::Equal => {
println!("You win!"); // اگر حدس برابر بود
return; // پایان بازی
}
}
```- **`guess.cmp(&secret_number)`**: این متد مقایسهای بین `guess` و `secret_number` انجام میدهد و نتیجه را به یکی از مقادیر `Ordering` (کمتر، بیشتر، برابر) برمیگرداند.
- **`match`**: با توجه به نتیجه مقایسه، یکی از سه حالت زیر اجرا میشود:
- **`Ordering::Less`**: اگر حدس کاربر کمتر از عدد مخفی باشد، پیام "Too small!" چاپ میشود.
- **`Ordering::Greater`**: اگر حدس کاربر بیشتر از عدد مخفی باشد، پیام "Too big!" چاپ میشود.
- **`Ordering::Equal`**: اگر حدس برابر با عدد مخفی باشد، پیام "You win!" چاپ میشود و با استفاده از `return` از تابع `main` خارج میشود، که به معنای پایان بازی است.### 10. **پایان حلقه و برنامه**
- اگر کاربر حدس صحیح را بزند، برنامه خاتمه مییابد. در غیر این صورت، حلقه ادامه مییابد و از کاربر خواسته میشود که دوباره حدس بزند.
### نکات اضافی
- **مدیریت خطا**: در این برنامه، از `expect` برای مدیریت خطاها استفاده شده است. این روش ساده است، اما در برنامههای بزرگتر، ممکن است بخواهید از مدیریت خطای پیچیدهتری استفاده کنید تا به کاربر اطلاعات بیشتری بدهید.
- **توسعه و بهبود**: این بازی میتواند با افزودن ویژگیهای جدید مانند شمارش تعداد حدسها، ارائه گزینههای دوباره بازی، یا ذخیرهسازی رکوردها بهبود یابد.
- **استفاده از ماژولها**: اگر برنامه بزرگتر شود، میتوانید از ماژولها برای سازماندهی کد خود استفاده کنید. Rust به شما اجازه میدهد تا کد خود را به ماژولهای مختلف تقسیم کنید که به مدیریت بهتر پروژه کمک میکند.
variable and constant in rust
### 1. متغیرها و تغییرپذیری
```rust
let mut x: i32 = 5; // متغیر قابل تغییر
println!("x value is : {x}");
x = 6;
println!("x value is : {x}");
```- **متغیرها**: در Rust، متغیرها به طور پیشفرض غیرقابل تغییر (immutable) هستند. این به این معنی است که پس از تعریف، نمیتوانید مقدار آن را تغییر دهید. برای تعریف یک متغیر قابل تغییر، از کلمه کلیدی `mut` استفاده میکنیم.
- **تعریف نوع**: در اینجا، `x` به عنوان یک عدد صحیح 32 بیتی (`i32`) تعریف شده است. Rust از نوع دادههای استاتیک استفاده میکند، به این معنی که نوع هر متغیر در زمان کامپایل مشخص میشود. این ویژگی به بهبود کارایی و ایمنی برنامه کمک میکند.- **چاپ مقادیر**: از ماکرو `println!` برای چاپ مقادیر استفاده میشود. با استفاده از `{x}`، مقدار متغیر `x` در خروجی چاپ میشود.
- **تغییر مقدار**: مقدار `x` از 5 به 6 تغییر میکند و این تغییر در خروجی نشان داده میشود. این قابلیت تغییر مقدار متغیرهای قابل تغییر، یکی از ویژگیهای مهم Rust است.
### 2. ثابتها
```rust
const ONE_DAY_IN_SEC: u64 = 24 * 60 * 60;
println!("one day in second: {ONE_DAY_IN_SEC}");
```- **تعریف ثابت**: با استفاده از کلمه کلیدی `const`، میتوان یک مقدار ثابت تعریف کرد که نمیتواند تغییر کند. ثابتها باید در زمان کامپایل مشخص شوند و در کل برنامه قابل دسترسی هستند.
- **نوع داده**: در اینجا، `ONE_DAY_IN_SEC` به عنوان یک عدد صحیح بدون علامت 64 بیتی (`u64`) تعریف شده است. این ثابت تعداد ثانیههای یک روز را محاسبه میکند (24 ساعت × 60 دقیقه × 60 ثانیه).
- **چاپ مقدار ثابت**: مقدار ثابت با استفاده از `println!` چاپ میشود. این به شما امکان میدهد تا مقادیر ثابت را به راحتی در برنامههای خود استفاده کنید.
### 3. سایهزنی (Shadowing)
```rust
let y = 5;
let y = y + 1;
{
let y = y * 2;
println!("y value is: {y}");
}
println!("y value is : {y}");
```- **سایهزنی**: در Rust، میتوانید یک متغیر با همان نام دوباره تعریف کنید. این به شما اجازه میدهد تا در یک بلوک خاص، متغیر جدیدی با نام مشابه تعریف کنید که بر روی متغیر قبلی سایه میزند.
- **مقداردهی اولیه**: `y` ابتدا به 5 مقداردهی میشود. سپس، در خط بعدی، `y` به 6 تغییر میکند.
- **بلوک داخلی**: در این بلوک، یک `y` جدید تعریف میشود که بر روی `y` قبلی سایه میزند. این `y` جدید مقدار 12 را دارد (6 × 2). فقط در این بلوک، این `y` معتبر است و در خط بعدی، `y` اصلی (6) چاپ میشود. این ویژگی به شما این امکان را میدهد که متغیرها را در محدودههای مختلف مدیریت کنید.
### 4. نوع دادهها
```rust
let age = "18";
let age: u8 = age.parse().expect("type is not casting to string");
println!("age is : {age}");
```- **تعریف رشته**: `age` به عنوان یک رشته (string) تعریف شده است که مقدار `"18"` را دارد. در Rust، رشتهها به صورت پیشفرض از نوع `&str` هستند که یک رشته قابل تغییر نیست.
- **تبدیل نوع**: با استفاده از متد `parse`، رشته به نوع `u8` (عدد صحیح 8 بیتی) تبدیل میشود. این متد به صورت عمومی برای تبدیل رشتهها به انواع مختلف دادهها استفاده میشود. اگر تبدیل موفقیتآمیز نباشد، از `expect` برای مدیریت خطا استفاده میشود. این به شما این امکان را میدهد که در صورت بروز خطا، پیام خطای مناسبی دریافت کنید.
- **چاپ مقدار**: مقدار نهایی `age` که اکنون از نوع `u8` است، چاپ میشود. این روش به شما کمک میکند تا ورودیهای کاربر را به نوع مناسب تبدیل کنید.
### 5. انواع مرکب
```rust
let _tup: (&str, u8, bool) = (&"mohammadreza", 2, false);
let names = ["mohammadreza", "reza"];
let ages: [u8; 2] = [29, 10];
let role = ["test"; 10];
```- **تاپل**:
- `tup` یک تاپل است که شامل سه نوع مختلف داده است: یک رشته (`&str`)، یک عدد صحیح 8 بیتی (`u8`)، و یک مقدار بولی (`bool`).
- تاپلها میتوانند مقادیر مختلفی از انواع مختلف را در خود نگه دارند. تاپلها به شما این امکان را میدهند که چندین مقدار را به عنوان یک واحد مدیریت کنید.- **آرایهها**:
- `names` یک آرایه از رشتهها است که شامل دو نام است. آرایهها در Rust باید دارای اندازه ثابت باشند.
- `ages` یک آرایه از نوع `u8` است که شامل دو سن است.
- `role` یک آرایه است که 10 بار مقدار `"test"` را تکرار میکند. این نوع تعریف آرایه به شما این امکان را میدهد که یک آرایه با اندازه ثابت و مقادیر تکراری ایجاد کنید.### 6. ورودی از کاربر و دسترسی به آرایه
```rust
let a = [1, 2, 3, 4, 5];println!("please enter array index : ");
let mut index = String::new();io::stdin()
.read_line(&mut index)
.expect("failed to read line");
let index: usize = index.trim().parse().expect("index entered is not a number");let element = a[index];
println!("the index value is {element}");
```- **تعریف آرایه**: `a` یک آرایه از اعداد صحیح است که شامل 5 عدد (1 تا 5) است. آرایهها در Rust دارای اندازه ثابت هستند و میتوانند مقادیر مشابه یا متفاوتی را نگه دارند.
- **ورودی از کاربر**:
- با استفاده از `println!` از کاربر خواسته میشود که ایندکس آرایه را وارد کند.
- یک رشته خالی به نام `index` تعریف میشود و با استفاده از `io::stdin().read_line()` ورودی کاربر خوانده میشود. این ورودی به `index` اضافه میشود.
- `expect` برای مدیریت خطا در صورت عدم موفقیت در خواندن ورودی استفاده میشود. این به شما کمک میکند تا در صورت بروز خطا، پیام خطای مناسبی دریافت کنید.- **تبدیل ورودی**: ورودی کاربر که به صورت رشته است، با استفاده از `trim()` و `parse()` به نوع `usize` تبدیل میشود. این نوع برای ایندکسها مناسب است و به شما این امکان را میدهد که به عناصر آرایه دسترسی پیدا کنید.
- **دسترسی به عنصر آرایه**: عنصر مربوط به ایندکس وارد شده از آرایه `a` خوانده میشود. این بخش از کد میتواند منجر به خطا شود اگر کاربر ایندکس نامعتبری وارد کند (مثلاً ایندکسهای خارج از محدوده آرایه).
- **چاپ مقدار**: مقدار عنصر آرایه با استفاده از ایندکس چاپ میشود. این به شما امکان میدهد تا ببینید که کدام عنصر از آرایه با ایندکس وارد شده مطابقت دارد.
### نکات اضافی
- **مدیریت خطا**: استفاده از `expect` به شما کمک میکند تا در صورت بروز خطا، پیام خطای مناسبی دریافت کنید. این روش به شما امکان میدهد تا در زمان توسعه و دیباگ کردن، مشکلات را سریعتر شناسایی کنید.
- **نوع دادههای استاتیک**: Rust به خاطر نوع دادههای استاتیک و ایمنی حافظه مشهور است. این ویژگیها باعث میشوند که بسیاری از خطاهای رایج در زمان کامپایل شناسایی شوند، که این امر به کاهش خطاها در زمان اجرا کمک میکند.
- **قابلیتهای اضافی Rust**: Rust دارای ویژگیهای دیگری نیز هست که میتواند به شما کمک کند، مانند:
- **مدیریت حافظه بدون جمعآوری زباله (Garbage Collection)**: Rust از یک سیستم مالکیت (Ownership) استفاده میکند که به شما این امکان را میدهد که حافظه را به طور ایمن مدیریت کنید بدون اینکه نیاز به جمعآوری زباله داشته باشید.
- **مدیریت همزمانی**: Rust به شما این امکان را میدهد که برنامههای همزمان را به راحتی بنویسید و از خطاهای رایج در برنامههای همزمان جلوگیری کنید.
- **ماکروها**: Rust از ماکروها برای تولید کد به صورت خودکار استفاده میکند که میتواند به کاهش تکرار و سادهسازی کد کمک کند.
functions in rust
### 1. وارد کردن ماژول `io`
```rust
use std::io;
```- **وارد کردن ماژول**: این خط ماژول `io` را از کتابخانه استاندارد Rust وارد میکند. ماژول `io` شامل توابعی برای کار با ورودی و خروجی (I/O) است، مانند خواندن ورودی از کاربر.
### 2. تعریف تابع `sum`
```rust
fn sum(a: i32, b: i32) -> bool {
let sum = a + b;
println!("sum: {sum}");
return true;
}
```- **تعریف تابع**: تابع `sum` با دو پارامتر `a` و `b` از نوع `i32` (عدد صحیح 32 بیتی) تعریف شده است.
- **محاسبه مجموع**: درون تابع، مقدار مجموع `a` و `b` محاسبه شده و در متغیر `sum` ذخیره میشود.- **چاپ مجموع**: با استفاده از ماکرو `println!`، مقدار مجموع چاپ میشود. `{sum}` به مقدار متغیر `sum` اشاره دارد.
- **نوع بازگشتی**: تابع `sum` از نوع `bool` (بولی) است، اما در اینجا همیشه مقدار `true` را باز میگرداند. در واقع، در این کد، مقدار بازگشتی چندان کاربردی ندارد.
### 3. تابع `main`
```rust
fn main() {
println!("Hello, world!");
let mut a = String::new();
let mut b = String::new();
io::stdin().read_line(&mut a).expect("value is not number");
io::stdin().read_line(&mut b).expect("value is not number");
let a: i32 = a.trim().parse().expect("error");
let b: i32 = b.trim().parse().expect("error");
sum(a, b);
}
```- **تعریف تابع `main`**: این تابع نقطه شروع اجرای برنامه است.
- **چاپ پیام خوشامدگویی**: با استفاده از `println!`، پیام `"Hello, world!"` چاپ میشود.
- **تعریف متغیرها**: دو متغیر `a` و `b` به عنوان رشتههای خالی (`String::new()`) تعریف شدهاند. این متغیرها برای ذخیره ورودی کاربر استفاده میشوند.
- **خواندن ورودی از کاربر**:
- با استفاده از `io::stdin().read_line(&mut a)`، برنامه از کاربر میخواهد که یک خط ورودی وارد کند و آن را در متغیر `a` ذخیره میکند.
- `expect("value is not number")` به شما این امکان را میدهد که در صورت بروز خطا در خواندن ورودی، یک پیام خطا چاپ کنید.- **تبدیل ورودی به عدد صحیح**:
- ورودی کاربر که به صورت رشته است، با استفاده از `trim()` (برای حذف فضاهای اضافی) و `parse()` به نوع `i32` تبدیل میشود.
- اگر تبدیل موفقیتآمیز نباشد، `expect("error")` پیام خطا چاپ میکند.- **فراخوانی تابع `sum`**: در نهایت، تابع `sum` با مقادیر `a` و `b` به عنوان آرگومان فراخوانی میشود.
### نکات اضافی
1. **مدیریت خطا**: استفاده از `expect` در این کد به شما کمک میکند تا در صورت بروز خطا، پیام خطای مناسبی دریافت کنید. این روش به شما امکان میدهد تا در زمان توسعه و دیباگ کردن، مشکلات را سریعتر شناسایی کنید.
2. **نوع بازگشتی تابع**: در این کد، تابع `sum` از نوع `bool` است، اما هیچ دلیلی برای این نوع بازگشتی وجود ندارد، زیرا مقدار بازگشتی هیچ کاربردی ندارد. میتوانید نوع بازگشتی را به `()` (واحد) تغییر دهید که نشاندهنده عدم وجود مقدار بازگشتی است.
3. **بهبود کد**: اگر فقط میخواهید مجموع دو عدد را محاسبه کنید، میتوانید از نوع بازگشتی `()` استفاده کنید و مقدار `true` را از تابع حذف کنید. این کار باعث سادهتر شدن کد میشود.
loops and conditions in rust
### 1. بررسی نمره (Grade)
```rust
fn main() {
let grade = 1;
if grade >= 12 && grade <= 20 {
println!("pass")
} else if grade >= 7 {
println!("failed")
} else {
println!("ridi");
}
```- **تعریف متغیر**:
- `let grade = 1;` یک متغیر به نام `grade` تعریف میکند و به آن مقدار 1 اختصاص میدهد. این متغیر نشاندهنده نمره یک دانشآموز است.
- **شرطها**:
- **شرط اول**: `if grade >= 12 && grade <= 20`:
- این شرط بررسی میکند که آیا نمره در بازه 12 تا 20 (شامل) قرار دارد. اگر این شرط برقرار باشد، پیام `"pass"` چاپ میشود.
- عملگر `&&` به معنای "و" است و به این معنی است که هر دو شرط باید درست باشند. در اینجا، نمره باید هم بزرگتر یا مساوی 12 و هم کمتر یا مساوی 20 باشد.- **شرط دوم**: `else if grade >= 7`:
- اگر نمره در بازه 7 تا 11 باشد (و نه در بازه قبلی)، پیام `"failed"` چاپ میشود. این به این معناست که نمره در حد قبولی نیست اما به اندازهای خوب است که به طور کامل مردود نشود.
- **شرط سوم**: `else`:
- اگر هیچیک از شرایط قبلی برقرار نباشد، یعنی نمره کمتر از 7 باشد، پیام `"ridi"` چاپ میشود. این پیام به نظر میرسد یک اصطلاح غیررسمی یا طنزآمیز باشد و ممکن است به معنای "خندهدار" یا "مسخره" باشد.### 2. حلقه `loop`
```rust
//loop
let mut count = 0;
'counter: loop {
if count == 100 {
println!("number: {count}");
break 'counter;
}
if count % 2 == 0 {
println!("number: {count}")
}
count += 1;
}
```- **تعریف متغیر شمارنده**:
- `let mut count = 0;` یک متغیر به نام `count` تعریف میکند و آن را به 0 مقداردهی میکند. این متغیر به عنوان شمارنده در حلقه استفاده میشود.
- **حلقه بیپایان**:
- `'counter: loop { ... }` یک حلقه بیپایان ایجاد میکند. نام `'counter` به این حلقه اختصاص داده شده است تا بتوانیم از آن در دستور `break` استفاده کنیم. این نامگذاری به ما این امکان را میدهد که از چندین حلقه در یک بلوک کد استفاده کنیم و به راحتی از یک حلقه خاص خارج شویم.
- **شرط خروج از حلقه**:
- `if count == 100`:
- اگر مقدار `count` برابر با 100 شود، پیام `"number: {count}"` چاپ میشود و با استفاده از `break 'counter` از حلقه خارج میشویم. در اینجا، `{count}` مقدار شمارنده را در خروجی نمایش میدهد.- **چاپ اعداد زوج**:
- `if count % 2 == 0`:
- اگر `count` عددی زوج باشد (یعنی باقیمانده تقسیم بر 2 برابر با 0 باشد)، مقدار آن چاپ میشود. این به ما امکان میدهد تا تمام اعداد زوج بین 0 و 100 را مشاهده کنیم.- **افزایش شمارنده**:
- `count += 1;`:
- در نهایت، مقدار `count` به اندازه 1 افزایش مییابد. این خط اطمینان میدهد که حلقه به سمت پایان پیش میرود.### 3. حلقه `while`
در کد شما به حلقه `while` اشاره شده، اما در واقع کدی برای `while` وجود ندارد. اما میتوانیم به صورت کلی بگوییم که در Rust میتوانیم از حلقه `while` به شکل زیر استفاده کنیم:
```rust
let mut count = 0;
while count < 100 {
// انجام کارها
count += 1;
}
```حلقه `while` به شما این امکان را میدهد که تا زمانی که یک شرط خاص برقرار باشد، کد را تکرار کنید. در اینجا، حلقه تا زمانی که `count` کمتر از 100 باشد، ادامه مییابد.
### 4. حلقه `for`
```rust
for element in (1..4).rev() {
println!("element: {element}");
}
```- **حلقه `for`**:
- این حلقه بر روی یک رنج (range) از اعداد تکرار میکند. `(1..4)` یک رنج از 1 تا 3 (شامل 1 و غیرشامل 4) را تولید میکند. توجه داشته باشید که در Rust، رنجها شامل عدد ابتدایی و غیرشامل عدد انتهایی هستند.
- **معکوس کردن رنج**:- `rev()` متدی است که رنج را معکوس میکند، بنابراین حلقه از 3 به 1 تکرار میشود. در این حالت، `element` به ترتیب 3، 2 و 1 خواهد بود.
- **چاپ مقادیر**:
- در هر تکرار، مقدار `element` چاپ میشود. این به شما امکان میدهد تا ببینید که چه مقادیری در هر تکرار از حلقه تولید میشوند.### 5. جمعبندی
کد شما شامل چهار بخش اصلی است:
1. **بررسی نمره**: با استفاده از شرطها، نمره یک دانشآموز را بررسی میکند و پیام مناسب را چاپ میکند.
2. **حلقه `loop`**: یک حلقه بیپایان که شمارنده را از 0 تا 100 افزایش میدهد و اعداد زوج را چاپ میکند.
3. **حلقه `while`**: اگرچه در این کد وجود ندارد، اما میتوان از آن برای تکرار بر اساس یک شرط خاص استفاده کرد.
4. **حلقه `for`**: یک حلقه که بر روی یک رنج معکوس از 3 تا 1 تکرار میکند و مقادیر را چاپ میکند.### 6. نکات اضافی
- **مدیریت خطا**: در این کد، هیچ مدیریت خطایی وجود ندارد. در شرایط واقعی، بهتر است برای ورودیها و تبدیلها از مدیریت خطا استفاده کنید تا از بروز مشکلات جلوگیری شود.
- **عملگرهای منطقی**: استفاده از عملگر `&&` برای ترکیب شرایط در شرطها به شما این امکان را میدهد که چندین شرط را به طور همزمان بررسی کنید.- **حلقههای تودرتو**: اگر بخواهید، میتوانید از حلقههای تودرتو (nested loops) نیز استفاده کنید. این کار به شما این امکان را میدهد که ساختارهای پیچیدهتری از تکرار را پیادهسازی کنید.
- **بهینهسازی کد**: میتوانید برخی از بخشهای کد را بهینه کنید، مانند استفاده از توابع برای جدا کردن منطق و بهبود خوانایی کد.
Ownership in rust
Rust به طور خاص به مدیریت ارجحیت بین استفاده از استک و هپ توجه دارد و این کار را از طریق سیستم مالکیت (ownership) و اعتبارسنجی زمان کامپایل (compile-time checks) انجام میدهد. در ادامه، به چگونگی مدیریت این ارجحیتها در Rust میپردازیم:1. سیستم مالکیت (Ownership)
Rust دارای یک سیستم مالکیت قوی است که به شما کمک میکند تا به طور مؤثری از حافظه استفاده کنید. این سیستم شامل سه اصل اصلی است:هر متغیر دارای یک مالک است: هر داده در Rust یک مالک (owner) دارد که مسئول مدیریت آن داده است. وقتی مالک داده از بین میرود (مثلاً وقتی از محدودهای خارج میشود)، داده به طور خودکار آزاد میشود.
تنها یک مالک میتواند داده را در یک زمان داشته باشد: این اصل به جلوگیری از شرایط رقابتی (race conditions) و مشکلات مربوط به دسترسی همزمان به دادهها کمک میکند.
انتقال مالکیت (Ownership Transfer): شما میتوانید مالکیت دادهها را به متغیرهای دیگر منتقل کنید. این کار به شما اجازه میدهد تا از هپ به طور مؤثر استفاده کنید بدون اینکه نیاز به مدیریت دستی حافظه داشته باشید.
2. ارجحیت در استفاده از استک و هپ
Rust به شما این امکان را میدهد که به طور خودکار تصمیم بگیرید که دادهها باید در کجا ذخیره شوند:استفاده از استک: اگر دادهها اندازه ثابتی دارند و نیازی به زندگی طولانیمدت ندارند، Rust به طور خودکار آنها را در استک قرار میدهد. این کار به دلیل سرعت و کارایی بالای استک انجام میشود.
استفاده از هپ: اگر دادهها اندازه نامشخصی دارند یا نیاز به زندگی طولانیمدت دارند (مثلاً آرایههای دینامیک، ساختارهای پیچیده)، Rust به شما این امکان را میدهد که آنها را در هپ قرار دهید. برای این کار، معمولاً از نوعهای اشارهگر مانند Box, Rc, یا Arc استفاده میشود که به شما اجازه میدهند تا به دادههای موجود در هپ دسترسی پیدا کنید و مالکیت را مدیریت کنید.
3. اعتبارسنجی زمان کامپایل
Rust با استفاده از اعتبارسنجی زمان کامپایل، از بروز مشکلات مربوط به مدیریت حافظه جلوگیری میکند. این اعتبارسنجی به شما این امکان را میدهد که قبل از اجرای برنامه، خطاهای مربوط به مالکیت و دسترسی به حافظه را شناسایی کنید. این کار باعث میشود که برنامههای Rust معمولاً ایمنتر و پایدارتر از برنامههای نوشته شده در زبانهای دیگر باشند که مدیریت حافظه را به عهده برنامهنویس میگذارند.stack and heap
### حافظه استک (Stack)
1. **ساختار و مدیریت**:
- استک به صورت یک ساختار دادهای LIFO (آخرین وارد، اولین خارج) عمل میکند. این به این معناست که آخرین مقداری که به استک اضافه میشود، اولین مقداری است که حذف میشود.
- مدیریت حافظه در استک به صورت خودکار انجام میشود. وقتی یک تابع فراخوانی میشود، فضای لازم برای متغیرهای محلی و پارامترها به طور خودکار در بالای استک قرار میگیرد و وقتی تابع به پایان میرسد، این فضا به طور خودکار آزاد میشود.2. **سرعت**:
- افزودن و حذف دادهها از استک بسیار سریع است، زیرا مکان دادهها همیشه در بالای استک قرار دارد و نیازی به جستجو ندارد.
- به همین دلیل، استفاده از استک برای دادههایی که اندازه آنها در زمان کامپایل مشخص است (مانند اعداد صحیح، آرایههای با اندازه ثابت و ...) بسیار مناسب است.3. **محدودیتها**:
- تمامی دادههایی که در استک قرار میگیرند باید اندازه ثابتی داشته باشند. این بدان معناست که نمیتوانید دادههایی با اندازه نامشخص یا متغیر را در استک ذخیره کنید.
- استک دارای محدودیت اندازه است و اگر بیش از حد از آن استفاده کنید، ممکن است با خطای "Stack Overflow" مواجه شوید.### حافظه هپ (Heap)
1. **ساختار و مدیریت**:
- هپ به صورت یک فضای بزرگ از حافظه عمل میکند که میتوانید با درخواستهای خاص، فضاهای مورد نیاز را از آن بگیرید.
- مدیریت حافظه در هپ نیاز به تخصیص و آزادسازی دستی دارد. شما باید خودتان مشخص کنید که چه زمانی فضا را اختصاص داده و چه زمانی آن را آزاد کنید. این کار معمولاً با استفاده از اشارهگرها انجام میشود.2. **سرعت**:
- تخصیص فضا در هپ معمولاً کندتر از استک است، زیرا سیستم باید یک مکان خالی مناسب پیدا کند و مدیریت کند.
- دسترسی به دادهها در هپ نیز معمولاً کندتر است، زیرا باید به اشارهگر مراجعه کنید.3. **مزایا**:
- هپ به شما این امکان را میدهد که دادههایی با اندازه نامشخص یا متغیر را ذخیره کنید، مانند آرایههای دینامیک یا ساختارهای پیچیده.
- همچنین میتوانید از هپ برای ذخیرهسازی دادههایی که باید در طول عمر بیشتری از برنامه باقی بمانند استفاده کنید.### ارجحیت بین استک و هپ
- **استفاده از استک**: به طور کلی، اگر دادهای دارید که اندازه آن در زمان کامپایل مشخص است و نیازی به زندگی طولانیمدت ندارد، بهتر است از استک استفاده کنید. این کار به دلیل سرعت و مدیریت خودکار حافظه، بهینهتر است.
- **استفاده از هپ**: اگر دادهای دارید که اندازه آن نامشخص است یا نیاز به زندگی طولانیمدت دارد (مانند دادههایی که باید پس از خروج از یک تابع همچنان در دسترس باشند)، باید از هپ استفاده کنید.### مفهوم مالکیت (Ownership)
مالکیت یکی از اصول کلیدی زبان Rust است که به مدیریت حافظه کمک میکند. این مفهوم به شما اجازه میدهد تا بدون نیاز به جمعآوری زباله (Garbage Collection) یا مدیریت دستی حافظه، به طور ایمن و کارآمد از حافظه استفاده کنید. در ادامه، به تفصیل به قوانین مالکیت، محدوده متغیرها، نوع دادهها، و تعاملات آنها میپردازیم.
### 1. قوانین مالکیت
مالکیت در Rust شامل سه قانون اصلی است:
- **هر مقدار در Rust یک مالک دارد**: هر دادهای که در Rust ایجاد میشود، یک مالک (owner) دارد که مسئول مدیریت آن داده است. این مالک معمولاً یک متغیر است.
- **در هر زمان فقط یک مالک وجود دارد**: این بدان معناست که نمیتوانید دو متغیر به طور همزمان مالک یک داده باشند. این ویژگی به جلوگیری از شرایط رقابتی (race conditions) و مشکلات مربوط به دسترسی همزمان به دادهها کمک میکند.
- **وقتی مالک از محدوده خارج میشود، مقدار رها میشود**: هنگامی که متغیر مالک از محدوده (scope) خارج میشود، حافظه مربوط به آن به طور خودکار آزاد میشود. این کار توسط تابع خاصی به نام `drop` انجام میشود.
### 2. محدوده متغیرها (Variable Scope)
محدوده به محدودهای در برنامه اشاره دارد که یک متغیر معتبر است. متغیرها از زمانی که تعریف میشوند تا زمانی که از محدوده خارج شوند، معتبر هستند. به عنوان مثال:
```rust
{
let s = "hello"; // s از این نقطه معتبر است
// do something with s
} // s دیگر معتبر نیست
```### 3. نوع داده String
برای درک بهتر قوانین مالکیت، نوع دادهای به نام **String** معرفی میشود. این نوع داده به شما این امکان را میدهد که متونی با اندازه متغیر را مدیریت کنید، زیرا دادهها در هپ ذخیره میشوند.
#### ایجاد یک String
شما میتوانید یک String از یک رشته ثابت (string literal) با استفاده از تابع `String::from` ایجاد کنید:
```rust
let s = String::from("hello");
```#### تغییر یک String
String به شما این امکان را میدهد که محتوای آن را تغییر دهید:
```rust
let mut s = String::from("hello");
s.push_str(", world!"); // محتوای s به "hello, world!" تغییر میکند
println!("{s}"); // خروجی: hello, world!
```### 4. حافظه و تخصیص
- **رشتههای ثابت** (string literals): مقادیر آنها در زمان کامپایل مشخص است و به طور مستقیم در باینری نهایی قرار میگیرد. این رشتهها غیرقابل تغییر (immutable) هستند و در استک ذخیره میشوند.
- **نوع داده String**: برای ذخیرهسازی متون متغیر، حافظهای در هپ تخصیص داده میشود. این حافظه در زمان اجرا درخواست میشود و وقتی متغیر مالک از محدوده خارج میشود، حافظه به طور خودکار آزاد میشود.
### 5. تعامل متغیرها و دادهها با مفهوم Move
در Rust، وقتی یک متغیر به متغیر دیگری اختصاص داده میشود، مالکیت داده منتقل میشود. به عنوان مثال:
```rust
let s1 = String::from("hello");
let s2 = s1; // مالکیت s1 به s2 منتقل میشود
```در اینجا، `s1` دیگر معتبر نیست و اگر بخواهید از آن استفاده کنید، خطای کامپایل دریافت خواهید کرد. این به دلیل جلوگیری از مشکلات مربوط به آزادسازی دو بار حافظه (double free) است.
### 6. استفاده از Clone
اگر بخواهید یک کپی عمیق (deep copy) از دادهها داشته باشید، میتوانید از متد `clone` استفاده کنید:
```rust
let s1 = String::from("hello");
let s2 = s1.clone(); // یک کپی عمیق از s1 ایجاد میکند
```### 7. دادههای فقط استک و Copy Trait
برخی از نوعها، مانند اعداد صحیح، به دلیل اینکه اندازه آنها در زمان کامپایل مشخص است، به طور کامل در استک ذخیره میشوند و میتوانند به راحتی کپی شوند. این نوعها **Copy trait** را پیادهسازی میکنند و بنابراین میتوانند بدون انتقال مالکیت کپی شوند.
#### مثال از Copy Trait
```rust
let x = 5;
let y = x; // x به y کپی میشود و x همچنان معتبر است
println!("x = {x}, y = {y}"); // خروجی: x = 5, y = 5
```### 8. مالکیت و توابع
وقتی یک متغیر به یک تابع منتقل میشود، مالکیت آن متغیر منتقل میشود. این به این معناست که اگر بخواهید از متغیری که به تابع منتقل شده استفاده کنید، دیگر نمیتوانید.
#### مثال از توابع
```rust
fn main() {
let s = String::from("hello"); // s به محدوده میآید
takes_ownership(s); // s به تابع منتقل میشود و دیگر معتبر نیست
// println!("{s}"); // این خط خطا میدهدlet x = 5; // x به محدوده میآید
makes_copy(x); // x به تابع منتقل میشود، اما چون i32 نوعی Copy است، x هنوز معتبر است
println!("{x}"); // خروجی: 5
}fn takes_ownership(some_string: String) {
println!("{some_string}");
} // some_string از محدوده خارج میشود و حافظه آزاد میشودfn makes_copy(some_integer: i32) {
println!("{some_integer}");
} // some_integer از محدوده خارج میشود، اما هیچ کاری انجام نمیدهد
```### 9. بازگشت مقادیر و مالکیت
وقتی یک تابع مقداری را برمیگرداند، مالکیت آن مقدار نیز منتقل میشود. این روند ممکن است کمی خستهکننده باشد، زیرا باید اطمینان حاصل کنید که دادهها به درستی مدیریت میشوند.
#### مثال از بازگشت مقادیر
```rust
fn main() {
let s1 = gives_ownership(); // gives_ownership مقدار خود را به s1 منتقل میکند
let s2 = String::from("hello"); // s2 به محدوده میآید
let s3 = takes_and_gives_back(s2); // s2 به تابع منتقل میشود و مقدار بازگشتی به s3 منتقل میشود
}fn gives_ownership() -> String {
let some_string = String::from("yours");
some_string // some_string به تابع فراخوانی منتقل میشود
}fn takes_and_gives_back(a_string: String) -> String {
a_string // a_string به تابع فراخوانی منتقل میشود
}
```### 10. مراجع (References)
Rust به شما این امکان را میدهد که بدون انتقال مالکیت از یک مقدار استفاده کنید. این کار با استفاده از مراجع (references) انجام میشود. مراجع به شما این امکان را میدهند که به دادهها دسترسی پیدا کنید بدون اینکه مالکیت آنها را تغییر دهید.
#### مثال از مراجع
```rust
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // با استفاده از مرجع به s1 دسترسی پیدا میکنیم
println!("The length of '{}' is {}.", s1, len); // s1 هنوز معتبر است
}fn calculate_length(s: &String) -> usize {
s.len() // طول رشته را برمیگرداند
}
```### 11. مراجع قابل تغییر (Mutable References)
اگر بخواهید به دادهای که در یک مرجع ذخیره شده است، تغییراتی اعمال کنید، باید از مراجع قابل تغییر (mutable references) استفاده کنید. در Rust، تنها میتوانید یک مرجع قابل تغییر در هر زمان داشته باشید تا از بروز شرایط رقابتی جلوگیری شود.
#### مثال از مراجع قابل تغییر
```rust
fn main() {
let mut s = String::from("hello");
change(&mut s); // مرجع قابل تغییر به تابع ارسال میشود
println!("{}", s); // خروجی: hi
}fn change(s: &mut String) {
s.push_str(", world!"); // محتوای s تغییر میکند
}
```### 12. قوانین مراجع
Rust برای مراجع قوانین خاصی دارد:
1. **هر مرجع باید به یک مقدار معتبر اشاره کند**: اگر مرجع به مقداری اشاره کند که دیگر معتبر نیست، خطای کامپایل ایجاد میشود.
2. **تنها میتوانید یک مرجع قابل تغییر در هر زمان داشته باشید**: این کار به جلوگیری از شرایط رقابتی کمک میکند.
3. **میتوانید چندین مرجع غیرقابل تغییر در یک زمان داشته باشید**: این به شما این امکان را میدهد که به دادهها دسترسی داشته باشید بدون اینکه مالکیت آنها را تغییر دهید.### 14. مزایای مالکیت در Rust
- **ایمنی حافظه**: با استفاده از مالکیت، Rust از بروز مشکلاتی مانند آزادسازی دو بار حافظه (double free) و استفاده از حافظه آزاد شده (dangling pointers) جلوگیری میکند.
- **کارایی**: سیستم مالکیت به Rust این امکان را میدهد که بدون نیاز به جمعآوری زباله، حافظه را به طور مؤثری مدیریت کند. این باعث میشود که برنامههای Rust سریعتر و کارآمدتر باشند.
- **شفافیت**: قوانین مالکیت و مراجع به وضوح مشخص میکنند که چه کسی مالک یک داده است و چه زمانی آن داده آزاد میشود، که به کد شما شفافیت بیشتری
### کد
```rust
use std::string;fn main() {
//ownership in rust
//variable scope
let s = "hello";
{
println!("s: {s}");
let s = "h";
println!("s: {s}");
} //drop new s => "h"
println!("s: {s}");//muteable string
let mut str = String::from("mohammadreza "); //idont know this
str.push_str("hooshmand");
println!("str name: {str}");//variables and data intracting with move
let x = 5;
let y = x; //copy x value to y
let s1 = String::from("test"); //{ptr:HEAP,len:4Byte,capacity:4memory}
let s2 = s1; //move s1 value to s2,1 is not shollow copy is move
// for copy value we can use s1.clone()
take_ownership(s2);
//we can use return for takes ownership
}fn take_ownership(s2: String) -> String {
return s2;
}
```### توضیحات
#### 1. **وارد کردن ماژول**
```rust
use std::string;
```- این خط ماژول `string` را از کتابخانه استاندارد Rust وارد میکند. با این حال، در این کد خاص، نیازی به وارد کردن `std::string` نیست، زیرا از نوع `String` به طور مستقیم استفاده نمیشود.
#### 2. **تابع اصلی**
```rust
fn main() {
```- این خط شروع تابع اصلی برنامه است. در Rust، اجرای برنامه از تابع `main` آغاز میشود.
### 3. **مالکیت و محدوده متغیرها**
```rust
let s = "hello";
{
println!("s: {s}");
let s = "h";
println!("s: {s}");
} //drop new s => "h"
println!("s: {s}");
```- **تعریف متغیر `s`**: متغیر `s` به رشته ثابت `"hello"` اشاره میکند. این رشته در استک ذخیره میشود. در Rust، رشتههای ثابت (string literals) به طور خودکار در حافظهای که به استک تعلق دارد، ذخیره میشوند و به دلیل اینکه اندازه آنها در زمان کامپایل مشخص است، سریع و کارآمد هستند.
- **محدوده داخلی**: درون `{}` یک محدوده جدید تعریف شده است. این محدوده به Rust میگوید که متغیرها و دادههای جدیدی که در اینجا تعریف میشوند، فقط در این بخش معتبر هستند.- `println!("s: {s}");`: این خط مقدار `s` را در این محدوده چاپ میکند، که `"hello"` است.
- `let s = "h";`: در اینجا، یک متغیر جدید به نام `s` تعریف میشود که در این محدوده معتبر است و مقدار آن `"h"` است. این متغیر جدید، متغیر قبلی را در این محدوده پنهان میکند. به عبارت دیگر، این یک **پنهانسازی** (shadowing) است.
- `println!("s: {s}");`: این خط مقدار جدید `s` را چاپ میکند، که `"h"` است.
- **خروج از محدوده**: وقتی از محدوده خارج میشویم، متغیر جدید `s` رها میشود و متغیر قبلی (`"hello"`) دوباره معتبر میشود. در اینجا، Rust به طور خودکار حافظه مربوط به متغیر جدید را آزاد میکند.
- `println!("s: {s}");`: این خط مقدار اصلی `s` را چاپ میکند، که `"hello"` است.### 4. **رشته قابل تغییر**
```rust
let mut str = String::from("mohammadreza "); //idont know this
str.push_str("hooshmand");
println!("str name: {str}");
```- **تعریف رشته قابل تغییر**: متغیر `str` به یک رشته قابل تغییر (`String`) با مقدار اولیه `"mohammadreza "` اختصاص داده میشود. از `String::from` برای ایجاد این نوع داده استفاده میشود. نوع `String` در هپ ذخیره میشود و به شما این امکان را میدهد که محتوای آن را تغییر دهید.
- **اضافه کردن به رشته**: با استفاده از متد `push_str`، رشته `"hooshmand"` به انتهای `str` اضافه میشود. حالا مقدار `str` به `"mohammadreza hooshmand"` تغییر میکند. این کار به دلیل این است که نوع `String` میتواند مقادیر متغیر را مدیریت کند.
- **چاپ مقدار رشته**: این خط مقدار جدید `str` را چاپ میکند. خروجی این بخش `"str name: mohammadreza hooshmand"` خواهد بود.### 5. **تعامل متغیرها و دادهها با مفهوم Move**
```rust
let x = 5;
let y = x; //copy x value to y
let s1 = String::from("test"); //{ptr:HEAP,len:4Byte,capacity:4memory}
let s2 = s1; //move s1 value to s2,1 is not shollow copy is move
// for copy value we can use s1.clone()
take_ownership(s2);
```- **تعریف عدد صحیح**: `let x = 5;` یک عدد صحیح به نام `x` تعریف میکند. از آنجا که `i32` (نوع عدد صحیح) یک نوع `Copy` است، میتوانیم مقدار آن را به متغیر دیگری بدون از بین رفتن اعتبار `x` کپی کنیم.
- **کپی عدد صحیح**: `let y = x;` در اینجا، مقدار `x` به `y` کپی میشود. از آنجا که `i32` یک نوع `Copy` است، `x` و `y` هر دو معتبر هستند و میتوانند به طور مستقل استفاده شوند.
- **تعریف یک رشته**: `let s1 = String::from("test");` یک رشته قابل تغییر با مقدار `"test"` ایجاد میکند. این رشته در هپ ذخیره میشود و شامل یک اشارهگر به دادهها، طول و ظرفیت است. این نوع داده به شما این امکان را میدهد که محتوای آن را تغییر دهید و اندازه آن را در زمان اجرا تغییر دهید.
- **انتقال مالکیت**: `let s2 = s1;` در اینجا، مالکیت `s1` به `s2` منتقل میشود. از آنجا که `String` یک نوع `Copy` نیست، `s1` دیگر معتبر نیست و نمیتوانید از آن استفاده کنید. این عمل به عنوان **move** شناخته میشود. به عبارت دیگر، `s1` دیگر به دادههای هپ اشاره نمیکند و `s2` اکنون مالک آن دادهها است.
- **فراخوانی تابع**: `take_ownership(s2);` تابع `take_ownership` با `s2` به عنوان آرگومان فراخوانی میشود. در اینجا، `s2` مالکیت داده را به تابع منتقل میکند.### 6. **تابع `take_ownership`**
```rust
fn take_ownership(s2: String) -> String {
return s2;
}
```- **تعریف تابع**: تابع `take_ownership` یک آرگومان از نوع `String` میگیرد و آن را برمیگرداند. این تابع در واقع مالکیت `s2` را میگیرد و سپس آن را برمیگرداند.
- **بازگشت مقدار**: مقدار `s2` به تابعی که آن را فراخوانی کرده است، بازگردانده میشود. در این کد، این مقدار به هیچ متغیر دیگری اختصاص داده نمیشود و در واقع هیچ استفادهای از آن نمیشود. با این حال، این نشان میدهد که چگونه میتوان مالکیت را به تابعی دیگر منتقل کرد و سپس آن را برگرداند.### نکات کلیدی
1. **مالکیت و انتقال**: در Rust، انتقال مالکیت یک مفهوم کلیدی است. وقتی یک متغیر به متغیر دیگری اختصاص داده میشود، مالکیت داده به متغیر جدید منتقل میشود. این به Rust کمک میکند تا از بروز مشکلاتی مانند آزادسازی دو بار حافظه جلوگیری کند.
2. **محدوده متغیرها**: محدوده متغیرها در Rust بسیار مهم است. متغیرها فقط در محدودهای که تعریف شدهاند معتبر هستند و وقتی از آن محدوده خارج میشوند، حافظه مربوط به آنها به طور خودکار آزاد میشود.
3. **رشتههای ثابت و قابل تغییر**: رشتههای ثابت (string literals) در استک ذخیره میشوند و غیرقابل تغییر هستند، در حالی که رشتههای قابل تغییر (String) در هپ ذخیره میشوند و میتوانند تغییر کنند.
4. **کپی و Move**: انواعی که اندازه آنها در زمان کامپایل مشخص است (مانند اعداد صحیح) به طور خودکار کپی میشوند، در حالی که انواعی مانند `String` که اندازه آنها در زمان کامپایل مشخص نیست، با انتقال مالکیت (move) مدیریت میشوند.
5. **استفاده از clone**: اگر بخواهید یک کپی عمیق از دادهها داشته باشید، میتوانید از متد `clone` استفاده کنید. این کار دادهها را در هپ کپی میکند و به شما این امکان را میدهد که از هر دو متغیر به طور مستقل استفاده کنید.