Rust - Hướng dẫn nhanh

Rust là một ngôn ngữ lập trình cấp hệ thống, được phát triển bởi Graydon Hoare. Mozilla Labs sau đó đã mua lại chương trình.

Ứng dụng v / s Ngôn ngữ lập trình hệ thống

Các ngôn ngữ lập trình ứng dụng như Java / C # được sử dụng để xây dựng phần mềm, cung cấp dịch vụ trực tiếp cho người dùng. Chúng giúp chúng tôi xây dựng các ứng dụng kinh doanh như bảng tính, trình xử lý văn bản, ứng dụng web hoặc ứng dụng di động.

Các ngôn ngữ lập trình hệ thống như C / C ++ được sử dụng để xây dựng phần mềm và nền tảng phần mềm. Chúng có thể được sử dụng để xây dựng hệ điều hành, công cụ trò chơi, trình biên dịch, v.v. Những ngôn ngữ lập trình này yêu cầu mức độ tương tác phần cứng lớn.

Hệ thống và ngôn ngữ lập trình ứng dụng phải đối mặt với hai vấn đề lớn:

  • Rất khó để viết mã an toàn.
  • Rất khó để viết mã đa luồng.

Tại sao gỉ?

Rust tập trung vào ba mục tiêu -

  • Safety
  • Speed
  • Concurrency

Ngôn ngữ này được thiết kế để phát triển phần mềm có độ tin cậy cao và nhanh chóng theo cách đơn giản. Rust có thể được sử dụng để viết các chương trình cấp cao xuống các chương trình dành riêng cho phần cứng.

Hiệu suất

Ngôn ngữ lập trình Rust không có Bộ thu gom rác (GC) theo thiết kế. Điều này cải thiện hiệu suất trong thời gian chạy.

An toàn bộ nhớ tại thời điểm biên dịch

Phần mềm được xây dựng bằng Rust an toàn trước các vấn đề về bộ nhớ như con trỏ treo, ghi đè bộ đệm và rò rỉ bộ nhớ.

Các ứng dụng đa luồng

Quyền sở hữu và các quy tắc an toàn bộ nhớ của Rust cung cấp sự đồng thời mà không có cuộc đua dữ liệu.

Hỗ trợ lắp ráp web (WASM)

Web Assembly giúp thực thi các thuật toán tính toán chuyên sâu trong trình duyệt, trên các thiết bị nhúng hoặc bất kỳ nơi nào khác. Nó chạy với tốc độ của mã gốc. Rust có thể được biên dịch thành Web Assembly để thực thi nhanh chóng và đáng tin cậy.

Cài đặt Rust dễ dàng thông qua rustup, một công cụ dựa trên bảng điều khiển để quản lý các phiên bản Rust và các công cụ liên quan.

Cài đặt trên Windows

Hãy để chúng tôi tìm hiểu cách cài đặt RUST trên Windows.

  • Cài đặt Visual Studio 2013 trở lên với các công cụ C ++ là bắt buộc để chạy chương trình Rust trên windows. Đầu tiên, tải xuống Visual Studio từ đây VS 2013 Express

  • Tải xuống và cài đặt rustup công cụ cho cửa sổ. rustup-init.execó sẵn để tải xuống tại đây - Rust Lang

  • Nhấn đúp chuột rustup-init.exetập tin. Khi nhấp vào, màn hình sau sẽ xuất hiện.

  • Nhấn enter để cài đặt mặc định. Sau khi cài đặt xong, màn hình sau sẽ xuất hiện.

  • Từ màn hình cài đặt, rõ ràng là các tệp liên quan đến Rust được lưu trữ trong thư mục -

    C: \ Users \ {PC} \. Row \ bin

Nội dung của thư mục là -

cargo-fmt.exe
cargo.exe
rls.exe
rust-gdb.exe
rust-lldb.exe
rustc.exe // this is the compiler for rust
rustdoc.exe
rustfmt.exe
rustup.exe
  • Cargolà trình quản lý gói cho Rust. Để xác minh nếucargo được cài đặt, thực hiện lệnh sau:

C:\Users\Admin>cargo -V
cargo 1.29.0 (524a578d7 2018-08-05)
  • Trình biên dịch cho Rust là rustc. Để xác minh phiên bản trình biên dịch, hãy thực hiện lệnh sau:

C:\Users\Admin>cargo -V
cargo 1.29.0 (524a578d7 2018-08-05)

Cài đặt trên Linux / Mac

Để cài đặt rustup trên Linux hoặc macOS, mở một thiết bị đầu cuối và nhập lệnh sau.

$ curl https://sh.rustup.rs -sSf | sh

Lệnh tải xuống một tập lệnh và bắt đầu cài đặt rustupcông cụ cài đặt phiên bản ổn định mới nhất của Rust. Bạn có thể được nhắc nhập mật khẩu của mình. Nếu cài đặt thành công, dòng sau sẽ xuất hiện:

Rust is installed now. Great!

Tập lệnh cài đặt tự động thêm Rust vào PATH hệ thống của bạn sau lần đăng nhập tiếp theo của bạn. Để bắt đầu sử dụng Rust ngay lập tức thay vì khởi động lại thiết bị đầu cuối của bạn, hãy chạy lệnh sau trong trình bao của bạn để thêm Rust vào PATH hệ thống của bạn theo cách thủ công -

$ source $HOME/.cargo/env

Ngoài ra, bạn có thể thêm dòng sau vào ~ / .bash_profile -

$ export PATH="$HOME/.cargo/bin:$PATH"

NOTE - Khi bạn cố gắng biên dịch một chương trình Rust và nhận được lỗi cho biết rằng một trình liên kết không thể thực thi, điều đó có nghĩa là một trình liên kết chưa được cài đặt trên hệ thống của bạn và bạn sẽ cần phải cài đặt một trình liên kết theo cách thủ công.

Sử dụng nền tảng mã hóa điểm hướng dẫn cho RUST

Vòng lặp Đọc-Đánh giá-In (REPL) là một trình bao tương tác dễ sử dụng để biên dịch và thực thi các chương trình máy tính. Nếu bạn muốn biên dịch và thực thi các chương trình Rust trực tuyến trong trình duyệt, hãy sử dụng Tutorialspoint Coding Ground .

Chương này giải thích cú pháp cơ bản của ngôn ngữ Rust thông qua một HelloWorld thí dụ.

  • Tạo một HelloWorld-App thư mục và điều hướng đến thư mục đó trên thiết bị đầu cuối

C:\Users\Admin>mkdir HelloWorld-App
C:\Users\Admin>cd HelloWorld-App
C:\Users\Admin\HelloWorld-App>
  • Để tạo tệp Rust, hãy thực hiện lệnh sau:

C:\Users\Admin\HelloWorld-App>notepad Hello.rs

Các tệp chương trình gỉ có phần mở rộng .rs. Lệnh trên tạo một tệp trốngHello.rsvà mở nó trong NOTEpad. Thêm mã được cung cấp bên dưới vào tệp này -

fn
main(){
   println!("Rust says Hello to TutorialsPoint !!");
}

Chương trình trên định nghĩa một hàm main fn main () . Các fn từ khóa được sử dụng để xác định một hàm. Hàm main () là một hàm được xác định trước hoạt động như một điểm vào chương trình. println! là một macro được xác định trước trong Rust. Nó được sử dụng để in một chuỗi (ở đây là Xin chào) vào bảng điều khiển. Các lệnh gọi macro luôn được đánh dấu bằng dấu chấm than - ! .

  • Biên dịch Hello.rs sử dụng tập tin rustc.

C:\Users\Admin\HelloWorld-App>rustc Hello.rs

Sau khi biên dịch thành công chương trình, một tệp thực thi ( file_name.exe ) sẽ được tạo. Để xác minh xem tệp .exe có được tạo hay không, hãy thực hiện lệnh sau.

C:\Users\Admin\HelloWorld-App>dir
//lists the files in folder
Hello.exe
Hello.pdb
Hello.rs
  • Thực thi tệp Hello.exe và xác minh đầu ra.

Macro là gì?

Rust cung cấp một hệ thống macro mạnh mẽ cho phép lập trình meta. Như bạn đã thấy trong ví dụ trước, macro trông giống như các hàm, ngoại trừ tên của chúng kết thúc bằng dấu nổ (!), Nhưng thay vì tạo ra một lệnh gọi hàm, các macro được mở rộng thành mã nguồn được biên dịch với phần còn lại của chương trình. Do đó, chúng cung cấp nhiều tính năng thời gian chạy hơn cho một chương trình không giống như các hàm. Macro là một phiên bản mở rộng của các hàm.

Sử dụng println! Macro - Cú pháp

println!(); // prints just a newline
println!("hello ");//prints hello
println!("format {} arguments", "some"); //prints format some arguments

Nhận xét bằng Rust

Nhận xét là một cách để cải thiện khả năng đọc của chương trình. Nhận xét có thể được sử dụng để bao gồm thông tin bổ sung về một chương trình như tác giả của mã, gợi ý về một hàm / cấu trúc, v.v. Trình biên dịch bỏ qua nhận xét.

Rust hỗ trợ các loại nhận xét sau:

  • Nhận xét một dòng (//) - Bất kỳ văn bản nào giữa // và cuối dòng đều được coi là nhận xét

  • Nhận xét nhiều dòng (/ * * /) - Những nhận xét này có thể kéo dài nhiều dòng.

Thí dụ

//this is single line comment

/* This is a
   Multi-line comment
*/

Thực thi trực tuyến

Các chương trình gỉ có thể được thực thi trực tuyến thông qua Tutorialspoint Coding Ground . Viết chương trình HelloWorld trong tab Editor và nhấp vào Execute để xem kết quả.

Hệ thống Loại biểu thị các loại giá trị khác nhau được ngôn ngữ hỗ trợ. Hệ thống Loại kiểm tra tính hợp lệ của các giá trị được cung cấp trước khi chúng được chương trình lưu trữ hoặc thao tác. Điều này đảm bảo rằng mã hoạt động như mong đợi. Hệ thống Loại cũng cho phép gợi ý mã phong phú hơn và tài liệu tự động.

Rust là một ngôn ngữ được gõ tĩnh. Mọi giá trị trong Rust đều thuộc một kiểu dữ liệu nhất định. Trình biên dịch có thể tự động suy ra kiểu dữ liệu của biến dựa trên giá trị được gán cho nó.

Khai báo một biến

Sử dụng let từ khóa để khai báo một biến.

fn main() {
   let company_string = "TutorialsPoint";  // string type
   let rating_float = 4.5;                 // float type
   let is_growing_boolean = true;          // boolean type
   let icon_char = '♥';                    //unicode character type

   println!("company name is:{}",company_string);
   println!("company rating on 5 is:{}",rating_float);
   println!("company is growing :{}",is_growing_boolean);
   println!("company icon is:{}",icon_char);
}

Trong ví dụ trên, kiểu dữ liệu của các biến sẽ được suy ra từ các giá trị được gán cho chúng. Ví dụ: Rust sẽ gán kiểu dữ liệu chuỗi cho biến company_string , kiểu dữ liệu float cho rating_float , v.v.

Bản in ! macro có hai đối số -

  • Cú pháp đặc biệt {} , là trình giữ chỗ
  • Tên biến hoặc một hằng số

Trình giữ chỗ sẽ được thay thế bằng giá trị của biến

Đầu ra của đoạn mã trên sẽ là:

company name is: TutorialsPoint
company rating on 5 is:4.5
company is growing: true
company icon is: ♥

Các loại vô hướng

Kiểu vô hướng đại diện cho một giá trị duy nhất. Ví dụ: 10,3.14, 'c'. Rust có bốn kiểu vô hướng chính.

  • Integer
  • Floating-point
  • Booleans
  • Characters

Chúng ta sẽ tìm hiểu về từng loại trong các phần tiếp theo của chúng tôi.

Số nguyên

Một số nguyên là một số không có thành phần phân số. Nói một cách đơn giản, kiểu dữ liệu số nguyên được sử dụng để biểu diễn các số nguyên.

Các số nguyên có thể được phân loại thêm là Đã ký và Chưa ký. Số nguyên có dấu có thể lưu trữ cả giá trị âm và giá trị dương. Các số nguyên không dấu chỉ có thể lưu trữ các giá trị dương. Mô tả chi tiết nếu các kiểu số nguyên được cung cấp bên dưới:

Sr.No. Kích thước Đã ký Chưa ký
1 8 bit i8 u8
2 16 bit i16 u16
3 32 bit i32 u32
4 64 bit i64 u64
5 128 bit i128 u128
6 Vòm isize sử dụng

Kích thước của một số nguyên có thể là vòm . Điều này có nghĩa là kích thước của kiểu dữ liệu sẽ được lấy từ kiến trúc của máy. Một số nguyên có kích thước là vòm sẽ là 32 bit trên máy x86 và 64 bit trên máy x64. Một số nguyên vòm chủ yếu được sử dụng khi lập chỉ mục một số loại tập hợp.

Hình minh họa

fn main() {
   let result = 10;    // i32 by default
   let age:u32 = 20;
   let sum:i32 = 5-15;
   let mark:isize = 10;
   let count:usize = 30;
   println!("result value is {}",result);
   println!("sum is {} and age is {}",sum,age);
   println!("mark is {} and count is {}",mark,count);
}

Đầu ra sẽ như dưới đây:

result value is 10
sum is -10 and age is 20
mark is 10 and count is 30

Đoạn mã trên sẽ trả về lỗi biên dịch nếu bạn thay thế giá trị của tuổi bằng giá trị dấu phẩy động.

Dải số nguyên

Mỗi biến thể có dấu có thể lưu trữ các số từ - (2 ^ (n-1) đến 2 ^ (n-1) -1 , trong đó n là số bit mà biến thể sử dụng. Ví dụ: i8 có thể lưu trữ các số từ - (2 ^ 7) thành 2 ^ 7 -1 - ở đây chúng tôi thay thế n bằng 8.

Mỗi biến thể không dấu có thể lưu trữ các số từ 0 đến (2 ^ n) -1 . Ví dụ: u8 có thể lưu trữ các số từ 0 đến 2 ^ 7 , tương đương với 0 đến 255.

Tràn số nguyên

Tràn số nguyên xảy ra khi giá trị được gán cho một biến số nguyên vượt quá phạm vi được xác định Rust cho kiểu dữ liệu. Hãy để chúng tôi hiểu điều này với một ví dụ -

fn main() {
   let age:u8 = 255;

   // 0 to 255 only allowed for u8
   let weight:u8 = 256;   //overflow value is 0
   let height:u8 = 257;   //overflow value is 1
   let score:u8 = 258;    //overflow value is 2

   println!("age is {} ",age);
   println!("weight is {}",weight);
   println!("height is {}",height);
   println!("score is {}",score);
}

Phạm vi hợp lệ của biến u8 không dấu là 0 đến 255. Trong ví dụ trên, các biến được gán giá trị lớn hơn 255 (giới hạn trên cho một biến số nguyên trong Rust). Khi thực thi, đoạn mã trên sẽ trả về cảnh báo -warning − literal out of range for u8cho các biến cân nặng, chiều cao và điểm số. Các giá trị tràn sau 255 sẽ bắt đầu từ 0, 1, 2, v.v. Kết quả cuối cùng không có cảnh báo như hình dưới đây:

age is 255
weight is 0
height is 1
score is 2

Phao nổi

Kiểu dữ liệu nổi trong Rust có thể được phân loại là f32f64. Loại f32 là phao có độ chính xác đơn và f64 có độ chính xác kép. Loại mặc định là f64. Hãy xem xét ví dụ sau để hiểu thêm về kiểu dữ liệu float.

fn main() {
   let result = 10.00;        //f64 by default
   let interest:f32 = 8.35;
   let cost:f64 = 15000.600;  //double precision
   
   println!("result value is {}",result);
   println!("interest is {}",interest);
   println!("cost is {}",cost);
}

Đầu ra sẽ như hình dưới đây -

interest is 8.35
cost is 15000.6

Đúc loại tự động

Không cho phép đúc kiểu tự động trong Rust. Hãy xem xét đoạn mã sau. Một giá trị số nguyên được gán cho biến floatinterest.

fn main() {
   let interest:f32 = 8;   // integer assigned to float variable
   println!("interest is {}",interest);
}

Trình biên dịch ném một mismatched types error như được đưa ra dưới đây.

error[E0308]: mismatched types
   --> main.rs:2:22
   |
 2 | let interest:f32=8;
   |    ^ expected f32, found integral variable
   |
   = note: expected type `f32`
      found type `{integer}`
error: aborting due to previous error(s)

Dấu phân cách số

Để dễ đọc các số lớn, chúng ta có thể sử dụng dấu phân tách trực quan _ dấu gạch dưới để phân tách các chữ số. Đó là 50.000 có thể được viết là 50_000. Điều này được hiển thị trong ví dụ dưới đây.

fn main() {
   let float_with_separator = 11_000.555_001;
   println!("float value {}",float_with_separator);
   
   let int_with_separator = 50_000;
   println!("int value {}",int_with_separator);
}

Đầu ra được đưa ra dưới đây -

float value 11000.555001
int value 50000

Boolean

Kiểu boolean có hai giá trị có thể có - đúng hoặc sai . Sử dụngbool từ khóa để khai báo một biến boolean.

Hình minh họa

fn main() {
   let isfun:bool = true;
   println!("Is Rust Programming Fun ? {}",isfun);
}

Đầu ra của đoạn mã trên sẽ là:

Is Rust Programming Fun ? true

Tính cách

Kiểu dữ liệu ký tự trong Rust hỗ trợ số, bảng chữ cái, Unicode và các ký tự đặc biệt. Sử dụngchartừ khóa để khai báo một biến kiểu dữ liệu ký tự. Kiểu char của Rust đại diện cho Giá trị vô hướng Unicode, có nghĩa là nó có thể đại diện cho nhiều thứ hơn là chỉ ASCII. Giá trị vô hướng Unicode có phạm vi từU+0000 đến U+D7FFU+E000 đến U+10FFFF bao gồm.

Chúng ta hãy xem xét một ví dụ để hiểu thêm về kiểu dữ liệu Ký tự.

fn main() {
   let special_character = '@'; //default
   let alphabet:char = 'A';
   let emoji:char = '';
   
   println!("special character is {}",special_character);
   println!("alphabet is {}",alphabet);
   println!("emoji is {}",emoji);
}

Đầu ra của đoạn mã trên sẽ là:

special character is @
alphabet is A
emoji is

Một biến là một nơi lưu trữ được đặt tên mà các chương trình có thể thao tác. Nói một cách đơn giản, một biến giúp các chương trình lưu trữ các giá trị. Các biến trong Rust được liên kết với một kiểu dữ liệu cụ thể. Kiểu dữ liệu xác định kích thước và cách bố trí bộ nhớ của biến, phạm vi giá trị có thể được lưu trữ trong bộ nhớ đó và tập hợp các thao tác có thể thực hiện trên biến.

Quy tắc đặt tên biến

Trong phần này, chúng ta sẽ tìm hiểu về các quy tắc khác nhau để đặt tên một biến.

  • Tên của một biến có thể bao gồm các chữ cái, chữ số và ký tự gạch dưới.

  • Nó phải bắt đầu bằng một chữ cái hoặc một dấu gạch dưới.

  • Chữ hoa và chữ thường khác nhau vì Rust phân biệt chữ hoa chữ thường.

Cú pháp

Kiểu dữ liệu là tùy chọn trong khi khai báo một biến trong Rust. Kiểu dữ liệu được suy ra từ giá trị được gán cho biến.

Cú pháp khai báo một biến được đưa ra dưới đây.

let variable_name = value;            // no type specified
let variable_name:dataType = value;   //type specified

Hình minh họa

fn main() {
   let fees = 25_000;
   let salary:f64 = 35_000.00;
   println!("fees is {} and salary is {}",fees,salary);
}

Đầu ra của đoạn mã trên sẽ là fees is 25000 and salary is 35000.

Bất biến

Theo mặc định, các biến là bất biến - chỉ đọc trong Rust. Nói cách khác, giá trị của biến không thể thay đổi khi một giá trị được liên kết với tên biến.

Hãy để chúng tôi hiểu điều này với một ví dụ.

fn main() {
   let fees = 25_000;
   println!("fees is {} ",fees);
   fees = 35_000;
   println!("fees changed is {}",fees);
}

Đầu ra sẽ như hình dưới đây -

error[E0384]: re-assignment of immutable variable `fees`
 --> main.rs:6:3
   |
 3 | let fees = 25_000;
   | ---- first assignment to `fees`
...
 6 | fees=35_000;
   | ^^^^^^^^^^^ re-assignment of immutable variable

error: aborting due to previous error(s)

Thông báo lỗi cho biết nguyên nhân gây ra lỗi - bạn không thể gán giá trị hai lần cho biến phí bất biến. Đây là một trong nhiều cách Rust cho phép các lập trình viên viết mã và tận dụng sự an toàn và đồng thời dễ dàng.

Có thể thay đổi

Các biến là bất biến theo mặc định. Tiền tố tên biến bằngmuttừ khóa để làm cho nó có thể thay đổi. Giá trị của một biến có thể thay đổi có thể được thay đổi.

Cú pháp khai báo một biến có thể thay đổi như hình dưới đây:

let mut variable_name = value;
let mut variable_name:dataType = value;
Let us understand this with an example

fn main() {
   let mut fees:i32 = 25_000;
   println!("fees is {} ",fees);
   fees = 35_000;
   println!("fees changed is {}",fees);
}

Đầu ra của đoạn mã được đưa ra dưới đây:

fees is 25000
fees changed is 35000

Hằng số đại diện cho các giá trị không thể thay đổi. Nếu bạn khai báo một hằng số thì không có cách nào giá trị của nó thay đổi. Từ khóa để sử dụng hằng số làconst. Các hằng số phải được nhập rõ ràng. Sau đây là cú pháp để khai báo một hằng số.

const VARIABLE_NAME:dataType = value;

Quy ước đặt tên không đổi gỉ

Quy ước đặt tên cho Hằng tương tự như quy ước đặt tên cho các biến. Tất cả các ký tự trong một tên hằng thường được viết hoa. Không giống như khai báo các biến,let từ khóa không được sử dụng để khai báo một hằng số.

Chúng tôi đã sử dụng hằng số trong Rust trong ví dụ dưới đây -

fn main() {
   const USER_LIMIT:i32 = 100;    // Declare a integer constant
   const PI:f32 = 3.14;           //Declare a float constant

   println!("user limit is {}",USER_LIMIT);  //Display value of the constant
   println!("pi value is {}",PI);            //Display value of the constant
}

Hằng số v / s Biến

Trong phần này, chúng ta sẽ tìm hiểu về các yếu tố phân biệt giữa hằng và biến.

  • Hằng số được khai báo bằng cách sử dụng const từ khóa trong khi các biến được khai báo bằng cách sử dụng let từ khóa.

  • Một khai báo biến có thể tùy chọn có kiểu dữ liệu trong khi khai báo hằng phải chỉ định kiểu dữ liệu. Điều này có nghĩa là const USER_LIMIT = 100 sẽ dẫn đến lỗi.

  • Một biến được khai báo bằng cách sử dụng lettừ khóa mặc định là bất biến. Tuy nhiên, bạn có một tùy chọn để thay đổi nó bằng cách sử dụngmuttừ khóa. Hằng số là bất biến.

  • Hằng số chỉ có thể được đặt thành một biểu thức hằng chứ không phải kết quả của một lệnh gọi hàm hoặc bất kỳ giá trị nào khác sẽ được tính toán trong thời gian chạy.

  • Hằng số có thể được khai báo trong bất kỳ phạm vi nào, bao gồm cả phạm vi toàn cục, điều này làm cho chúng hữu ích cho các giá trị mà nhiều phần của mã cần biết.

Đổ bóng của các biến và hằng số

Rust cho phép người lập trình khai báo các biến có cùng tên. Trong trường hợp như vậy, biến mới sẽ ghi đè biến trước đó.

Hãy để chúng tôi hiểu điều này với một ví dụ.

fn main() {
   let salary = 100.00;
   let salary = 1.50 ; 
   // reads first salary
   println!("The value of salary is :{}",salary);
}

Đoạn mã trên khai báo hai biến theo tên lương. Khai báo đầu tiên được gán giá trị 100,00 trong khi khai báo thứ hai được gán giá trị 1,50. Biến thứ hai đổ bóng hoặc ẩn biến đầu tiên trong khi hiển thị đầu ra.

Đầu ra

The value of salary is :1.50

Rust hỗ trợ các biến với các kiểu dữ liệu khác nhau trong khi tạo bóng.

Hãy xem xét ví dụ sau.

Mã khai báo hai biến theo tên uname. Khai báo đầu tiên được gán một giá trị chuỗi, trong khi khai báo thứ hai được gán một số nguyên. Hàm len trả về tổng số ký tự trong một giá trị chuỗi.

fn main() {
   let uname = "Mohtashim";
   let uname = uname.len();
   println!("name changed to integer : {}",uname);
}

Đầu ra

name changed to integer: 9

Không giống như các biến, hằng số không thể bị che khuất. Nếu các biến trong chương trình trên được thay thế bằng hằng số, trình biên dịch sẽ báo lỗi.

fn main() {
   const NAME:&str = "Mohtashim";
   const NAME:usize = NAME.len(); 
   //Error : `NAME` already defined
   println!("name changed to integer : {}",NAME);
}

Kiểu dữ liệu chuỗi trong Rust có thể được phân loại thành như sau:

  • Chuỗi chữ(&str)

  • Đối tượng chuỗi(String)

Chuỗi chữ

Chuỗi ký tự (& str) được sử dụng khi giá trị của chuỗi được biết tại thời điểm biên dịch. Chuỗi ký tự là một tập hợp các ký tự, được mã hóa cứng thành một biến. Ví dụ: let company = "Tutorials Point" . Các ký tự chuỗi được tìm thấy trong mô-đun std :: str. Các ký tự chuỗi còn được gọi là các lát chuỗi.

Ví dụ sau khai báo hai chuỗi ký tự - công tyvị trí .

fn main() {
   let company:&str="TutorialsPoint";
   let location:&str = "Hyderabad";
   println!("company is : {} location :{}",company,location);
}

Các ký tự chuỗi là tĩnh theo mặc định. Điều này có nghĩa là các ký tự chuỗi được đảm bảo có giá trị trong suốt thời gian của toàn bộ chương trình. Chúng ta cũng có thể chỉ định rõ ràng biến là static như hình dưới đây:

fn main() {
   let company:&'static str = "TutorialsPoint";
   let location:&'static str = "Hyderabad";
   println!("company is : {} location :{}",company,location);
}

Chương trình trên sẽ tạo ra kết quả sau:

company is : TutorialsPoint location :Hyderabad

Đối tượng chuỗi

Kiểu đối tượng Chuỗi được cung cấp trong Thư viện Chuẩn. Không giống như chuỗi ký tự, kiểu đối tượng chuỗi không phải là một phần của ngôn ngữ cốt lõi. Nó được định nghĩa là cấu trúc công cộng trong thư viện chuẩn pub struct String . Chuỗi là một tập hợp có thể phát triển. Đây là loại có thể thay đổi và được mã hóa UTF-8. CácStringkiểu đối tượng có thể được sử dụng để biểu diễn các giá trị chuỗi được cung cấp trong thời gian chạy. Đối tượng chuỗi được cấp phát trong heap.

Cú pháp

Để tạo một đối tượng String, chúng ta có thể sử dụng bất kỳ cú pháp nào sau đây:

String::new()

Cú pháp trên tạo một chuỗi trống

String::from()

Điều này tạo ra một chuỗi với một số giá trị mặc định được truyền dưới dạng tham số cho from() phương pháp.

Ví dụ sau minh họa việc sử dụng đối tượng String.

fn main(){
   let empty_string = String::new();
   println!("length is {}",empty_string.len());

   let content_string = String::from("TutorialsPoint");
   println!("length is {}",content_string.len());
}

Ví dụ trên tạo hai chuỗi - một đối tượng chuỗi rỗng sử dụng phương thức mới và một đối tượng chuỗi từ chuỗi ký tự bằng cách sử dụng phương thức from .

Đầu ra như hình dưới đây -

length is 0
length is 14

Các phương thức chung - Đối tượng chuỗi

Sr.No. phương pháp Chữ ký Sự miêu tả
1 Mới() pub const fn new () → Chuỗi Tạo một chuỗi trống mới.
2 to_string () fn to_string (& self) → Chuỗi Chuyển đổi giá trị đã cho thành Chuỗi.
3 thay thế() pub fn thay thế <'a, P> (&' a self, from: P, to: & str) → String Thay thế tất cả các kết quả phù hợp của một mẫu bằng một chuỗi khác.
4 as_str () pub fn as_str (& self) → & str Trích xuất một lát chuỗi chứa toàn bộ chuỗi.
5 đẩy() pub fn push (& mut self, ch: char) Nối các char đã cho vào cuối Chuỗi này.
6 push_str () pub fn push_str (& mut self, string: & str) Thêm một lát chuỗi đã cho vào phần cuối của Chuỗi này.
7 len () pub fn len (& self) → usize Trả về độ dài của Chuỗi này, tính bằng byte.
số 8 trim () pub fn trim (& self) → & str Trả về một lát chuỗi đã xóa bỏ khoảng trắng ở đầu và cuối.
9 split_whitespace () pub fn split_whitespace (& self) → SplitWhitespace Tách một lát chuỗi theo khoảng trắng và trả về một trình lặp.
10 tách () pub fn split <'a, P> (&' a self, pat: P) → Split <'a, P>, trong đó P là mẫu có thể là & str, char hoặc một bao đóng xác định sự phân tách. Trả về một trình lặp trên các chuỗi con của lát chuỗi này, được phân tách bằng các ký tự được so khớp bằng một mẫu.
11 ký tự () ký tự pub fn (& self) → Các ký tự Trả về một trình lặp trên các ký tự của một lát chuỗi.

Hình minh họa: new ()

Một đối tượng chuỗi rỗng được tạo bằng cách sử dụng new()và giá trị của nó được đặt thành hello .

fn main(){
   let mut z = String::new();
   z.push_str("hello");
   println!("{}",z);
}

Đầu ra

Chương trình trên tạo ra kết quả sau:

hello

Hình minh họa: to_string ()

Để truy cập tất cả các phương thức của đối tượng Chuỗi, hãy chuyển đổi một chuỗi ký tự thành kiểu đối tượng bằng cách sử dụng to_string() chức năng.

fn main(){
   let name1 = "Hello TutorialsPoint , 
   Hello!".to_string();
   println!("{}",name1);
}

Đầu ra

Chương trình trên tạo ra kết quả sau:

Hello TutorialsPoint , Hello!

Hình minh họa: Replace ()

Các replace()hàm nhận hai tham số - tham số đầu tiên là một mẫu chuỗi để tìm kiếm và tham số thứ hai là giá trị mới được thay thế. Trong ví dụ trên, Hello xuất hiện hai lần trong chuỗi name1 .

Hàm thay thế thay thế tất cả các lần xuất hiện của chuỗi Hello với Howdy.

fn main(){
   let name1 = "Hello TutorialsPoint , 
   Hello!".to_string();         //String object
   let name2 = name1.replace("Hello","Howdy");    //find and replace
   println!("{}",name2);
}

Đầu ra

Chương trình trên tạo ra kết quả sau:

Howdy TutorialsPoint , Howdy!

Hình minh họa: as_str ()

Các as_str() hàm trích xuất một lát chuỗi chứa toàn bộ chuỗi.

fn main() {
   let example_string = String::from("example_string");
   print_literal(example_string.as_str());
}
fn print_literal(data:&str ){
   println!("displaying string literal {}",data);
}

Đầu ra

Chương trình trên tạo ra kết quả sau:

displaying string literal example_string

Hình minh họa: push ()

Các push() hàm nối các ký tự đã cho vào cuối Chuỗi này.

fn main(){
   let mut company = "Tutorial".to_string();
   company.push('s');
   println!("{}",company);
}

Đầu ra

Chương trình trên tạo ra kết quả sau:

Tutorials

Hình minh họa: push_str ()

Các push_str() hàm nối một lát chuỗi đã cho vào phần cuối của một Chuỗi.

fn main(){
   let mut company = "Tutorials".to_string();
   company.push_str(" Point");
   println!("{}",company);
}

Đầu ra

Chương trình trên tạo ra kết quả sau:

Tutorials Point

Hình minh họa: len ()

Các len() hàm trả về tổng số ký tự trong một chuỗi (bao gồm cả dấu cách).

fn main() {
   let fullname = " Tutorials Point";
   println!("length is {}",fullname.len());
}

Đầu ra

Chương trình trên tạo ra kết quả sau:

length is 20

Hình minh họa: trim ()

Hàm trim () loại bỏ các khoảng trắng ở đầu và cuối trong một chuỗi. LƯU Ý rằng hàm này sẽ không loại bỏ các khoảng trắng nội tuyến.

fn main() {
   let fullname = " Tutorials Point \r\n";
   println!("Before trim ");
   println!("length is {}",fullname.len());
   println!();
   println!("After trim ");
   println!("length is {}",fullname.trim().len());
}

Đầu ra

Chương trình trên tạo ra kết quả sau:

Before trim
length is 24

After trim
length is 15

Hình minh họa: split_whitespace ()

Các split_whitespace()chia chuỗi đầu vào thành các chuỗi khác nhau. Nó trả về một trình lặp, vì vậy chúng tôi đang lặp qua các mã thông báo như hình dưới đây:

fn main(){
   let msg = "Tutorials Point has good t
   utorials".to_string();
   let mut i = 1;
   
   for token in msg.split_whitespace(){
      println!("token {} {}",i,token);
      i+=1;
   }
}

Đầu ra

token 1 Tutorials
token 2 Point
token 3 has
token 4 good
token 5 tutorials

Minh họa: chuỗi split ()

Các split() stringphương thức trả về một trình lặp trên các chuỗi con của một lát chuỗi, được phân tách bằng các ký tự được so khớp bởi một mẫu. Hạn chế của phương thức split () là không thể lưu trữ kết quả để sử dụng sau này. Cáccollect có thể được sử dụng để lưu trữ kết quả được trả về bởi split () dưới dạng một vectơ.

fn main() {
   let fullname = "Kannan,Sudhakaran,Tutorialspoint";

   for token in fullname.split(","){
      println!("token is {}",token);
   }

   //store in a Vector
   println!("\n");
   let tokens:Vec<&str>= fullname.split(",").collect();
   println!("firstName is {}",tokens[0]);
   println!("lastname is {}",tokens[1]);
   println!("company is {}",tokens[2]);
}

Ví dụ trên chia chuỗi fullname, bất cứ khi nào nó gặp phải dấu phẩy (,).

Đầu ra

token is Kannan
token is Sudhakaran
token is Tutorialspoint

firstName is Kannan
lastname is Sudhakaran
company is Tutorialspoint

Hình minh họa: chars ()

Các ký tự riêng lẻ trong một chuỗi có thể được truy cập bằng phương thức ký tự. Chúng ta hãy xem xét một ví dụ để hiểu điều này.

fn main(){
   let n1 = "Tutorials".to_string();

   for n in n1.chars(){
      println!("{}",n);
   }
}

Đầu ra

T
u
t
o
r
i
a
l
s

Nối chuỗi với toán tử +

Một giá trị chuỗi có thể được nối vào một chuỗi khác. Đây được gọi là nối hoặc nội suy. Kết quả của việc nối chuỗi là một đối tượng chuỗi mới. Toán tử + sử dụng nội bộ một phương thức thêm . Cú pháp của hàm add có hai tham số. Tham số đầu tiên là self - chính đối tượng chuỗi và tham số thứ hai là một tham chiếu của đối tượng chuỗi thứ hai. Điều này được hiển thị bên dưới -

//add function
add(self,&str)->String { 
   // returns a String object
}

Minh họa: Kết nối chuỗi

fn main(){
   let n1 = "Tutorials".to_string();
   let n2 = "Point".to_string();

   let n3 = n1 + &n2; // n2 reference is passed
   println!("{}",n3);
}

Đầu ra sẽ như dưới đây

TutorialsPoint

Minh họa: Loại Đúc

Ví dụ sau minh họa việc chuyển đổi một số thành một đối tượng chuỗi:

fn main(){
   let number = 2020;
   let number_as_string = number.to_string(); 
   
   // convert number to string
   println!("{}",number_as_string);
   println!("{}",number_as_string=="2020");
}

Đầu ra sẽ như dưới đây

2020
true

Hình minh họa: Định dạng! Macro

Một cách khác để thêm vào các đối tượng Chuỗi với nhau là sử dụng một hàm macro được gọi là định dạng. Việc sử dụng Định dạng! như hình dưới đây.

fn main(){
   let n1 = "Tutorials".to_string();
   let n2 = "Point".to_string();
   let n3 = format!("{} {}",n1,n2);
   println!("{}",n3);
}

Đầu ra sẽ như dưới đây

Tutorials Point

Một toán tử xác định một số chức năng sẽ được thực hiện trên dữ liệu. Dữ liệu mà các toán tử làm việc được gọi là toán hạng. Hãy xem xét biểu thức sau:

7 + 5 = 12

Ở đây, các giá trị 7, 5 và 12 là toán hạng, trong khi + và = là toán tử.

Các toán tử chính trong Rust có thể được phân loại là:

  • Arithmetic
  • Bitwise
  • Comparison
  • Logical
  • Bitwise
  • Conditional

Toán tử số học

Giả sử các giá trị trong biến a và b lần lượt là 10 và 5.

Hiển thị các ví dụ

Sr.No Nhà điều hành Sự miêu tả Thí dụ
1 + (Bổ sung) trả về tổng của các toán hạng a + b là 15
2 - (Phép trừ) trả về sự khác biệt của các giá trị ab là 5
3 * (Phép nhân) trả về sản phẩm của các giá trị a * b là 50
4 / (Bộ phận) thực hiện phép toán chia và trả về thương số a / b là 2
5 % (Mô-đun) thực hiện phép toán chia và trả về phần còn lại a% b là 0

NOTE - Toán tử ++ và - không được hỗ trợ trong Rust.

Toán tử quan hệ

Toán tử quan hệ kiểm tra hoặc xác định kiểu quan hệ giữa hai thực thể. Toán tử quan hệ được sử dụng để so sánh hai hoặc nhiều giá trị. Các toán tử quan hệ trả về giá trị Boolean - true hoặc false.

Giả sử giá trị của A là 10 và B là 20.

Hiển thị các ví dụ

Sr.No Nhà điều hành Sự miêu tả Thí dụ
1 > Lớn hơn (A> B) là Sai
2 < Ít hơn (A <B) là True
3 > = Lớn hơn hoặc bằng (A> = B) là Sai
4 <= Nhỏ hơn hoặc bằng (A <= B) là True
5 == Bình đẳng (A == B) là giả mạo
6 ! = Không công bằng (A! = B) là True

Toán tử logic

Toán tử lôgic được sử dụng để kết hợp hai hoặc nhiều điều kiện. Các toán tử logic cũng trả về giá trị Boolean. Giả sử giá trị của biến A là 10 và B là 20.

Hiển thị các ví dụ

Sr.No Nhà điều hành Sự miêu tả Thí dụ
1 && (Và) Toán tử chỉ trả về true nếu tất cả các biểu thức được chỉ định trả về true (A> 10 && B> 10) là Sai
2 || (HOẶC) Toán tử trả về true nếu ít nhất một trong các biểu thức được chỉ định trả về true (A> 10 || B> 10) là True
3 ! (KHÔNG PHẢI) Toán tử trả về nghịch đảo của kết quả của biểu thức. Ví dụ:! (> 5) trả về false ! (A> 10) là Đúng

Toán tử Bitwise

Giả sử biến A = 2 và B = 3.

Hiển thị các ví dụ

Sr.No Nhà điều hành Sự miêu tả Thí dụ
1 & (Bitwise AND) Nó thực hiện phép toán Boolean AND trên mỗi bit của các đối số nguyên của nó. (A & B) là 2
2 | (BitWise HOẶC) Nó thực hiện phép toán Boolean OR trên mỗi bit của các đối số nguyên của nó. (A | B) là 3
3 ^ (Bitwise XOR) Nó thực hiện phép toán HOẶC độc quyền Boolean trên mỗi bit của các đối số nguyên của nó. HOẶC độc quyền có nghĩa là toán hạng một là đúng hoặc toán hạng hai là đúng, nhưng không phải cả hai. (A ^ B) là 1
4 ! (Bitwise Không) Nó là một toán tử một ngôi và hoạt động bằng cách đảo ngược tất cả các bit trong toán hạng. (! B) là -4
5 << (Dịch sang trái) Nó di chuyển tất cả các bit trong toán hạng đầu tiên sang trái theo số vị trí được chỉ định trong toán hạng thứ hai. Các bit mới được lấp đầy bởi các số không. Chuyển một giá trị sang trái một vị trí tương đương với nhân nó với 2, dịch chuyển hai vị trí tương đương với nhân 4, v.v. (A << 1) là 4
6 >> (Chuyển sang phải) Toán tử Shift phải nhị phân. Giá trị của toán hạng bên trái được di chuyển sang phải bằng số bit được chỉ định bởi toán hạng bên phải. (A >> 1) là 1
7 >>> (Sang phải với Zero) Toán tử này cũng giống như toán tử >>, ngoại trừ việc các bit được chuyển sang trái luôn bằng không. (A >>> 1) là 1

Cấu trúc ra quyết định yêu cầu người lập trình chỉ định một hoặc nhiều điều kiện để được đánh giá hoặc kiểm tra bởi chương trình, cùng với một câu lệnh hoặc các câu lệnh sẽ được thực thi nếu điều kiện được xác định là đúng, và tùy chọn, các câu lệnh khác sẽ được thực thi nếu điều kiện được xác định là sai.

Dưới đây là hình thức chung của cấu trúc ra quyết định điển hình được tìm thấy trong hầu hết các ngôn ngữ lập trình -

Sr.No Tuyên bố & Mô tả
1

if statement

Một khi tuyên bố bao gồm một biểu thức Boolean theo sau là một hoặc nhiều câu lệnh.

2

if...else statement

Một khi tuyên bố có thể được theo sau bởi một tùy chọn khác tuyên bố, mà thực hiện khi biểu thức Boolean là sai.

3

else...if and nested ifstatement

Bạn có thể sử dụng một câu lệnh if hoặc else if bên trong một câu lệnh if hoặc else if khác .

4

match statement

Một trận đấu tuyên bố cho phép một biến được thử nghiệm chống lại một danh sách các giá trị.

Nếu tuyên bố

Cấu trúc if… else đánh giá một điều kiện trước khi một khối mã được thực thi.

Cú pháp

if boolean_expression {
   // statement(s) will execute if the boolean expression is true
}

Nếu biểu thức Boolean đánh giá là true, thì khối mã bên trong câu lệnh if sẽ được thực thi. Nếu biểu thức Boolean được đánh giá là false, thì tập mã đầu tiên sau khi kết thúc câu lệnh if (sau dấu ngoặc nhọn đóng) sẽ được thực thi.

fn main(){
   let num:i32 = 5;
   if num > 0 {
      println!("number is positive") ;
   }
}

Ví dụ trên sẽ in number is positive như điều kiện được chỉ định bởi khối if là true.

nếu khác tuyên bố

An if có thể được theo sau bởi một tùy chọn elsekhối. Khối else sẽ thực thi nếu biểu thức Boolean được kiểm tra bởi câu lệnh if cho kết quả là false.

Cú pháp

if boolean_expression {
   // statement(s) will execute if the boolean expression is true
} else {
   // statement(s) will execute if the boolean expression is false
}

Sơ đồ

Các ifkhối bảo vệ biểu thức điều kiện. Khối liên kết với câu lệnh if được thực thi nếu biểu thức Boolean đánh giá là true.

Khối if có thể được theo sau bởi một câu lệnh else tùy chọn. Khối lệnh liên kết với khối else được thực thi nếu biểu thức đánh giá là false.

Hình minh họa - Đơn giản nếu… khác

fn main() {
   let num = 12;
   if num % 2==0 {
      println!("Even");
   } else {
      println!("Odd");
   }
}

Ví dụ trên in ra giá trị trong một biến là chẵn hay lẻ. Khối if kiểm tra khả năng chia hết của giá trị cho 2 để xác định giá trị tương tự. Đây là đầu ra của đoạn mã trên -

Even

Lồng nhau nếu

Các else…ifthang rất hữu ích để kiểm tra nhiều điều kiện. Cú pháp như hình dưới đây -

Cú pháp

if boolean_expression1 {
   //statements if the expression1 evaluates to true
} else if boolean_expression2 {
   //statements if the expression2 evaluates to true
} else {
   //statements if both expression1 and expression2 result to false
}

Khi sử dụng câu lệnh if… else… if và else, có một số điểm cần lưu ý.

  • Một if có thể có số 0 hoặc số khác và nó phải đứng sau bất kỳ cái nào khác..nếu.
  • Một if có thể có 0 với nhiều else..if và chúng phải đến trước if khác.
  • Một khi cái khác..nếu thành công, không cái nào còn lại..nếu hoặc khác sẽ được kiểm tra.

Ví dụ: else… if bậc thang

fn main() {
   let num = 2 ;
   if num > 0 {
      println!("{} is positive",num);
   } else if num < 0 {
      println!("{} is negative",num);
   } else {
      println!("{} is neither positive nor negative",num) ;
   }
}

Đoạn mã hiển thị giá trị là dương, âm hay không.

Đầu ra

2 is positive

Tuyên bố đối sánh

Câu lệnh so khớp kiểm tra xem giá trị hiện tại có khớp với danh sách các giá trị hay không, điều này rất giống với câu lệnh switch trong ngôn ngữ C. Trước hết, hãy lưu ý rằng biểu thức theo sau từ khóa đối sánh không cần phải đặt trong dấu ngoặc đơn.

Cú pháp như hình dưới đây.

let expressionResult = match variable_expression {
   constant_expr1 => {
      //statements;
   },
   constant_expr2 => {
      //statements;
   },
   _ => {
      //default
   }
};

Trong ví dụ dưới đây, state_code được so khớp với một danh sách các giá trị MH, KL, KA, GA- nếu tìm thấy bất kỳ kết quả phù hợp nào, giá trị chuỗi được trả về trạng thái biến . Nếu không tìm thấy kết quả phù hợp nào, thì trường hợp mặc định _ khớp và giá trị Unkown được trả về.

fn main(){
   let state_code = "MH";
   let state = match state_code {
      "MH" => {println!("Found match for MH"); "Maharashtra"},
      "KL" => "Kerala",
      "KA" => "Karnadaka",
      "GA" => "Goa",
      _ => "Unknown"
   };
   println!("State name is {}",state);
}

Đầu ra

Found match for MH
State name is Maharashtra

Có thể có các trường hợp, trong đó một khối mã cần được thực thi nhiều lần. Nói chung, các lệnh lập trình được thực hiện tuần tự: Câu lệnh đầu tiên trong một hàm được thực hiện đầu tiên, tiếp theo là câu lệnh thứ hai, v.v.

Các ngôn ngữ lập trình cung cấp các cấu trúc điều khiển khác nhau cho phép các đường dẫn thực thi phức tạp hơn.

Một câu lệnh lặp cho phép chúng ta thực hiện một câu lệnh hoặc một nhóm câu lệnh nhiều lần. Dưới đây là dạng chung của câu lệnh lặp trong hầu hết các ngôn ngữ lập trình.

Rust cung cấp các loại vòng lặp khác nhau để xử lý các yêu cầu về vòng lặp -

  • while
  • loop
  • for

Vòng lặp xác định

Một vòng lặp có số lần lặp là xác định / cố định được gọi là một vòng lặp xác định. Cácfor vòng lặp là một thực hiện của một vòng lặp xác định.

Đối với vòng lặp

Vòng lặp for thực thi khối mã trong một số lần xác định. Nó có thể được sử dụng để lặp qua một tập giá trị cố định, chẳng hạn như một mảng. Cú pháp của vòng lặp for như dưới đây

Cú pháp

for temp_variable in lower_bound..upper_bound {
   //statements
}

Ví dụ về vòng lặp for như hình dưới đây

fn main(){
   for x in 1..11{ // 11 is not inclusive
      if x==5 {
         continue;
      }
      println!("x is {}",x);
   }
}

NOTE: rằng biến x chỉ có thể truy cập được trong khối for.

Đầu ra

x is 1
x is 2
x is 3
x is 4
x is 6
x is 7
x is 8
x is 9
x is 10

Vòng lặp vô thời hạn

Vòng lặp không xác định được sử dụng khi số lần lặp trong vòng lặp là không xác định hoặc không xác định.

Các vòng lặp không giới hạn có thể được thực hiện bằng cách sử dụng -

Sr.No Tên & Mô tả
1

While

Các trong khi vòng lặp thực hiện các hướng dẫn mỗi lần điều kiện quy định để đánh giá đúng

2

Loop

Các vòng lặp là một while (true) vòng lặp không xác định

Minh họa - trong khi

fn main(){
   let mut x = 0;
   while x < 10{
      x+=1;
      println!("inside loop x value is {}",x);
   }
   println!("outside loop x value is {}",x);
}

Đầu ra như hình dưới đây -

inside loop x value is 1
inside loop x value is 2
inside loop x value is 3
inside loop x value is 4
inside loop x value is 5
inside loop x value is 6
inside loop x value is 7
inside loop x value is 8
inside loop x value is 9
inside loop x value is 10
outside loop x value is 10

Hình minh họa −loop

fn main(){
   //while true

   let mut x = 0;
   loop {
      x+=1;
      println!("x={}",x);

      if x==15 {
         break;
      }
   }
}

Các breakcâu lệnh được sử dụng để lấy điều khiển ra khỏi một cấu trúc. Sử dụng break trong một vòng lặp khiến chương trình thoát khỏi vòng lặp.

Đầu ra

x=1
x=2
x=3
x=4
x=5
x=6
x=7
x=8
x=9
x=10
x=11
x=12
x=13
x=14
x=15

Tiếp tục Tuyên bố

Câu lệnh continue bỏ qua các câu lệnh tiếp theo trong vòng lặp hiện tại và đưa điều khiển trở lại phần đầu của vòng lặp. Không giống như câu lệnh break, continue không thoát khỏi vòng lặp. Nó kết thúc lần lặp hiện tại và bắt đầu lần lặp tiếp theo.

Dưới đây là một ví dụ về câu lệnh continue.

fn main() {

   let mut count = 0;

   for num in 0..21 {
      if num % 2==0 {
         continue;
      }
      count+=1;
   }
   println! (" The count of odd values between 0 and 20 is: {} ",count);
   //outputs 10
}

Ví dụ trên hiển thị số lượng giá trị chẵn từ 0 đến 20. Vòng lặp thoát khỏi vòng lặp hiện tại nếu số đó là số chẵn. Điều này đạt được bằng cách sử dụng câu lệnh continue.

Tổng số các giá trị lẻ từ 0 đến 20 là 10

Các hàm là các khối xây dựng của mã có thể đọc được, có thể bảo trì và có thể tái sử dụng. Hàm là một tập hợp các câu lệnh để thực hiện một nhiệm vụ cụ thể. Các hàm tổ chức chương trình thành các khối mã logic. Sau khi được xác định, các hàm có thể được gọi để truy cập mã. Điều này làm cho mã có thể được sử dụng lại. Hơn nữa, các hàm giúp bạn dễ dàng đọc và duy trì mã của chương trình.

Một khai báo hàm cho trình biên dịch biết về tên, kiểu trả về và các tham số của hàm. Một định nghĩa hàm cung cấp nội dung thực tế của hàm.

Sr.No Mô tả chức năng
1

Defining a function

Định nghĩa hàm TA xác định những gì và cách thức một nhiệm vụ cụ thể sẽ được thực hiện.

2

Calling or invoking a Function

Một hàm phải được gọi để thực thi nó.

3

Returning Functions

Các hàm cũng có thể trả về giá trị cùng với điều khiển, quay lại người gọi.

4

Parameterized Function

Tham số là một cơ chế để truyền giá trị cho các hàm.

Xác định một chức năng

Một định nghĩa hàm xác định những gì và cách thức một nhiệm vụ cụ thể sẽ được thực hiện. Trước khi sử dụng một hàm, nó phải được xác định. Phần thân hàm chứa mã sẽ được thực thi bởi hàm. Các quy tắc đặt tên một hàm tương tự như đối với một biến. Các hàm được xác định bằng cách sử dụngfntừ khóa. Cú pháp để xác định một hàm tiêu chuẩn được đưa ra dưới đây

Cú pháp

fn function_name(param1,param2..paramN) {
   // function body
}

Một khai báo hàm có thể chứa các tham số / đối số tùy ý. Các tham số được sử dụng để truyền các giá trị cho các hàm.

Ví dụ - Định nghĩa hàm đơn giản

//Defining a function
fn fn_hello(){
   println!("hello from function fn_hello ");
}

Gọi một hàm

Một hàm phải được gọi để thực thi nó. Quá trình này được gọi làfunction invocation. Giá trị cho các tham số phải được chuyển khi một hàm được gọi. Hàm gọi một hàm khác được gọi làcaller function.

Cú pháp

function_name(val1,val2,valN)

Ví dụ: Gọi một hàm

fn main(){
   //calling a function
   fn_hello();
}

Ở đây, hàm main () là hàm người gọi.

Hình minh họa

Ví dụ sau định nghĩa một hàm fn_hello(). Hàm in một thông báo đến bàn điều khiển. Cácmain()hàm gọi hàm fn_hello () .

fn main(){
   //calling a function
   fn_hello();
}
//Defining a function
fn fn_hello(){
   println!("hello from function fn_hello ");
}

Đầu ra

hello from function fn_hello

Trả về giá trị từ một hàm

Các hàm cũng có thể trả về một giá trị cùng với điều khiển, trở lại người gọi. Các hàm như vậy được gọi là hàm trả về.

Cú pháp

Có thể sử dụng một trong hai cú pháp sau để xác định một hàm có kiểu trả về.

Với câu lệnh trả lại

// Syntax1
fn function_name() -> return_type {
   //statements
   return value;
}

Cú pháp viết tắt không có câu lệnh trả về

//Syntax2
fn function_name() -> return_type {
   value //no semicolon means this value is returned
}

sự say mê

fn main(){
   println!("pi value is {}",get_pi());
}
fn get_pi()->f64 {
   22.0/7.0
}

Đầu ra

pi value is 3.142857142857143

Chức năng với các tham số

Tham số là một cơ chế để truyền giá trị cho các hàm. Các tham số tạo thành một phần của chữ ký của hàm. Các giá trị tham số được chuyển cho hàm trong quá trình gọi của nó. Trừ khi được chỉ định rõ ràng, số lượng giá trị được truyền vào một hàm phải khớp với số lượng tham số được xác định.

Các tham số có thể được truyền cho một hàm bằng một trong các kỹ thuật sau:

Vượt qua giá trị

Khi một phương thức được gọi, một vị trí lưu trữ mới sẽ được tạo cho mỗi tham số giá trị. Giá trị của các tham số thực tế được sao chép vào chúng. Do đó, các thay đổi được thực hiện đối với tham số bên trong phương thức được gọi không ảnh hưởng đến đối số.

Ví dụ sau đây khai báo một biến không có, ban đầu là 5. Biến được truyền dưới dạng tham số (theo giá trị) cho mutate_no_to_zero()functionnction, thay đổi giá trị thành 0. Sau khi gọi hàm khi điều khiển quay trở lại phương thức chính, giá trị sẽ giống nhau.

fn main(){
   let no:i32 = 5;
   mutate_no_to_zero(no);
   println!("The value of no is:{}",no);
}

fn mutate_no_to_zero(mut param_no: i32) {
   param_no = param_no*0;
   println!("param_no value is :{}",param_no);
}

Đầu ra

param_no value is :0
The value of no is:5

Chuyển qua tài liệu tham khảo

Khi bạn chuyển tham số bằng tham chiếu, không giống như tham số giá trị, vị trí lưu trữ mới không được tạo cho các tham số này. Các tham số tham chiếu đại diện cho cùng một vị trí bộ nhớ với các tham số thực được cung cấp cho phương thức. Các giá trị tham số có thể được chuyển qua tham chiếu bằng cách đặt trước tên biến bằng một& .

Trong ví dụ dưới đây, chúng tôi có một biến không có , đó là ban đầu 5. Một tham chiếu đến biến không được truyền chomutate_no_to_zero()chức năng. Hàm hoạt động trên biến gốc. Sau lời gọi hàm, khi điều khiển quay trở lại phương thức chính, giá trị của biến ban đầu sẽ là số không.

fn main() {
   let mut no:i32 = 5;
   mutate_no_to_zero(&mut no);
   println!("The value of no is:{}",no);
}
fn mutate_no_to_zero(param_no:&mut i32){
   *param_no = 0; //de reference
}

Toán tử * được sử dụng để truy cập giá trị được lưu trữ trong vị trí bộ nhớ mà biến param_nochỉ tới. Đây còn được gọi là hội nghị truyền hình.

Đầu ra sẽ là -

The value of no is 0.

Truyền chuỗi cho một hàm

Hàm main () chuyển một đối tượng chuỗi vào hàm display () .

fn main(){
   let name:String = String::from("TutorialsPoint");
   display(name); 
   //cannot access name after display
}
fn display(param_name:String){
   println!("param_name value is :{}",param_name);
}

Đầu ra

param_name value is :TutorialsPoint

Tuple là một kiểu dữ liệu phức hợp. Kiểu vô hướng chỉ có thể lưu trữ một kiểu dữ liệu. Ví dụ, một biến i32 chỉ có thể lưu trữ một giá trị nguyên duy nhất. Trong các loại phức hợp, chúng ta có thể lưu trữ nhiều giá trị cùng một lúc và nó có thể thuộc nhiều loại khác nhau.

Tuples có chiều dài cố định - một khi được khai báo chúng không thể phát triển hoặc thu nhỏ kích thước. Chỉ mục tuple bắt đầu từ0.

Cú pháp

//Syntax1
let tuple_name:(data_type1,data_type2,data_type3) = (value1,value2,value3);

//Syntax2
let tuple_name = (value1,value2,value3);

Hình minh họa

Ví dụ sau đây hiển thị các giá trị trong một bộ giá trị.

fn main() {
   let tuple:(i32,f64,u8) = (-325,4.9,22);
   println!("{:?}",tuple);
}

Các println! ( "{}", Tuple) cú pháp không thể được dùng để hiển thị giá trị trong một tuple. Điều này là do tuple là một loại hợp chất. Sử dụng cú pháp println! ("{:?}", Tuple_name) để in các giá trị trong một bộ giá trị.

Đầu ra

(-325, 4.9, 22)

Hình minh họa

Ví dụ sau in các giá trị riêng lẻ trong một bộ.

fn main() {
   let tuple:(i32,f64,u8) = (-325,4.9,22);
   println!("integer is :{:?}",tuple.0);
   println!("float is :{:?}",tuple.1);
   println!("unsigned integer is :{:?}",tuple.2);
}

Đầu ra

integer is :-325
float is :4.9
unsigned integer is :2

Hình minh họa

Ví dụ sau chuyển một tuple dưới dạng tham số cho một hàm. Các bộ giá trị được truyền theo giá trị cho các hàm.

fn main(){
   let b:(i32,bool,f64) = (110,true,10.9);
   print(b);
}
//pass the tuple as a parameter

fn print(x:(i32,bool,f64)){
   println!("Inside print method");
   println!("{:?}",x);
}

Đầu ra

Inside print method
(110, true, 10.9)

Phá hủy

Hủy chuyển nhượng là một đặc điểm của gỉ trong đó chúng ta giải nén các giá trị của một bộ giá trị. Điều này đạt được bằng cách gán một bộ giá trị cho các biến riêng biệt.

Hãy xem xét ví dụ sau:

fn main(){
   let b:(i32,bool,f64) = (30,true,7.9);
   print(b);
}
fn print(x:(i32,bool,f64)){
   println!("Inside print method");
   let (age,is_male,cgpa) = x; //assigns a tuple to 
   distinct variables
   println!("Age is {} , isMale? {},cgpa is 
   {}",age,is_male,cgpa);
}

Biến x là một bộ giá trị được gán cho câu lệnh let. Mỗi biến - age, is_male và cgpa sẽ chứa các giá trị tương ứng trong một bộ giá trị.

Đầu ra

Inside print method
Age is 30 , isMale? true,cgpa is 7.9

Trong chương này, chúng ta sẽ tìm hiểu về một mảng và các tính năng khác nhau liên quan đến nó. Trước khi tìm hiểu về mảng, chúng ta hãy xem mảng khác với một biến như thế nào.

Các biến có những hạn chế sau:

  • Các biến có bản chất vô hướng. Nói cách khác, một khai báo biến chỉ có thể chứa một giá trị duy nhất tại một thời điểm. Điều này có nghĩa là để lưu trữ n giá trị trong một chương trình sẽ cần khai báo n biến. Do đó, việc sử dụng các biến là không khả thi khi một người cần lưu trữ một bộ sưu tập giá trị lớn hơn.

  • Các biến trong chương trình được cấp phát bộ nhớ theo thứ tự ngẫu nhiên, do đó gây khó khăn cho việc truy xuất / đọc các giá trị theo thứ tự khai báo của chúng.

Mảng là một tập hợp các giá trị đồng nhất. Nói một cách đơn giản, mảng là tập hợp các giá trị của cùng một kiểu dữ liệu.

Các tính năng của một mảng

Các tính năng của một mảng được liệt kê dưới đây:

  • Khai báo mảng cấp phát các khối bộ nhớ tuần tự.

  • Mảng là tĩnh. Điều này có nghĩa là một mảng sau khi được khởi tạo sẽ không thể thay đổi kích thước.

  • Mỗi khối bộ nhớ đại diện cho một phần tử mảng.

  • Các phần tử của mảng được xác định bởi một số nguyên duy nhất được gọi là chỉ số con / chỉ số của phần tử.

  • Việc điền các phần tử mảng được gọi là khởi tạo mảng.

  • Các giá trị phần tử mảng có thể được cập nhật hoặc sửa đổi nhưng không thể bị xóa.

Khai báo và Khởi tạo Mảng

Sử dụng cú pháp dưới đây để khai báo và khởi tạo một mảng trong Rust.

Cú pháp

//Syntax1
let variable_name = [value1,value2,value3];

//Syntax2
let variable_name:[dataType;size] = [value1,value2,value3];

//Syntax3
let variable_name:[dataType;size] = [default_value_for_elements,size];

Trong cú pháp đầu tiên, kiểu của mảng được suy ra từ kiểu dữ liệu của phần tử đầu tiên của mảng trong quá trình khởi tạo.

Minh họa: Mảng đơn giản

Ví dụ sau chỉ định rõ ràng kích thước và kiểu dữ liệu của mảng. Các {:}! Cú pháp của println () chức năng được sử dụng để in tất cả các giá trị trong mảng. Hàm len () được sử dụng để tính toán kích thước của mảng.

fn main(){
   let arr:[i32;4] = [10,20,30,40];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());
}

Đầu ra

array is [10, 20, 30, 40]
array size is :4

Hình minh họa: Mảng không có kiểu dữ liệu

Chương trình sau khai báo một mảng gồm 4 phần tử. Kiểu dữ liệu không được chỉ định rõ ràng trong quá trình khai báo biến. Trong trường hợp này, mảng sẽ có kiểu số nguyên. Hàm len () được sử dụng để tính toán kích thước của mảng.

fn main(){
   let arr = [10,20,30,40];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());
}

Đầu ra

array is [10, 20, 30, 40]
array size is :4

Minh họa: Giá trị mặc định

Ví dụ sau đây tạo một mảng và khởi tạo tất cả các phần tử của nó với giá trị mặc định là -1 .

fn main() {
   let arr:[i32;4] = [-1;4];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());
}

Đầu ra

array is [-1, -1, -1, -1]
array size is :4

Minh họa: Mảng với vòng lặp for

Ví dụ sau lặp qua một mảng và in các chỉ mục và giá trị tương ứng của chúng. Vòng lặp truy xuất các giá trị từ chỉ số 0 đến 4 (chỉ số của phần tử mảng cuối cùng).

fn main(){
   let arr:[i32;4] = [10,20,30,40];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());

   for index in 0..4 {
      println!("index is: {} & value is : {}",index,arr[index]);
   }
}

Đầu ra

array is [10, 20, 30, 40]
array size is :4
index is: 0 & value is : 10
index is: 1 & value is : 20
index is: 2 & value is : 30
index is: 3 & value is : 40

Minh họa: Sử dụng hàm iter ()

Hàm iter () tìm nạp các giá trị của tất cả các phần tử trong một mảng.

fn main(){

let arr:[i32;4] = [10,20,30,40];
   println!("array is {:?}",arr);
   println!("array size is :{}",arr.len());

   for val in arr.iter(){
      println!("value is :{}",val);
   }
}

Đầu ra

array is [10, 20, 30, 40]
array size is :4
value is :10
value is :20
value is :30
value is :40

Minh họa: Mảng có thể thay đổi

Các mứt từ khóa có thể được sử dụng để khai báo một mảng có thể thay đổi. Ví dụ sau khai báo một mảng có thể thay đổi và sửa đổi giá trị của phần tử mảng thứ hai.

fn main(){
   let mut arr:[i32;4] = [10,20,30,40];
   arr[1] = 0;
   println!("{:?}",arr);
}

Đầu ra

[10, 0, 30, 40]

Truyền Mảng dưới dạng Tham số cho Hàm

Một mảng có thể được truyền theo giá trị hoặc tham chiếu đến các hàm.

Minh họa: Truyền theo giá trị

fn main() {
   let arr = [10,20,30];
   update(arr);

   print!("Inside main {:?}",arr);
}
fn update(mut arr:[i32;3]){
   for i in 0..3 {
      arr[i] = 0;
   }
   println!("Inside update {:?}",arr);
}

Đầu ra

Inside update [0, 0, 0]
Inside main [10, 20, 30]

Minh họa: Qua tham khảo

fn main() {
   let mut arr = [10,20,30];
   update(&mut arr);
   print!("Inside main {:?}",arr);
}
fn update(arr:&mut [i32;3]){
   for i in 0..3 {
      arr[i] = 0;
   }
   println!("Inside update {:?}",arr);
}

Đầu ra

Inside update [0, 0, 0]
Inside main [0, 0, 0]

Khai báo mảng và hằng số

Chúng ta hãy xem xét một ví dụ dưới đây để hiểu khai báo mảng và các hằng số.

fn main() {
   let N: usize = 20;
   let arr = [0; N]; //Error: non-constant used with constant
   print!("{}",arr[10])
}

Trình biên dịch sẽ dẫn đến một ngoại lệ. Điều này là do độ dài của một mảng phải được biết tại thời điểm biên dịch. Ở đây, giá trị của biến "N" sẽ được xác định trong thời gian chạy. Nói cách khác, các biến không thể được sử dụng để xác định kích thước của một mảng.

Tuy nhiên, chương trình sau hợp lệ:

fn main() {
   const N: usize = 20; 
   // pointer sized
   let arr = [0; N];

   print!("{}",arr[10])
}

Giá trị của mã định danh có tiền tố từ khóa const được xác định tại thời điểm biên dịch và không thể thay đổi trong thời gian chạy. usize có kích thước bằng con trỏ, do đó kích thước thực của nó phụ thuộc vào kiến ​​trúc mà bạn đang biên dịch chương trình của mình.

Bộ nhớ cho một chương trình có thể được cấp phát như sau:

  • Stack
  • Heap

Cây rơm

Một ngăn xếp theo sau một cuối cùng trong thứ tự đầu tiên. Stack lưu trữ các giá trị dữ liệu mà kích thước đã biết tại thời điểm biên dịch. Ví dụ, một biến có kích thước cố định i32 là một ứng cử viên để phân bổ ngăn xếp. Kích thước của nó được biết tại thời điểm biên dịch. Tất cả các loại vô hướng có thể được lưu trữ trong ngăn xếp khi kích thước được cố định.

Hãy xem xét một ví dụ về một chuỗi, được gán một giá trị trong thời gian chạy. Kích thước chính xác của một chuỗi như vậy không thể được xác định tại thời điểm biên dịch. Vì vậy, nó không phải là một ứng cử viên cho phân bổ ngăn xếp mà là phân bổ đống.

Đống

Bộ nhớ heap lưu trữ các giá trị dữ liệu có kích thước không xác định tại thời điểm biên dịch. Nó được sử dụng để lưu trữ dữ liệu động. Nói một cách đơn giản, bộ nhớ heap được cấp phát cho các giá trị dữ liệu có thể thay đổi trong suốt vòng đời của chương trình. Heap là một vùng trong bộ nhớ ít được tổ chức hơn khi so sánh với stack.

Quyền sở hữu là gì?

Mỗi giá trị trong Rust có một biến được gọi là ownercủa giá trị. Mọi dữ liệu được lưu trữ trong Rust sẽ có chủ sở hữu được liên kết với nó. Ví dụ, trong cú pháp - cho age = 30, age là chủ sở hữu của giá trị 30 .

  • Mỗi dữ liệu chỉ có thể có một chủ sở hữu tại một thời điểm.

  • Hai biến không thể trỏ đến cùng một vị trí bộ nhớ. Các biến sẽ luôn trỏ đến các vị trí bộ nhớ khác nhau.

Chuyển quyền sở hữu

Quyền sở hữu giá trị có thể được chuyển giao bởi -

  • Gán giá trị của một biến này cho một biến khác.

  • Truyền giá trị cho một hàm.

  • Trả về giá trị từ một hàm.

Gán giá trị của một biến cho một biến khác

Điểm bán hàng chính của Rust as a language là tính an toàn về bộ nhớ của nó. Sự an toàn của bộ nhớ đạt được bằng cách kiểm soát chặt chẽ những người có thể sử dụng những gì và khi các hạn chế.

Hãy xem xét đoạn mã sau -

fn main(){
   let v = vec![1,2,3]; 
   // vector v owns the object in heap

   //only a single variable owns the heap memory at any given time
   let v2 = v; 
   // here two variables owns heap value,
   //two pointers to the same content is not allowed in rust

   //Rust is very smart in terms of memory access ,so it detects a race condition
   //as two variables point to same heap

   println!("{:?}",v);
}

Ví dụ trên khai báo một vectơ v. Ý tưởng về quyền sở hữu là chỉ một biến liên kết với một tài nguyên, v liên kết với tài nguyên hoặc v2liên kết với tài nguyên. Ví dụ trên đưa ra một lỗi - sử dụng giá trị đã di chuyển: `v` . Điều này là do quyền sở hữu tài nguyên được chuyển sang v2. Nó có nghĩa là quyền sở hữu được chuyển từ v sang v2 (v2 = v) và v sẽ bị vô hiệu sau khi di chuyển.

Truyền giá trị cho một hàm

Quyền sở hữu của một giá trị cũng thay đổi khi chúng ta chuyển một đối tượng trong heap tới một hàm hoặc một hàm.

fn main(){
   let v = vec![1,2,3];     // vector v owns the object in heap
   let v2 = v;              // moves ownership to v2
   display(v2);             // v2 is moved to display and v2 is invalidated
   println!("In main {:?}",v2);    //v2 is No longer usable here
}
fn display(v:Vec<i32>){
   println!("inside display {:?}",v);
}

Trả về giá trị từ một hàm

Quyền sở hữu được chuyển cho hàm sẽ bị vô hiệu khi quá trình thực thi hàm hoàn tất. Một cách giải quyết vấn đề này là cho phép hàm trả lại đối tượng sở hữu trở lại trình gọi.

fn main(){
   let v = vec![1,2,3];       // vector v owns the object in heap
   let v2 = v;                // moves ownership to v2
   let v2_return = display(v2);    
   println!("In main {:?}",v2_return);
}
fn display(v:Vec<i32>)->Vec<i32> { 
   // returning same vector
   println!("inside display {:?}",v);
}

Quyền sở hữu và các kiểu nguyên thủy

Trong trường hợp kiểu nguyên thủy, nội dung từ biến này được sao chép sang biến khác. Vì vậy, không có động thái sở hữu nào xảy ra. Điều này là do một biến nguyên thủy cần ít tài nguyên hơn một đối tượng. Hãy xem xét ví dụ sau:

fn main(){
   let u1 = 10;
   let u2 = u1;  // u1 value copied(not moved) to u2

   println!("u1 = {}",u1);
}

Đầu ra sẽ là - 10.

Rất bất tiện khi chuyển quyền sở hữu của một biến cho một hàm khác và sau đó trả lại quyền sở hữu. Rust hỗ trợ một khái niệm, vay mượn, trong đó quyền sở hữu giá trị được chuyển tạm thời cho một thực thể và sau đó trả lại cho thực thể chủ sở hữu ban đầu.

Hãy xem xét những điều sau -

fn main(){
   // a list of nos
   let v = vec![10,20,30];
   print_vector(v);
   println!("{}",v[0]); // this line gives error
}
fn print_vector(x:Vec<i32>){
   println!("Inside print_vector function {:?}",x);
}

Hàm main gọi một hàm print_vector () . Một vectơ được chuyển làm tham số cho hàm này. Quyền sở hữu vectơ cũng được chuyển cho hàm print_vector () từ hàm main () . Đoạn mã trên sẽ dẫn đến lỗi như hình dưới đây khi hàm main () cố gắng truy cập vào vector v .

|  print_vector(v);
|     - value moved here
|  println!("{}",v[0]);
|     ^ value used here after move

Điều này là do một biến hoặc giá trị không còn có thể được sử dụng bởi hàm đã sở hữu nó ban đầu khi quyền sở hữu được chuyển sang một hàm khác.

Vay mượn là gì?

Khi một hàm tạm thời chuyển quyền kiểm soát của nó đối với một biến / giá trị sang một hàm khác, trong một thời gian, nó được gọi là mượn. Điều này đạt được bằng cách chuyển một tham chiếu đến biến(& var_name)chứ không phải truyền chính biến / giá trị vào hàm. Quyền sở hữu của biến / giá trị được chuyển cho chủ sở hữu ban đầu của biến sau khi hàm mà điều khiển được chuyển đến hoàn tất quá trình thực thi.

fn main(){
   // a list of nos
   let v = vec![10,20,30];
   print_vector(&v); // passing reference
   println!("Printing the value from main() v[0]={}",v[0]);
}
fn print_vector(x:&Vec<i32>){
   println!("Inside print_vector function {:?}",x);
}

Đầu ra

Inside print_vector function [10, 20, 30]
Printing the value from main() v[0] = 10

Tham chiếu có thể thay đổi

Một hàm có thể sửa đổi một tài nguyên đã mượn bằng cách sử dụng một tham chiếu có thể thay đổi đến tài nguyên đó. Tham chiếu có thể thay đổi được bắt đầu bằng&mut. Các tham chiếu có thể thay đổi chỉ có thể hoạt động trên các biến có thể thay đổi.

Minh họa: Thay đổi tham chiếu số nguyên

fn add_one(e: &mut i32) {
   *e+= 1;
}
fn main() {
   let mut i = 3;
   add_one(&mut i);
   println!("{}", i);
}

Hàm main () khai báo một biến số nguyên có thể thay đổi i và chuyển một tham chiếu có thể thay đổi của i đếnadd_one(). Add_one () tăng giá trị của biến i lên một.

Minh họa: Thay đổi tham chiếu chuỗi

fn main() {
   let mut name:String = String::from("TutorialsPoint");
   display(&mut name); 
   //pass a mutable reference of name
   println!("The value of name after modification is:{}",name);
}
fn display(param_name:&mut String){
   println!("param_name value is :{}",param_name);
   param_name.push_str(" Rocks"); 
   //Modify the actual string,name
}

Hàm main () chuyển một tham chiếu có thể thay đổi của tên biến đến hàm display () . Hàm hiển thị thêm một chuỗi bổ sung vào biến tên gốc .

Đầu ra

param_name value is :TutorialsPoint
The value of name after modification is:TutorialsPoint Rocks

Một lát cắt là một con trỏ đến một khối bộ nhớ. Slices có thể được sử dụng để truy cập các phần dữ liệu được lưu trữ trong các khối bộ nhớ liền kề. Nó có thể được sử dụng với các cấu trúc dữ liệu như mảng, vectơ và chuỗi. Slices sử dụng số chỉ mục để truy cập các phần dữ liệu. Kích thước của một lát cắt được xác định trong thời gian chạy.

Slices là con trỏ đến dữ liệu thực tế. Chúng được chuyển qua tham chiếu đến các hàm, còn được gọi là sự vay mượn.

Ví dụ, các lát cắt có thể được sử dụng để tìm nạp một phần của giá trị chuỗi. Một chuỗi được cắt lát là một con trỏ đến đối tượng chuỗi thực. Do đó, chúng ta cần xác định chỉ mục bắt đầu và kết thúc của một Chuỗi. Chỉ mục bắt đầu từ 0 giống như mảng.

Cú pháp

let sliced_value = &data_structure[start_index..end_index]

Giá trị chỉ mục tối thiểu là 0 và giá trị chỉ mục lớn nhất là kích thước của cấu trúc dữ liệu. LƯU Ý rằng end_index sẽ không được bao gồm trong chuỗi cuối cùng.

Sơ đồ dưới đây cho thấy một chuỗi mẫu Hướng dẫn , có 9 ký tự. Chỉ số của ký tự đầu tiên là 0 và của ký tự cuối cùng là 8.

Đoạn mã sau tìm nạp 5 ký tự từ chuỗi (bắt đầu từ chỉ mục 4).

fn main() {
   let n1 = "Tutorials".to_string();
   println!("length of string is {}",n1.len());
   let c1 = &n1[4..9]; 
   
   // fetches characters at 4,5,6,7, and 8 indexes
   println!("{}",c1);
}

Đầu ra

length of string is 9
rials

Hình minh họa - Cắt một mảng số nguyên

Hàm main () khai báo một mảng có 5 phần tử. Nó gọiuse_slice()và chuyển cho nó một lát gồm ba phần tử (trỏ tới mảng dữ liệu). Các lát cắt được thông qua bằng tham chiếu. Hàm use_slice () in ra giá trị của lát cắt và độ dài của nó.

fn main(){
   let data = [10,20,30,40,50];
   use_slice(&data[1..4]);
   //this is effectively borrowing elements for a while
}
fn use_slice(slice:&[i32]) { 
   // is taking a slice or borrowing a part of an array of i32s
   println!("length of slice is {:?}",slice.len());
   println!("{:?}",slice);
}

Đầu ra

length of slice is 3
[20, 30, 40]

Các lát cắt có thể thay đổi

Các &mut từ khóa có thể được sử dụng để đánh dấu một lát là có thể thay đổi.

fn main(){
   let mut data = [10,20,30,40,50];
   use_slice(&mut data[1..4]);
   // passes references of 
   20, 30 and 40
   println!("{:?}",data);
}
fn use_slice(slice:&mut [i32]) {
   println!("length of slice is {:?}",slice.len());
   println!("{:?}",slice);
   slice[0] = 1010; // replaces 20 with 1010
}

Đầu ra

length of slice is 3
[20, 30, 40]
[10, 1010, 30, 40, 50]

Đoạn mã trên chuyển một lát có thể thay đổi đến hàm use_slice () . Hàm sửa đổi phần tử thứ hai của mảng ban đầu.

Mảng được sử dụng để biểu diễn một tập hợp các giá trị đồng nhất. Tương tự, một cấu trúc là một kiểu dữ liệu do người dùng xác định khác có sẵn trong Rust cho phép chúng ta kết hợp các mục dữ liệu thuộc các kiểu khác nhau, bao gồm cả một cấu trúc khác. Một cấu trúc xác định dữ liệu là một cặp khóa-giá trị.

Cú pháp - Khai báo cấu trúc

Các struct từ khóa được sử dụng để khai báo một cấu trúc. Vì cấu trúc được nhập tĩnh nên mọi trường trong cấu trúc phải được liên kết với một kiểu dữ liệu. Các quy tắc và quy ước đặt tên cho một cấu trúc giống như của một biến. Khối cấu trúc phải kết thúc bằng dấu chấm phẩy.

struct Name_of_structure {
   field1:data_type,
   field2:data_type,
   field3:data_type
}

Cú pháp - Khởi tạo cấu trúc

Sau khi khai báo một cấu trúc, mỗi trường sẽ được gán một giá trị. Điều này được gọi là khởi tạo.

let instance_name = Name_of_structure {
   field1:value1,
   field2:value2,
   field3:value3
}; 
//NOTE the semicolon
Syntax: Accessing values in a structure
Use the dot notation to access value of a specific field.
instance_name.field1
Illustration
struct Employee {
   name:String,
   company:String,
   age:u32
}
fn main() {
   let emp1 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   println!("Name is :{} company is {} age is {}",emp1.name,emp1.company,emp1.age);
}

Ví dụ trên khai báo một struct Employee với ba trường - tên, công ty và tuổi của các loại. Main () khởi tạo cấu trúc. Nó sử dụng println! macro để in giá trị của các trường được xác định trong cấu trúc.

Đầu ra

Name is :Mohtashim company is TutorialsPoint age is 50

Sửa đổi một phiên bản cấu trúc

Để sửa đổi một phiên bản, biến phiên bản phải được đánh dấu là có thể thay đổi được. Ví dụ dưới đây khai báo và khởi tạo một cấu trúc có tên là Employee và sau đó sửa đổi giá trị của trường age thành 40 từ 50.

let mut emp1 = Employee {
   company:String::from("TutorialsPoint"),
   name:String::from("Mohtashim"),
   age:50
};
emp1.age = 40;
println!("Name is :{} company is {} age is 
{}",emp1.name,emp1.company,emp1.age);

Đầu ra

Name is :Mohtashim company is TutorialsPoint age is 40

Truyền một cấu trúc cho một hàm

Ví dụ sau đây cho thấy cách chuyển thể hiện của struct dưới dạng tham số. Phương thức hiển thị lấy một cá thể Employee làm tham số và in các chi tiết.

fn display( emp:Employee) {
   println!("Name is :{} company is {} age is 
   {}",emp.name,emp.company,emp.age);
}

Đây là chương trình hoàn chỉnh -

//declare a structure
struct Employee {
   name:String,
   company:String,
   age:u32
}
fn main() {
   //initialize a structure
   let emp1 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   let emp2 = Employee{
      company:String::from("TutorialsPoint"),
      name:String::from("Kannan"),
      age:32
   };
   //pass emp1 and emp2 to display()
   display(emp1);
   display(emp2);
}
// fetch values of specific structure fields using the 
// operator and print it to the console
fn display( emp:Employee){
   println!("Name is :{} company is {} age is 
   {}",emp.name,emp.company,emp.age);
}

Đầu ra

Name is :Mohtashim company is TutorialsPoint age is 50
Name is :Kannan company is TutorialsPoint age is 32

Trả về cấu trúc từ một hàm

Chúng ta hãy xem xét một hàm who_is_elder () , so sánh tuổi của hai nhân viên và trả về tuổi của nhân viên.

fn who_is_elder (emp1:Employee,emp2:Employee)->Employee {
   if emp1.age>emp2.age {
      return emp1;
   } else {
      return emp2;
   }
}

Đây là chương trình hoàn chỉnh -

fn main() {
   //initialize structure
   let emp1 = Employee{
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   let emp2 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Kannan"),
      age:32
   };
   let elder = who_is_elder(emp1,emp2);
   println!("elder is:");

   //prints details of the elder employee
   display(elder);
}
//accepts instances of employee structure and compares their age
fn who_is_elder (emp1:Employee,emp2:Employee)->Employee {
   if emp1.age>emp2.age {
      return emp1;
   } else {
      return emp2;
   }
}
//display name, comapny and age of the employee
fn display( emp:Employee) {
   println!("Name is :{} company is {} age is {}",emp.name,emp.company,emp.age);
}
//declare a structure
struct Employee {
   name:String,
   company:String,
   age:u32
}

Đầu ra

elder is:
Name is :Mohtashim company is TutorialsPoint age is 50

Phương pháp trong cấu trúc

Các phương thức giống như các hàm. Chúng là một nhóm các hướng dẫn lập trình hợp lý. Các phương thức được khai báo vớifntừ khóa. Phạm vi của một phương thức nằm trong khối cấu trúc.

Các phương thức được khai báo bên ngoài khối cấu trúc. Cácimpltừ khóa được sử dụng để xác định một phương thức trong ngữ cảnh của một cấu trúc. Tham số đầu tiên của một phương thức sẽ luôn làself, đại diện cho phiên bản gọi của cấu trúc. Các phương thức hoạt động trên các thành viên dữ liệu của một cấu trúc.

Để gọi một phương thức, trước tiên chúng ta cần khởi tạo cấu trúc. Phương thức có thể được gọi bằng thể hiện của cấu trúc.

Cú pháp

struct My_struct {}
impl My_struct { 
   //set the method's context
   fn method_name() { 
      //define a method
   }
}

Hình minh họa

Ví dụ sau định nghĩa cấu trúc Hình chữ nhật với các trường - chiều rộngchiều cao . Một vùng phương thức được xác định trong ngữ cảnh của cấu trúc. Phương thức area truy cập các trường của cấu trúc thông qua từ khóa self và tính diện tích hình chữ nhật.

//define dimensions of a rectangle
struct Rectangle {
   width:u32, height:u32
}

//logic to calculate area of a rectangle
impl Rectangle {
   fn area(&self)->u32 {
      //use the . operator to fetch the value of a field via the self keyword
      self.width * self.height
   }
}

fn main() {
   // instanatiate the structure
   let small = Rectangle {
      width:10,
      height:20
   };
   //print the rectangle's area
   println!("width is {} height is {} area of Rectangle 
   is {}",small.width,small.height,small.area());
}

Đầu ra

width is 10 height is 20 area of Rectangle is 200

Phương pháp tĩnh trong cấu trúc

Các phương thức tĩnh có thể được sử dụng như các phương thức tiện ích. Các phương thức này tồn tại ngay cả trước khi cấu trúc được khởi tạo. Các phương thức tĩnh được gọi bằng cách sử dụng tên của cấu trúc và có thể được truy cập mà không cần thể hiện. Không giống như các phương thức thông thường, một phương thức tĩnh sẽ không nhận tham số & self .

Cú pháp - Khai báo một phương thức tĩnh

Một phương thức tĩnh giống như các hàm và các phương thức khác có thể tùy chọn chứa các tham số.

impl Structure_Name {
   //static method that creates objects of the Point structure
   fn method_name(param1: datatype, param2: datatype) -> return_type {
      // logic goes here
   }
}

Cú pháp - Gọi một phương thức tĩnh

Các structure_name :: cú pháp được sử dụng để truy cập vào một phương pháp tĩnh.

structure_name::method_name(v1,v2)

Hình minh họa

Ví dụ sau sử dụng phương thức getInstance như một lớp nhà máy tạo và trả về các thể hiện của cấu trúc Point .

//declare a structure
struct Point {
   x: i32,
   y: i32,
}
impl Point {
   //static method that creates objects of the Point structure
   fn getInstance(x: i32, y: i32) -> Point {
      Point { x: x, y: y }
   }
   //display values of the structure's field
   fn display(&self){
      println!("x ={} y={}",self.x,self.y );
   }
}
fn main(){
   // Invoke the static method
   let p1 = Point::getInstance(10,20);
   p1.display();
}

Đầu ra

x =10 y=20

Trong lập trình Rust, khi chúng ta phải chọn một giá trị từ danh sách các biến thể có thể có, chúng ta sử dụng kiểu dữ liệu liệt kê. Một kiểu liệt kê được khai báo bằng từ khóa enum . Sau đây là cú pháp của enum:

enum enum_name {
   variant1,
   variant2,
   variant3
}

Minh họa: Sử dụng phép liệt kê

Ví dụ khai báo một enum - GenderCategory , có các biến thể là Nam và Nữ. Bản in! macro hiển thị giá trị của enum. Trình biên dịch sẽ báo lỗi rằng đặc điểm std :: fmt :: Debug không được triển khai cho GenderCategory . Thuộc tính # [dẫn xuất (Gỡ lỗi)] được sử dụng để loại bỏ lỗi này.

// The `derive` attribute automatically creates the implementation
// required to make this `enum` printable with `fmt::Debug`.
#[derive(Debug)]
enum GenderCategory {
   Male,Female
}
fn main() {
   let male = GenderCategory::Male;
   let female = GenderCategory::Female;

   println!("{:?}",male);
   println!("{:?}",female);
}

Đầu ra

Male
Female

Struct và Enum

Ví dụ sau đây định nghĩa một cấu trúc Person. Trường giới tính thuộc loại GenderCategory (là một enum) và có thể được chỉ định Nam hoặc Nữ làm giá trị.

// The `derive` attribute automatically creates the 
implementation
// required to make this `enum` printable with 
`fmt::Debug`.

#[derive(Debug)]
enum GenderCategory {
   Male,Female
}

// The `derive` attribute automatically creates the implementation
// required to make this `struct` printable with `fmt::Debug`.
#[derive(Debug)]
struct Person {
   name:String,
   gender:GenderCategory
}

fn main() {
   let p1 = Person {
      name:String::from("Mohtashim"),
      gender:GenderCategory::Male
   };
   let p2 = Person {
      name:String::from("Amy"),
      gender:GenderCategory::Female
   };
   println!("{:?}",p1);
   println!("{:?}",p2);
}

Ví dụ này tạo các đối tượng p1p2 kiểu Person và khởi tạo các thuộc tính, tên và giới tính cho từng đối tượng này.

Đầu ra

Person { name: "Mohtashim", gender: Male }
Person { name: "Amy", gender: Female }

Tùy chọn Enum

Option là một enum được xác định trước trong thư viện chuẩn Rust. Enum này có hai giá trị - Some (dữ liệu) và None.

Cú pháp

enum Option<T> {
   Some(T),      //used to return a value
   None          // used to return null, as Rust doesn't support 
   the null keyword
}

Ở đây, kiểu T đại diện cho giá trị của bất kỳ kiểu nào.

Rust không hỗ trợ từ khóa null . Giá trị None , trong enumOption , có thể được sử dụng bởi một hàm để trả về giá trị null. Nếu có dữ liệu để trả về, hàm có thể trả về Một số (dữ liệu) .

Hãy để chúng tôi hiểu điều này với một ví dụ -

Chương trình định nghĩa một hàm is_even () , với kiểu trả về là Option. Hàm xác minh xem giá trị được truyền là số chẵn hay không. Nếu đầu vào là chẵn thì trả về giá trị true, còn lại thì hàm trả về Không có .

fn main() {
   let result = is_even(3);
   println!("{:?}",result);
   println!("{:?}",is_even(30));
}
fn is_even(no:i32)->Option<bool> {
   if no%2 == 0 {
      Some(true)
   } else {
      None
   }
}

Đầu ra

None
Some(true)

Câu lệnh đối sánh và Enum

Câu lệnh so khớp có thể được sử dụng để so sánh các giá trị được lưu trữ trong một enum. Ví dụ sau định nghĩa một hàm, print_size , lấy CarType enum làm tham số. Hàm so sánh các giá trị tham số với một tập hợp hằng số được xác định trước và hiển thị thông báo thích hợp.

enum CarType {
   Hatch,
   Sedan,
   SUV
}
fn print_size(car:CarType) {
   match car {
      CarType::Hatch => {
         println!("Small sized car");
      },
      CarType::Sedan => {
         println!("medium sized car");
      },
      CarType::SUV =>{
         println!("Large sized Sports Utility car");
      }
   }
}
fn main(){
   print_size(CarType::SUV);
   print_size(CarType::Hatch);
   print_size(CarType::Sedan);
}

Đầu ra

Large sized Sports Utility car
Small sized car
medium sized car

Phù hợp với Tùy chọn

Ví dụ về hàm is_even , trả về kiểu Option, cũng có thể được triển khai với câu lệnh so khớp như hình dưới đây:

fn main() {
   match is_even(5) {
      Some(data) => {
         if data==true {
            println!("Even no");
         }
      },
      None => {
         println!("not even");
      }
   }
}
fn is_even(no:i32)->Option<bool> {
   if no%2 == 0 {
      Some(true)
   } else {
      None
   }
}

Đầu ra

not even

Đối sánh & Enum với Loại dữ liệu

Có thể thêm kiểu dữ liệu vào từng biến thể của enum. Trong ví dụ sau, các biến thể Tên và Usr_ID của enum tương ứng là kiểu Chuỗi và kiểu số nguyên. Ví dụ sau cho thấy việc sử dụng câu lệnh so khớp với một enum có kiểu dữ liệu.

// The `derive` attribute automatically creates the implementation
// required to make this `enum` printable with `fmt::Debug`.
#[derive(Debug)]
enum GenderCategory {
   Name(String),Usr_ID(i32)
}
fn main() {
   let p1 = GenderCategory::Name(String::from("Mohtashim"));
   let p2 = GenderCategory::Usr_ID(100);
   println!("{:?}",p1);
   println!("{:?}",p2);

   match p1 {
      GenderCategory::Name(val)=> {
         println!("{}",val);
      }
      GenderCategory::Usr_ID(val)=> {
         println!("{}",val);
      }
   }
}

Đầu ra

Name("Mohtashim")
Usr_ID(100)
Mohtashim

Một nhóm mã logic được gọi là Mô-đun. Nhiều mô-đun được biên dịch thành một đơn vị được gọi làcrate. Các chương trình gỉ có thể chứa thùng nhị phân hoặc thùng thư viện. Thùng nhị phân là một dự án thực thi có phương thức main () . Thùng thư viện là một nhóm các thành phần có thể được sử dụng lại trong các dự án khác. Không giống như thùng nhị phân, thùng thư viện không có điểm vào (phương thức main ()). Công cụ Cargo được sử dụng để quản lý các thùng trong Rust. Ví dụ, mô-đun mạng chứa các chức năng liên quan đến mạng và mô-đun đồ họa chứa các chức năng liên quan đến bản vẽ. Mô-đun tương tự như không gian tên trong các ngôn ngữ lập trình khác. Các thùng của bên thứ ba có thể được tải xuống bằng hàng hóa từ crates.io .

Sr.No Thuật ngữ & Mô tả
1

crate

Là một đơn vị biên dịch trong Rust; Thùng được biên dịch sang hệ nhị phân hoặc thư viện.

2

cargo

Công cụ quản lý gói Rust chính thức cho thùng.

3

module

Nhóm mã một cách hợp lý trong một thùng.

4

crates.io

Sổ đăng ký gói Rust chính thức.

Cú pháp

//public module
pub mod a_public_module {
   pub fn a_public_function() {
      //public function
   }
   fn a_private_function() {
      //private function
   }
}
//private module
mod a_private_module {
   fn a_private_function() {
   }
}

Các mô-đun có thể là công khai hoặc riêng tư. Các thành phần trong một mô-đun riêng tư không thể được truy cập bởi các mô-đun khác. Các mô-đun trong Rust là riêng tư theo mặc định. Ngược lại, các chức năng trong một mô-đun công cộng có thể được các mô-đun khác truy cập. Mô-đun phải được bắt đầu bằngpubtừ khóa để công khai. Các chức năng trong một mô-đun công khai cũng phải được công khai.

Minh họa: Định nghĩa một Mô-đun

Ví dụ định nghĩa một mô-đun công cộng - phim . Mô-đun chứa một hàm play () chấp nhận một tham số và in giá trị của nó.

pub mod movies {
   pub fn play(name:String) {
      println!("Playing movie {}",name);
   }
}
fn main(){
   movies::play("Herold and Kumar".to_string());
}

Đầu ra

Playing movie Herold and Kumar

Sử dụng từ khóa

Các sử dụng từ khóa giúp để nhập khẩu một mô-đun công cộng.

Cú pháp

use public_module_name::function_name;

Hình minh họa

pub mod movies {
   pub fn play(name:String) {
      println!("Playing movie {}",name);
   }
}
use movies::play;
fn main(){
   play("Herold and Kumar ".to_string());
}

Đầu ra

Playing movie Herold and Kumar

Mô-đun lồng nhau

Các mô-đun cũng có thể được lồng vào nhau. Các bộ phim hài mô-đun được lồng trong tiếng anh mô-đun, đó là tiếp tục lồng trong phim mô-đun. Ví dụ dưới đây xác định một chức năng chơi bên trong mô-đun phim / tiếng anh / phim hài .

pub mod movies {
   pub mod english {
      pub mod comedy {
         pub fn play(name:String) {
            println!("Playing comedy movie {}",name);
         }
      }
   }
}
use movies::english::comedy::play; 
// importing a public module

fn main() {
   // short path syntax
   play("Herold and Kumar".to_string());
   play("The Hangover".to_string());

   //full path syntax
   movies::english::comedy::play("Airplane!".to_string());
}

Đầu ra

Playing comedy movie Herold and Kumar
Playing comedy movie The Hangover
Playing comedy movie Airplane!

Hình minh họa - Tạo thùng thư viện và tiêu thụ trong thùng nhị phân

Hãy để chúng tôi tạo một thùng thư viện có tên movie_lib, chứa một mô-đun movies. Để xây dựngmovie_lib thùng thư viện, chúng tôi sẽ sử dụng công cụ cargo.

Bước 1 - Tạo thư mục Dự án

Tạo một thư mục movie-app, theo sau là một thư mục con movie-lib . Sau khi thư mục và thư mục con được tạo, hãy tạosrcthư mục và tệp Cargo.toml trong thư mục này. Mã nguồn sẽ nằm trong thư mục src . Tạo các tệp lib.rs và phim.rs trong thư mục src. Tệp Cargo.toml sẽ chứa siêu dữ liệu của dự án như số phiên bản, tên tác giả, v.v.

Cấu trúc thư mục dự án sẽ như hình dưới đây:

movie-app
   movie-lib/
      -->Cargo.toml
      -->src/
         lib.rs
         movies.rs

Bước 2 - Chỉnh sửa tệp Cargo.toml để thêm siêu dữ liệu dự án

[package]
name = "movies_lib"
version = "0.1.0"
authors = ["Mohtashim"]

Bước 3 - Chỉnh sửa tệp lib.rs.

Thêm định nghĩa mô-đun sau vào tệp này.

pub mod movies;

Dòng trên tạo một mô-đun công khai - movies.

Bước 4 - Chỉnh sửa tệp phim.rs

Tệp này sẽ xác định tất cả các chức năng cho mô-đun phim.

pub fn play(name:String){
   println!("Playing movie {} :movies-app",name);
}

Đoạn mã trên xác định một hàm play() chấp nhận một tham số và in nó ra bảng điều khiển.

Bước 5 - Xây dựng thùng thư viện

Xây dựng ứng dụng bằng cách sử dụng cargo buildlệnh để xác minh xem thùng thư viện có được cấu trúc đúng hay không. Đảm bảo rằng bạn đang ở thư mục gốc của dự án - thư mục phim-ứng dụng. Thông báo sau sẽ được hiển thị trong thiết bị đầu cuối nếu quá trình xây dựng thành công.

D:\Rust\movie-lib> cargo build
   Compiling movies_lib v0.1.0 (file:///D:/Rust/movie-lib)
   Finished dev [unoptimized + debuginfo] target(s) in 0.67s

Bước 6 - Tạo ứng dụng thử nghiệm

Tạo một thư mục khác movie-lib-testtrong thư mục movie-app, theo sau là tệp Cargo.toml và thư mục src. Dự án này nên có phương thức chính vì đây là một thùng nhị phân, sẽ sử dụng thùng thư viện đã tạo trước đó. Tạo tệp main.rs trong thư mục src. Cấu trúc thư mục sẽ như hình.

movie-app
   movie-lib 
   // already completed

   movie-lib-test/
      -->Cargo.toml
      -->src/
         main.rs

Bước 7 - Thêm thông tin sau vào tệp Cargo.toml

[package]
name = "test_for_movie_lib"
version = "0.1.0"
authors = ["Mohtashim"]

[dependencies]
movies_lib = { path = "../movie-lib" }

NOTE- Đường dẫn đến thư mục thư viện được đặt dưới dạng phụ thuộc. Sơ đồ sau đây cho thấy nội dung của cả hai dự án.

Bước 8 - Thêm phần sau vào tệp main.rs

extern crate movies_lib;
use movies_lib::movies::play;
fn main() {
   println!("inside main of test ");
   play("Tutorialspoint".to_string())
}

Đoạn mã trên nhập một gói bên ngoài có tên là movies_lib. Kiểm tra Cargo.toml của dự án hiện tại để xác minh tên thùng.

Bước 9 - Sử dụng đóng hàng và vận chuyển hàng hóa

Chúng tôi sẽ sử dụng việc xây dựng hàng hóa và chạy hàng hóa để xây dựng dự án nhị phân và thực hiện nó như hình dưới đây -

Thư viện thu thập tiêu chuẩn của Rust cung cấp triển khai hiệu quả các cấu trúc dữ liệu lập trình mục đích chung phổ biến nhất. Chương này thảo luận về việc triển khai các tập hợp thường được sử dụng - Vector, HashMap và HashSet.

Véc tơ

Vector là một mảng có thể thay đổi kích thước. Nó lưu trữ các giá trị trong các khối bộ nhớ liền nhau. Cấu trúc được xác định trước Vec có thể được sử dụng để tạo vectơ. Một số tính năng quan trọng của Vector là:

  • Một Vector có thể phát triển hoặc thu nhỏ trong thời gian chạy.

  • Vector là một tập hợp đồng nhất.

  • Một Vector lưu trữ dữ liệu dưới dạng chuỗi các phần tử theo một thứ tự cụ thể. Mỗi phần tử trong một Vectơ được gán một số chỉ mục duy nhất. Chỉ mục bắt đầu từ 0 và tăng lên n-1 trong đó, n là kích thước của tập hợp. Ví dụ, trong tập hợp 5 phần tử, phần tử đầu tiên sẽ ở chỉ số 0 và phần tử cuối cùng sẽ ở chỉ số 4.

  • Một Vectơ sẽ chỉ nối các giá trị vào (hoặc gần) cuối. Nói cách khác, một Vector có thể được sử dụng để triển khai một ngăn xếp.

  • Bộ nhớ cho một Vector được cấp phát trong heap.

Cú pháp - Tạo một vectơ

let mut instance_name = Vec::new();

Phương thức tĩnh new () của cấu trúc Vec được sử dụng để tạo một thể hiện vectơ.

Ngoài ra, một vector cũng có thể được tạo bằng vec! vĩ mô. Cú pháp như dưới đây:

let vector_name = vec![val1,val2,val3]

Bảng sau liệt kê một số chức năng thường được sử dụng của cấu trúc Vec.

Sr.No phương pháp Chữ ký & Mô tả
1 Mới()

pub fn new()->Vect

Tạo một Vec mới, trống. Vectơ sẽ không phân bổ cho đến khi các phần tử được đẩy lên nó.

2 đẩy()

pub fn push(&mut self, value: T)

Thêm một phần tử vào phía sau một tập hợp.

3 tẩy()

pub fn remove(&mut self, index: usize) -> T

Loại bỏ và trả về phần tử tại chỉ mục vị trí trong vectơ, dịch chuyển tất cả các phần tử sau nó sang trái.

4 chứa đựng()

pub fn contains(&self, x: &T) -> bool

Trả về true nếu lát cắt chứa phần tử có giá trị đã cho.

5 len ()

pub fn len(&self) -> usize

Trả về số phần tử trong vectơ, còn được gọi là 'độ dài' của nó.

Minh họa: Tạo Vector - new ()

Để tạo một vector, chúng tôi sử dụng phương thức static new -

fn main() {
   let mut v = Vec::new();
   v.push(20);
   v.push(30);
   v.push(40);

   println!("size of vector is :{}",v.len());
   println!("{:?}",v);
}

Ví dụ trên tạo một Vector bằng phương thức tĩnh new () được định nghĩa trong cấu trúc Vec . Hàm push (val) thêm giá trị được truyền dưới dạng tham số vào bộ sưu tập. Hàm len () trả về độ dài của vectơ.

Đầu ra

size of vector is :3
[20, 30, 40]

Hình minh họa: Tạo một Vector - vec! Macro

Đoạn mã sau tạo một vectơ bằng vec! vĩ mô. Kiểu dữ liệu của vectơ được suy ra là giá trị đầu tiên được gán cho nó.

fn main() {
   let v = vec![1,2,3];
   println!("{:?}",v);
}

Đầu ra

[1, 2, 3]

Như đã đề cập trước đó, một vectơ chỉ có thể chứa các giá trị của cùng một kiểu dữ liệu. Đoạn mã sau sẽ gây ra lỗi [E0308]: lỗi loại không khớp .

fn main() {
   let v = vec![1,2,3,"hello"];
   println!("{:?}",v);
}

Hình minh họa: push ()

Thêm một phần tử vào cuối tập hợp.

fn main() {
   let mut v = Vec::new();
   v.push(20);
   v.push(30);
   v.push(40);
   
   println!("{:?}",v);
}

Đầu ra

[20, 30, 40]

Minh họa: remove ()

Loại bỏ và trả về phần tử tại chỉ mục vị trí trong vectơ, dịch chuyển tất cả các phần tử sau nó sang trái.

fn main() {
   let mut v = vec![10,20,30];
   v.remove(1);
   println!("{:?}",v);
}

Đầu ra

[10, 30]

Hình minh họa - chứa ()

Trả về true nếu lát chứa phần tử có giá trị cho trước -

fn main() {
   let v = vec![10,20,30];
   if v.contains(&10) {
      println!("found 10");
   }
   println!("{:?}",v);
}

Đầu ra

found 10
[10, 20, 30]

Hình minh họa: len ()

Trả về số phần tử trong vectơ, còn được gọi là 'độ dài' của nó.

fn main() {
   let v = vec![1,2,3];
   println!("size of vector is :{}",v.len());
}

Đầu ra

size of vector is :3

Truy cập các giá trị từ một Vectơ

Các phần tử riêng lẻ trong một vectơ có thể được truy cập bằng cách sử dụng số chỉ mục tương ứng của chúng. Ví dụ sau tạo một quảng cáo vectơ in giá trị của phần tử đầu tiên.

fn main() {
   let mut v = Vec::new();
   v.push(20);
   v.push(30);

   println!("{:?}",v[0]);
}
Output: `20`

Các giá trị trong một vectơ cũng có thể được tìm nạp bằng cách sử dụng tham chiếu đến bộ sưu tập.

fn main() {
   let mut v = Vec::new();
   v.push(20);
   v.push(30);
   v.push(40);
   v.push(500);

   for i in &v {
      println!("{}",i);
   }
   println!("{:?}",v);
}

Đầu ra

20
30
40
500
[20, 30, 40, 500]

Bản đồ băm

Bản đồ là một tập hợp các cặp khóa-giá trị (được gọi là các mục nhập). Không có mục nhập nào trong một bản đồ có thể có cùng một khóa. Tóm lại, bản đồ là một bảng tra cứu. HashMap lưu trữ các khóa và giá trị trong một bảng băm. Các mục được lưu trữ theo thứ tự tùy ý. Khóa được sử dụng để tìm kiếm các giá trị trong HashMap. Cấu trúc HashMap được định nghĩa trongstd::collectionsmô-đun. Mô-đun này phải được nhập rõ ràng để truy cập cấu trúc HashMap.

Cú pháp: Tạo HashMap

let mut instance_name = HashMap::new();

Phương thức tĩnh new () của cấu trúc HashMap được sử dụng để tạo một đối tượng HashMap. Phương pháp này tạo một HashMap trống.

Các chức năng thường được sử dụng của HashMap được thảo luận dưới đây:

Sr.No phương pháp Chữ ký & Mô tả
1 chèn()

pub fn insert(&mut self, k: K, v: V) -> Option

Chèn một cặp khóa / giá trị, nếu không có khóa thì trả về Không có. Sau khi cập nhật, giá trị cũ được trả lại.

2 len ()

pub fn len(&self) -> usize

Trả về số phần tử trong bản đồ.

3 được()

pub fn get<Q: ?Sized>(&lself, k: &Q) -> Option<&V> where K:Borrow Q:Hash+ Eq

Trả về một tham chiếu đến giá trị tương ứng với khóa.

4 iter ()

pub fn iter(&self) -> Iter<K, V>

Một trình lặp truy cập tất cả các cặp khóa-giá trị theo thứ tự tùy ý. Loại phần tử của trình lặp là (& 'a K, &' a V).

5 khóa_ chứa

pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool

Trả về true nếu bản đồ chứa giá trị cho khóa được chỉ định.

6 tẩy()

pub fn remove_entry<Q: ?Sized>(&mut self, k: &Q) -> Option<(K, V)>

Xóa khóa khỏi bản đồ, trả lại khóa và giá trị đã lưu trữ nếu khóa trước đó có trong bản đồ.

Hình minh họa: insert ()

Chèn một cặp khóa / giá trị vào HashMap.

use std::collections::HashMap;
fn main(){
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   println!("{:?}",stateCodes);
}

Chương trình trên tạo một HashMap và khởi tạo nó với 2 cặp khóa-giá trị.

Đầu ra

{"KL": "Kerala", "MH": "Maharashtra"}

Hình minh họa: len ()

Trả về số phần tử trong bản đồ

use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   println!("size of map is {}",stateCodes.len());
}

Ví dụ trên tạo một HashMap và in ra tổng số phần tử trong đó.

Đầu ra

size of map is 2

Hình minh họa - get ()

Trả về một tham chiếu đến giá trị tương ứng với khóa. Ví dụ sau lấy giá trị cho khóa KL trong HashMap.

use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   println!("size of map is {}",stateCodes.len());
   println!("{:?}",stateCodes);

   match stateCodes.get(&"KL") {
      Some(value)=> {
         println!("Value for key KL is {}",value);
      }
      None => {
         println!("nothing found");
      }
   }
}

Đầu ra

size of map is 2
{"KL": "Kerala", "MH": "Maharashtra"}
Value for key KL is Kerala

Hình minh họa - iter ()

Trả về một trình lặp chứa tham chiếu đến tất cả các cặp khóa-giá trị theo thứ tự tùy ý.

use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");

   for (key, val) in stateCodes.iter() {
      println!("key: {} val: {}", key, val);
   }
}

Đầu ra

key: MH val: Maharashtra
key: KL val: Kerala

Hình minh họa: contains_key ()

Trả về true nếu bản đồ chứa giá trị cho khóa được chỉ định.

use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   stateCodes.insert("GJ","Gujarat");

   if stateCodes.contains_key(&"GJ") {
      println!("found key");
   }
}

Đầu ra

found key

Minh họa: remove ()

Xóa khóa khỏi bản đồ.

use std::collections::HashMap;
fn main() {
   let mut stateCodes = HashMap::new();
   stateCodes.insert("KL","Kerala");
   stateCodes.insert("MH","Maharashtra");
   stateCodes.insert("GJ","Gujarat");

   println!("length of the hashmap {}",stateCodes.len());
   stateCodes.remove(&"GJ");
   println!("length of the hashmap after remove() {}",stateCodes.len());
}

Đầu ra

length of the hashmap 3
length of the hashmap after remove() 2

HashSet

HashSet là một tập hợp các giá trị duy nhất của kiểu T. Việc thêm và xóa các giá trị rất nhanh, và nhanh chóng để hỏi liệu một giá trị đã cho có nằm trong tập hợp đó hay không. Cấu trúc HashSet được định nghĩa trong mô-đun std :: collection. Mô-đun này nên được nhập rõ ràng để truy cập cấu trúc HashSet.

Cú pháp: Tạo HashSet

let mut hash_set_name = HashSet::new();

Phương thức tĩnh, mới , của cấu trúc HashSet được sử dụng để tạo một HashSet. Phương thức này tạo một HashSet trống.

Bảng sau liệt kê một số phương thức thường được sử dụng của cấu trúc HashSet.

Sr.No phương pháp Chữ ký & Mô tả
1 chèn()

pub fn insert(&mut self, value: T) -> bool

Thêm một giá trị vào tập hợp. Nếu tập hợp không có giá trị này, true được trả về ngược lại false.

2 len ()

pub fn len(&self) -> usize

Trả về số phần tử trong tập hợp.

3 được()

pub fn get<Q:?Sized>(&self, value: &Q) -> Option<&T> where T: Borrow,Q: Hash + Eq,

Trả về một tham chiếu đến giá trị trong tập hợp, nếu có thì bằng giá trị đã cho.

4 iter ()

pub fn iter(&self) -> Iter

Trả về một trình lặp truy cập tất cả các phần tử theo thứ tự tùy ý. Loại phần tử của trình lặp là & 'a T.

5 khóa_ chứa

pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool

Trả về true nếu tập hợp chứa một giá trị.

6 tẩy()

pub fn remove<Q: ?Sized>(&mut self, value: &Q) -> bool

Xóa một giá trị khỏi tập hợp. Trả về true nếu giá trị có trong tập hợp.

Hình minh họa - insert ()

Thêm một giá trị vào tập hợp. HashSet không thêm các giá trị trùng lặp vào bộ sưu tập.

use std::collections::HashSet;
fn main() {
   let mut names = HashSet::new();

   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   names.insert("Mohtashim");//duplicates not added

   println!("{:?}",names);
}

Đầu ra

{"TutorialsPoint", "Kannan", "Mohtashim"}

Hình minh họa: len ()

Trả về số phần tử trong tập hợp.

use std::collections::HashSet;
fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   println!("size of the set is {}",names.len());
}

Đầu ra

size of the set is 3

Hình minh họa - iter ()

Truy xuất một trình lặp truy cập tất cả các phần tử theo thứ tự tùy ý.

use std::collections::HashSet;
fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   names.insert("Mohtashim");

   for name in names.iter() {
      println!("{}",name);
   }
}

Đầu ra

TutorialsPoint
Mohtashim
Kannan

Hình minh họa: get ()

Trả về một tham chiếu đến giá trị trong tập hợp, nếu có, bằng giá trị đã cho.

use std::collections::HashSet;
fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   names.insert("Mohtashim");

   match names.get(&"Mohtashim"){
      Some(value)=>{
         println!("found {}",value);
      }
      None =>{
         println!("not found");
      }
   }
   println!("{:?}",names);
}

Đầu ra

found Mohtashim
{"Kannan", "Mohtashim", "TutorialsPoint"}

Hình minh họa - chứa ()

Trả về true nếu tập hợp chứa một giá trị.

use std::collections::HashSet;

fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");

   if names.contains(&"Kannan") {
      println!("found name");
   }  
}

Đầu ra

found name

Minh họa: remove ()

Xóa một giá trị khỏi tập hợp.

use std::collections::HashSet;

fn main() {
   let mut names = HashSet::new();
   names.insert("Mohtashim");
   names.insert("Kannan");
   names.insert("TutorialsPoint");
   println!("length of the Hashset: {}",names.len());
   names.remove(&"Kannan");
   println!("length of the Hashset after remove() : {}",names.len());
}

Đầu ra

length of the Hashset: 3
length of the Hashset after remove() : 2

Trong Rust, các lỗi có thể được phân thành hai loại chính như trong bảng dưới đây.

Sr.No Tên & Mô tả Sử dụng
1

Recoverable

Các lỗi có thể được xử lý

Kết quả enum
2

UnRecoverable

Các lỗi không thể xử lý

vĩ mô hoảng loạn

Lỗi có thể khôi phục là lỗi có thể sửa được. Một chương trình có thể thử lại hoạt động không thành công hoặc chỉ định một quy trình hành động thay thế khi nó gặp lỗi có thể khôi phục. Các lỗi có thể khôi phục không làm cho chương trình bị lỗi đột ngột. Một ví dụ về lỗi có thể khôi phục là lỗi Không tìm thấy tệp .

Các lỗi không thể khôi phục khiến chương trình bị lỗi đột ngột. Một chương trình không thể trở lại trạng thái bình thường của nó nếu xảy ra lỗi không thể khôi phục. Nó không thể thử lại thao tác không thành công hoặc hoàn tác lỗi. Một ví dụ về lỗi không thể khôi phục đang cố gắng truy cập một vị trí nằm ngoài phần cuối của một mảng.

Không giống như các ngôn ngữ lập trình khác, Rust không có ngoại lệ. Nó trả về kết quả enum <T, E> cho các lỗi có thể khôi phục được, trong khi nó gọipanicmacro nếu chương trình gặp lỗi không thể khôi phục. Các hoảng loạn vĩ mô làm cho chương trình để thoát đột ngột.

Macro hoảng sợ và lỗi không thể khôi phục

hoảng loạn! macro cho phép một chương trình kết thúc ngay lập tức và cung cấp phản hồi cho người gọi chương trình. Nó nên được sử dụng khi một chương trình đạt đến trạng thái không thể khôi phục.

fn main() {
   panic!("Hello");
   println!("End of main"); //unreachable statement
}

Trong ví dụ trên, chương trình sẽ kết thúc ngay lập tức khi gặp sự cố ! vĩ mô.

Đầu ra

thread 'main' panicked at 'Hello', main.rs:3

Hình minh họa: hoảng sợ! vĩ mô

fn main() {
   let a = [10,20,30];
   a[10]; //invokes a panic since index 10 cannot be reached
}

Đầu ra như hình dưới đây -

warning: this expression will panic at run-time
--> main.rs:4:4
  |
4 | a[10];
  | ^^^^^ index out of bounds: the len is 3 but the index is 10

$main
thread 'main' panicked at 'index out of bounds: the len 
is 3 but the index is 10', main.rs:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Một chương trình có thể gây ra sự hoảng loạn! vĩ mô nếu các quy tắc kinh doanh bị vi phạm như được hiển thị trong ví dụ dưới đây -

fn main() {
   let no = 13; 
   //try with odd and even
   if no%2 == 0 {
      println!("Thank you , number is even");
   } else {
      panic!("NOT_AN_EVEN"); 
   }
   println!("End of main");
}

Ví dụ trên trả về lỗi nếu giá trị được gán cho biến là số lẻ.

Đầu ra

thread 'main' panicked at 'NOT_AN_EVEN', main.rs:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Enum kết quả và lỗi có thể khôi phục

Kết quả Enum - <T, E> có thể được sử dụng để xử lý các lỗi có thể khôi phục được. Nó có hai biến thể -OKErr. TE là các tham số kiểu chung. T đại diện cho loại giá trị sẽ được trả lại trong trường hợp thành công trong biến thể OK và E đại diện cho loại lỗi sẽ được trả lại trong trường hợp lỗi trong biến thể Err.

enum Result<T,E> {
   OK(T),
   Err(E)
}

Hãy để chúng tôi hiểu điều này với sự trợ giúp của một ví dụ -

use std::fs::File;
fn main() {
   let f = File::open("main.jpg"); 
   //this file does not exist
   println!("{:?}",f);
}

Chương trình trả về OK (Tệp) nếu tệp đã tồn tại và Err (Lỗi) nếu không tìm thấy tệp.

Err(Error { repr: Os { code: 2, message: "No such file or directory" } })

Bây giờ chúng ta hãy xem cách xử lý biến thể Err.

Ví dụ sau xử lý lỗi được trả về khi mở tệp bằng cách sử dụng match tuyên bố

use std::fs::File;
fn main() {
   let f = File::open("main.jpg");   // main.jpg doesn't exist
   match f {
      Ok(f)=> {
         println!("file found {:?}",f);
      },
      Err(e)=> {
         println!("file not found \n{:?}",e);   //handled error
      }
   }
   println!("end of main");
}

NOTE- Các bản in chương trình kết thúc của chính sự kiện mặc dù tập tin này không được tìm thấy. Điều này có nghĩa là chương trình đã xử lý lỗi một cách khéo léo.

Đầu ra

file not found
Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." }
end of main

Hình minh họa

Hàm is_even trả về một lỗi nếu số không phải là số chẵn. Hàm main () xử lý lỗi này.

fn main(){
   let result = is_even(13);
   match result {
      Ok(d)=>{
         println!("no is even {}",d);
      },
      Err(msg)=>{
         println!("Error msg is {}",msg);
      }
   }
   println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
   if no%2==0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}

NOTE- Vì hàm main xử lý lỗi một cách khéo léo nên phần cuối của câu lệnh chính được in ra.

Đầu ra

Error msg is NOT_AN_EVEN
end of main

Unrap () và mong đợi ()

Thư viện chuẩn chứa một số phương thức trợ giúp mà cả hai phương thức enums - Result <T, E> và Option <T> đều thực hiện. Bạn có thể sử dụng chúng để đơn giản hóa các trường hợp lỗi mà bạn thực sự không mong đợi mọi thứ không thành công. Trong trường hợp thành công từ một phương pháp, hàm "unrap" được sử dụng để trích xuất kết quả thực tế.

Sr.No phương pháp Chữ ký & Mô tả
1 mở ra

unwrap(self): T

Tự mong đợi là Ok / Some và trả về giá trị chứa bên trong. Nếu nó làErr hoặc là None thay vào đó, nó gây ra một sự hoảng sợ với nội dung của lỗi được hiển thị.

2 chờ đợi

expect(self, msg: &str): T

Hoạt động giống như mở ra, ngoại trừ việc nó xuất ra một thông báo tùy chỉnh trước khi phát hoảng ngoài nội dung của lỗi.

mở ra ()

Hàm unsrap () trả về kết quả thực tế mà một hoạt động thành công. Nó trả về một sự hoảng sợ với một thông báo lỗi mặc định nếu một hoạt động không thành công. Hàm này là cách viết tắt của câu lệnh đối sánh. Điều này được hiển thị trong ví dụ dưới đây -

fn main(){
   let result = is_even(10).unwrap();
   println!("result is {}",result);
   println!("end of main");
}
fn is_even(no:i32)->Result<bool,String> {
   if no%2==0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}
result is true
end of main

Sửa đổi mã trên để chuyển một số lẻ đến is_even() chức năng.

Hàm unsrap () sẽ hoảng loạn và trả về thông báo lỗi mặc định như hình bên dưới

thread 'main' panicked at 'called `Result::unwrap()` on 
an `Err` value: "NOT_AN_EVEN"', libcore\result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace

chờ đợi()

Chương trình có thể trả về một thông báo lỗi tùy chỉnh trong trường hợp có sự cố. Điều này được thể hiện trong ví dụ sau:

use std::fs::File;
fn main(){
   let f = File::open("pqr.txt").expect("File not able to open");
   //file does not exist
   println!("end of main");
}

Hàm mong đợi () tương tự như hàm unsrap (). Sự khác biệt duy nhất là một thông báo lỗi tùy chỉnh có thể được hiển thị bằng mong đợi.

Đầu ra

thread 'main' panicked at 'File not able to open: Error { repr: Os 
{ code: 2, message: "No such file or directory" } }', src/libcore/result.rs:860
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Generics là một phương tiện để viết mã cho nhiều ngữ cảnh với nhiều kiểu khác nhau. Trong Rust, generics đề cập đến việc tham số hóa các loại dữ liệu và đặc điểm. Generics cho phép viết mã ngắn gọn và rõ ràng hơn bằng cách giảm trùng lặp mã và cung cấp an toàn kiểu. Khái niệm Generics có thể được áp dụng cho các phương pháp, chức năng, cấu trúc, kiểu liệt kê, tập hợp và đặc điểm.

Các <T> syntaxđược gọi là tham số kiểu, được sử dụng để khai báo một cấu trúc chung. T đại diện cho bất kỳ kiểu dữ liệu nào.

Minh họa: Bộ sưu tập chung

Ví dụ sau khai báo một vectơ chỉ có thể lưu trữ các số nguyên.

fn main(){
   let mut vector_integer: Vec<i32> = vec![20,30];
   vector_integer.push(40);
   println!("{:?}",vector_integer);
}

Đầu ra

[20, 30, 40]

Hãy xem xét đoạn mã sau -

fn main() {
   let mut vector_integer: Vec<i32> = vec![20,30];
   vector_integer.push(40);
   vector_integer.push("hello"); 
   //error[E0308]: mismatched types
   println!("{:?}",vector_integer);
}

Ví dụ trên cho thấy rằng một vector kiểu số nguyên chỉ có thể lưu trữ các giá trị nguyên. Vì vậy, nếu chúng ta cố gắng đẩy một giá trị chuỗi vào bộ sưu tập, trình biên dịch sẽ trả về lỗi. Generics giúp các bộ sưu tập an toàn hơn.

Minh họa: Cấu trúc chung

Tham số type đại diện cho một kiểu mà trình biên dịch sẽ điền vào sau.

struct Data<T> {
   value:T,
}
fn main() {
   //generic type of i32
   let t:Data<i32> = Data{value:350};
   println!("value is :{} ",t.value);
   //generic type of String
   let t2:Data<String> = Data{value:"Tom".to_string()};
   println!("value is :{} ",t2.value);
}

Ví dụ trên khai báo một cấu trúc chung có tên là Dữ liệu . Kiểu <T> chỉ ra một số kiểu dữ liệu. Hàm main () tạo ra hai trường hợp - một trường hợp số nguyên và một trường hợp chuỗi, của cấu trúc.

Đầu ra

value is :350
value is :Tom

Đặc điểm

Các đặc điểm có thể được sử dụng để thực hiện một bộ tiêu chuẩn các hành vi (phương pháp) trên nhiều cấu trúc. Đặc điểm giống nhưinterfacestrong Lập trình hướng đối tượng. Cú pháp của đặc điểm như hình dưới đây -

Khai báo một đặc điểm

trait some_trait {
   //abstract or method which is empty
   fn method1(&self);
   // this is already implemented , this is free
   fn method2(&self){
      //some contents of method2
   }
}

Đặc điểm có thể chứa các phương thức cụ thể (phương thức có phần thân) hoặc phương thức trừu tượng (phương thức không có phần thân). Sử dụng một phương pháp cụ thể nếu định nghĩa phương pháp sẽ được chia sẻ bởi tất cả các cấu trúc triển khai Đặc điểm. Tuy nhiên, một cấu trúc có thể chọn ghi đè một chức năng được xác định bởi đặc điểm.

Sử dụng các phương thức trừu tượng nếu định nghĩa phương thức khác nhau đối với các cấu trúc triển khai.

Cú pháp - Triển khai một đặc điểm

impl some_trait for structure_name {
   // implement method1() there..
   fn method1(&self ){
   }
}

Các ví dụ sau định nghĩa một đặc điểm có thể in với một phương pháp in () , được thực hiện bởi các cấu trúc cuốn sách .

fn main(){
   //create an instance of the structure
   let b1 = Book {
      id:1001,
      name:"Rust in Action"
   };
   b1.print();
}
//declare a structure
struct Book {
   name:&'static str,
   id:u32
}
//declare a trait
trait Printable {
   fn print(&self);
}
//implement the trait
impl Printable for Book {
   fn print(&self){
      println!("Printing book with id:{} and name {}",self.id,self.name)
   }
}

Đầu ra

Printing book with id:1001 and name Rust in Action

Các chức năng chung

Ví dụ định nghĩa một hàm chung hiển thị một tham số được truyền cho nó. Tham số có thể là bất kỳ loại nào. Kiểu của tham số phải triển khai đặc điểm Hiển thị để giá trị của nó có thể được in bởi println! vĩ mô.

use std::fmt::Display;

fn main(){
   print_pro(10 as u8);
   print_pro(20 as u16);
   print_pro("Hello TutorialsPoint");
}

fn print_pro<T:Display>(t:T){
   println!("Inside print_pro generic function:");
   println!("{}",t);
}

Đầu ra

Inside print_pro generic function:
10
Inside print_pro generic function:
20
Inside print_pro generic function:
Hello TutorialsPoint

Chương này thảo luận về cách chấp nhận các giá trị từ đầu vào tiêu chuẩn (bàn phím) và hiển thị các giá trị cho đầu ra tiêu chuẩn (bàn điều khiển). Trong chương này, chúng ta cũng sẽ thảo luận về việc truyền các đối số dòng lệnh.

Các loại trình đọc và ghi

Các tính năng thư viện tiêu chuẩn của Rust cho đầu vào và đầu ra được sắp xếp theo hai đặc điểm:

  • Read
  • Write
Sr.No Đặc điểm & Mô tả Thí dụ
1

Read

Các kiểu triển khai Read có các phương thức cho đầu vào hướng byte. Họ được gọi là độc giả

Stdin, Tệp
2

Write

Các kiểu triển khai Viết hỗ trợ cả đầu ra văn bản hướng byte và UTF-8. Họ được gọi là nhà văn.

Stdout, Tệp

Đọc đặc điểm

Readerslà các thành phần mà chương trình của bạn có thể đọc các byte. Ví dụ bao gồm đọc đầu vào từ bàn phím, tệp, v.v.read_line() phương pháp của đặc điểm này có thể được sử dụng để đọc dữ liệu, từng dòng một, từ một tệp hoặc luồng đầu vào chuẩn.

Sr.No Đặc điểm Phương pháp & Mô tả
1 Đọc

read_line(&mut line)->Result

Đọc một dòng văn bản và nối nó vào dòng, đó là một Chuỗi. Giá trị trả về là io :: Kết quả, số byte được đọc.

Hình minh họa - Đọc từ Console - stdin ()

Các chương trình gỉ có thể phải chấp nhận các giá trị từ người dùng trong thời gian chạy. Ví dụ sau đây đọc các giá trị từ đầu vào tiêu chuẩn (Bàn phím) và in ra bảng điều khiển.

fn main(){
   let mut line = String::new();
   println!("Enter your name :");
   let b1 = std::io::stdin().read_line(&mut line).unwrap();
   println!("Hello , {}", line);
   println!("no of bytes read , {}", b1);
}

Hàm stdin () trả về một xử lý cho dòng đầu vào chuẩn của quy trình hiện tại, mà hàm read_line có thể được áp dụng. Hàm này cố gắng đọc tất cả các ký tự có trong bộ đệm đầu vào khi nó gặp một ký tự cuối dòng.

Đầu ra

Enter your name :
Mohtashim
Hello , Mohtashim
no of bytes read , 10

Viết đặc điểm

Writerslà các thành phần mà chương trình của bạn có thể ghi byte vào. Ví dụ bao gồm in giá trị vào bảng điều khiển, ghi vào tệp, v.v. Phương thức write () của đặc điểm này có thể được sử dụng để ghi dữ liệu vào tệp hoặc luồng đầu ra chuẩn.

Sr.No Đặc điểm Phương pháp & Mô tả
1 Viết

write(&buf)->Result

Ghi một số byte trong buf lát vào luồng bên dưới. Nó trả về io :: Kết quả, số byte được viết.

Hình minh họa - Ghi vào Bảng điều khiển - stdout ()

Bản in! hoặc println! macro có thể được sử dụng để hiển thị văn bản trên bảng điều khiển. Tuy nhiên, bạn cũng có thể sử dụng hàm thư viện chuẩn write () để hiển thị một số văn bản ở đầu ra chuẩn.

Chúng ta hãy xem xét một ví dụ để hiểu điều này.

use std::io::Write;
fn main() {
   let b1 = std::io::stdout().write("Tutorials ".as_bytes()).unwrap();
   let b2 = std::io::stdout().write(String::from("Point").as_bytes()).unwrap();
   std::io::stdout().write(format!("\nbytes written {}",(b1+b2)).as_bytes()).unwrap();
}

Đầu ra

Tutorials Point
bytes written 15

Hàm thư viện tiêu chuẩn stdout () trả về một xử lý cho luồng đầu ra tiêu chuẩn của quy trình hiện tại, màwritechức năng có thể được áp dụng. Phương thức write () trả về một enum, Kết quả. Unrap () là một phương thức trợ giúp để trích xuất kết quả thực tế từ kiểu liệt kê. Phương thức Unrap sẽ gây hoảng sợ nếu xảy ra lỗi.

NOTE - File IO được thảo luận trong chương tiếp theo.

Đối số CommandLine

Các đối số CommandLine được chuyển đến một chương trình trước khi thực thi nó. Chúng giống như các tham số được truyền cho các hàm. Tham số CommandLine có thể được sử dụng để chuyển các giá trị cho hàm main () . Cácstd::env::args() trả về các đối số dòng lệnh.

Hình minh họa

Ví dụ sau đây chuyển các giá trị dưới dạng đối số commandLine cho hàm main (). Chương trình này được tạo ra trong một tên tập tin main.rs .

//main.rs
fn main(){
   let cmd_line = std::env::args();
   println!("No of elements in arguments is :{}",cmd_line.len()); 
   //print total number of values passed
   for arg in cmd_line {
      println!("[{}]",arg); //print all values passed 
      as commandline arguments
   }
}

Chương trình sẽ tạo một tệp main.exe sau khi được biên dịch. Nhiều tham số dòng lệnh phải được phân tách bằng dấu cách. Thực thi main.exe từ thiết bị đầu cuối dưới dạng main.exe xin chào hướng dẫn viên .

NOTE- hellotutorialspoint là các đối số dòng lệnh.

Đầu ra

No of elements in arguments is :3
[main.exe]
[hello]
[tutorialspoint]

Kết quả hiển thị 3 đối số vì main.exe là đối số đầu tiên.

Hình minh họa

Chương trình sau đây tính tổng các giá trị được truyền dưới dạng đối số dòng lệnh. Một danh sách các giá trị nguyên được phân tách bằng dấu cách được chuyển vào chương trình.

fn main(){
   let cmd_line = std::env::args();
   println!("No of elements in arguments is 
   :{}",cmd_line.len()); 
   // total number of elements passed

   let mut sum = 0;
   let mut has_read_first_arg = false;

   //iterate through all the arguments and calculate their sum

   for arg in cmd_line {
      if has_read_first_arg { //skip the first argument since it is the exe file name
         sum += arg.parse::<i32>().unwrap();
      }
      has_read_first_arg = true; 
      // set the flag to true to calculate sum for the subsequent arguments.
   }
   println!("sum is {}",sum);
}

Khi thực thi chương trình dưới dạng main.exe 1 2 3 4, kết quả đầu ra sẽ là:

No of elements in arguments is :5
sum is 10

Ngoài việc đọc và ghi vào bảng điều khiển, Rust cho phép đọc và ghi vào các tệp.

Cấu trúc Tệp đại diện cho một tệp. Nó cho phép một chương trình thực hiện các thao tác đọc-ghi trên một tệp. Tất cả các phương thức trong cấu trúc Tệp trả về một biến thể của kiểu liệt kê io :: Kết quả.

Các phương thức thường được sử dụng của cấu trúc tệp được liệt kê trong bảng bên dưới:

Sr.No Mô-đun phương pháp Chữ ký Sự miêu tả
1 std :: fs :: Tệp mở() pub fn open <P: AsRef> (path: P) -> Kết quả Phương thức tĩnh mở có thể được sử dụng để mở tệp ở chế độ chỉ đọc.
2 std :: fs :: Tệp tạo nên() pub fn create <P: AsRef> (path: P) -> Kết quả Phương thức tĩnh mở tệp ở chế độ chỉ ghi. Nếu tệp đã tồn tại, nội dung cũ sẽ bị hủy. Nếu không, một tệp mới sẽ được tạo.
3 std :: fs :: remove_file remove_file () pub fn remove_file <P: AsRef> (path: P) -> Kết quả <()> Xóa một tệp khỏi hệ thống tệp. Không có gì đảm bảo rằng tệp sẽ bị xóa ngay lập tức.
4 std :: fs :: OpenOptions chắp thêm () pub fn append (& mut self, append: bool) -> & mut OpenOptions Đặt tùy chọn cho chế độ nối thêm của tệp.
5 std :: io :: Writes write_all () fn write_all (& mut self, buf: & [u8]) -> Kết quả <()> Cố gắng ghi toàn bộ bộ đệm vào lần ghi này.
6 std :: io :: Đọc read_to_string () fn read_to_string (& mut self, buf: & mut String) -> Kết quả Đọc tất cả các byte cho đến khi EOF trong nguồn này, gắn chúng vào buf.

Ghi vào tệp

Hãy để chúng tôi xem một ví dụ để hiểu cách ghi một tệp.

Chương trình sau tạo một tệp 'data.txt'. Phương thức create () được sử dụng để tạo một tệp. Phương thức trả về một xử lý tệp nếu tệp được tạo thành công. Hàm write_all dòng cuối cùng sẽ ghi các byte trong tệp mới tạo. Nếu bất kỳ thao tác nào không thành công, hàm mong đợi () trả về một thông báo lỗi.

use std::io::Write;
fn main() {
   let mut file = std::fs::File::create("data.txt").expect("create failed");
   file.write_all("Hello World".as_bytes()).expect("write failed");
   file.write_all("\nTutorialsPoint".as_bytes()).expect("write failed");
   println!("data written to file" );
}

Đầu ra

data written to file

Đọc từ một tệp

Chương trình sau đây đọc nội dung trong tệp data.txt và in ra bảng điều khiển. Chức năng "mở" được sử dụng để mở một tệp hiện có. Một đường dẫn tuyệt đối hoặc tương đối đến tệp được chuyển tới hàm open () dưới dạng tham số. Hàm open () ném ra một ngoại lệ nếu tệp không tồn tại hoặc nếu nó không thể truy cập được vì bất kỳ lý do gì. Nếu thành công, một trình xử lý tệp cho tệp đó sẽ được gán cho biến "tệp".

Hàm "read_to_string" của xử lý "tệp" được sử dụng để đọc nội dung của tệp đó thành một biến chuỗi.

use std::io::Read;

fn main(){
   let mut file = std::fs::File::open("data.txt").unwrap();
   let mut contents = String::new();
   file.read_to_string(&mut contents).unwrap();
   print!("{}", contents);
}

Đầu ra

Hello World
TutorialsPoint

Xóa tệp

Ví dụ sau sử dụng hàm remove_file () để xóa một tệp. Hàm mong đợi () trả về một thông báo tùy chỉnh trong trường hợp xảy ra lỗi.

use std::fs;
fn main() {
   fs::remove_file("data.txt").expect("could not remove file");
   println!("file is removed");
}

Đầu ra

file is removed

Nối dữ liệu vào một tệp

Hàm append () ghi dữ liệu vào cuối tệp. Điều này được thể hiện trong ví dụ dưới đây:

use std::fs::OpenOptions;
use std::io::Write;

fn main() {
   let mut file = OpenOptions::new().append(true).open("data.txt").expect(
      "cannot open file");
   file.write_all("Hello World".as_bytes()).expect("write failed");
   file.write_all("\nTutorialsPoint".as_bytes()).expect("write failed");
   println!("file append success");
}

Đầu ra

file append success

Sao chép một tập tin

Ví dụ sau đây sao chép nội dung trong tệp sang tệp mới.

use std::io::Read;
use std::io::Write;

fn main() {
   let mut command_line: std::env::Args = std::env::args();
   command_line.next().unwrap();
   // skip the executable file name
   // accept the source file
   let source = command_line.next().unwrap();
   // accept the destination file
   let destination = command_line.next().unwrap();
   let mut file_in = std::fs::File::open(source).unwrap();
   let mut file_out = std::fs::File::create(destination).unwrap();
   let mut buffer = [0u8; 4096];
   loop {
      let nbytes = file_in.read(&mut buffer).unwrap();
      file_out.write(&buffer[..nbytes]).unwrap();
      if nbytes < buffer.len() { break; }
   }
}

Thực thi chương trình trên dưới dạng main.exe data.txt datacopy.txt . Hai đối số dòng lệnh được truyền trong khi thực thi tệp:

  • đường dẫn đến tệp nguồn
  • tệp đích

Cargo là người quản lý gói hàng cho RUST. Điều này hoạt động giống như một công cụ và quản lý các dự án Rust.

Một số lệnh vận chuyển hàng hóa thường được sử dụng được liệt kê trong bảng dưới đây:

Sr.No Lệnh & Mô tả
1

cargo build

Biên dịch dự án hiện tại.

2

cargo check

Phân tích dự án hiện tại và báo cáo lỗi, nhưng không tạo tệp đối tượng.

3

cargo run

Xây dựng và thực thi src / main.rs.

4

cargo clean

Xóa thư mục đích.

5

cargo update

Cập nhật các phụ thuộc được liệt kê trong Cargo.lock.

6

cargo new

Tạo một dự án vận chuyển hàng hóa mới.

Cargo giúp tải xuống thư viện của bên thứ ba. Do đó, nó hoạt động giống như một trình quản lý gói. Bạn cũng có thể xây dựng các thư viện của riêng mình. Cargo được cài đặt theo mặc định khi bạn cài đặt Rust.

Để tạo một dự án hàng hóa mới, chúng ta có thể sử dụng các lệnh dưới đây.

Tạo thùng nhị phân

cargo new project_name --bin

Tạo thùng thư viện

cargo new project_name --lib

Để kiểm tra phiên bản hiện tại của hàng hóa, hãy thực hiện lệnh sau:

cargo --version

Hình minh họa - Tạo một dự án Hàng hóa nhị phân

Trò chơi tạo ra một số ngẫu nhiên và nhắc người dùng đoán số đó.

Bước 1 - Tạo một thư mục dự án

Mở thiết bị đầu cuối và nhập lệnh sau đây, hàng hóa ứng dụng đoán mới --bin .

Điều này sẽ tạo ra cấu trúc thư mục sau.

guess-game-app/
   -->Cargo.toml
   -->src/
      main.rs

Lệnh mới về hàng hóa được sử dụng để tạo thùng. Các --bin cờ chỉ ra rằng thùng được tạo ra là một thùng nhị phân. Các thùng công cộng được lưu trữ trong một kho lưu trữ trung tâm có tên là crates.iohttps://crates.io/.

Bước 2 - Bao gồm các tham chiếu đến các thư viện bên ngoài

Ví dụ này cần tạo một số ngẫu nhiên. Vì thư viện tiêu chuẩn nội bộ không cung cấp logic tạo số ngẫu nhiên, chúng ta cần xem xét các thư viện hoặc thùng bên ngoài. Hãy để chúng tôi sử dụngrandthùng có sẵn tại trang web crates.io crates.io

Các https://crates.io/crates/randlà một thư viện gỉ để tạo số ngẫu nhiên. Rand cung cấp các tiện ích để tạo số ngẫu nhiên, chuyển đổi chúng thành các kiểu và phân phối hữu ích, và một số thuật toán liên quan đến ngẫu nhiên.

Biểu đồ sau đây hiển thị trang web crate.io và kết quả tìm kiếm cho thùng rand.

Sao chép phiên bản của thùng rand vào tệp Cargo.toml rand = "0.5.5" .

[package]
name = "guess-game-app"
version = "0.1.0"
authors = ["Mohtashim"]

[dependencies]
rand = "0.5.5"

Bước 3: Biên dịch dự án

Điều hướng đến thư mục dự án. Thực hiện lệnhcargo build trên cửa sổ đầu cuối -

Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading rand v0.5.5
Downloading rand_core v0.2.2
Downloading winapi v0.3.6
Downloading rand_core v0.3.0
   Compiling winapi v0.3.6
   Compiling rand_core v0.3.0
   Compiling rand_core v0.2.2
   Compiling rand v0.5.5
   Compiling guess-game-app v0.1.0 
   (file:///E:/RustWorks/RustRepo/Code_Snippets/cargo-projects/guess-game-app)
   Finished dev [unoptimized + debuginfo] target(s) in 1m 07s

Thùng rand và tất cả các phụ thuộc bắc cầu (phụ thuộc bên trong của rand) sẽ được tự động tải xuống.

Bước 4 - Hiểu Logic Kinh doanh

Bây giờ chúng ta hãy xem cách logic nghiệp vụ hoạt động cho trò chơi đoán số -

  • Trò chơi ban đầu tạo ra một số ngẫu nhiên.

  • Người dùng được yêu cầu nhập đầu vào và đoán số.

  • Nếu số nhỏ hơn số đã tạo, thông báo “Quá thấp” sẽ được in.

  • Nếu số lớn hơn số đã tạo, thông báo “Quá cao” sẽ được in.

  • Nếu người dùng nhập số được tạo bởi chương trình, trò chơi sẽ thoát.

Bước 5 - Chỉnh sửa tệp main.rs

Thêm logic nghiệp vụ vào tệp main.rs.

use std::io;
extern crate rand; 
//importing external crate
use rand::random;
fn get_guess() -> u8 {
   loop {
      println!("Input guess") ;
      let mut guess = String::new();
      io::stdin().read_line(&mut guess)
         .expect("could not read from stdin");
      match guess.trim().parse::<u8>(){ //remember to trim input to avoid enter spaces
         Ok(v) => return v,
         Err(e) => println!("could not understand input {}",e)
      }
   }
}
fn handle_guess(guess:u8,correct:u8)-> bool {
   if guess < correct {
      println!("Too low");
      false

   } else if guess> correct {
      println!("Too high");
      false
   } else {
      println!("You go it ..");
      true
   }
}
fn main() {
   println!("Welcome to no guessing game");

   let correct:u8 = random();
   println!("correct value is {}",correct);
   loop {
      let guess = get_guess();
      if handle_guess(guess,correct){
         break;
      }
   }
}

Bước 6 - Biên dịch và Thực thi Dự án

Thực hiện lệnh chạy hàng hóa trên thiết bị đầu cuối. Đảm bảo rằng thiết bị đầu cuối trỏ đến thư mục Dự án.

Welcome to no guessing game
correct value is 97
Input guess
20
Too low
Input guess
100
Too high
Input guess
97
You got it ..

Trong chương này, chúng ta sẽ tìm hiểu cách thức hoạt động của các trình vòng lặp và bao đóng trong RUST.

Trình lặp lại

Một trình lặp giúp lặp qua một tập hợp các giá trị như mảng, vectơ, bản đồ, v.v. Trình lặp thực hiện đặc điểm của Iterator được xác định trong thư viện tiêu chuẩn Rust. Phương thức iter () trả về một đối tượng trình lặp của tập hợp. Các giá trị trong một đối tượng trình lặp được gọi là các mục. Phương thức next () của trình lặp có thể được sử dụng để duyệt qua các mục. Phương thức next () trả về giá trị None khi nó đến cuối tập hợp.

Ví dụ sau sử dụng một trình lặp để đọc các giá trị từ một mảng.

fn main() {
   //declare an array
   let a = [10,20,30];

   let mut iter = a.iter(); 
   // fetch an iterator object for the array
   println!("{:?}",iter);

   //fetch individual values from the iterator object
   println!("{:?}",iter.next());
   println!("{:?}",iter.next());
   println!("{:?}",iter.next());
   println!("{:?}",iter.next());
}

Đầu ra

Iter([10, 20, 30])
Some(10)
Some(20)
Some(30)
None

Nếu một tập hợp như mảng hoặc Vector triển khai đặc điểm Iterator thì nó có thể được duyệt qua bằng cú pháp for ... in như hình dưới đây-

fn main() {
   let a = [10,20,30];
   let iter = a.iter();
   for data in iter{
      print!("{}\t",data);
   }
}

Đầu ra

10 20 30

3 phương thức sau đây trả về một đối tượng vòng lặp từ một tập hợp, trong đó T đại diện cho các phần tử trong một tập hợp.

Sr.No Phương pháp & Mô tả
1

iter()

cung cấp một trình lặp qua & T (tham chiếu đến T)

2

into_iter()

cung cấp cho một trình lặp trên T

3

iter_mut()

cung cấp một trình lặp qua & mut T

Hình minh họa: iter ()

Hàm iter () sử dụng khái niệm vay mượn. Nó trả về một tham chiếu đến từng phần tử của bộ sưu tập, để bộ sưu tập không bị ảnh hưởng và có sẵn để sử dụng lại sau vòng lặp.

fn main() {
   let names = vec!["Kannan", "Mohtashim", "Kiran"];
   for name in names.iter() {
      match name {
         &"Mohtashim" => println!("There is a rustacean among us!"),
         _ => println!("Hello {}", name),
      }
   }
   println!("{:?}",names); 
   // reusing the collection after iteration
}

Đầu ra

Hello Kannan
There is a rustacean among us!
Hello Kiran
["Kannan", "Mohtashim", "Kiran"]

Hình minh họa - into_iter ()

Chức năng này sử dụng khái niệm quyền sở hữu. Nó di chuyển các giá trị trong bộ sưu tập vào một đối tượng iter, tức là bộ sưu tập đã được tiêu thụ và nó không còn khả dụng để sử dụng lại.

fn main(){
   let names = vec!["Kannan", "Mohtashim", "Kiran"];
   for name in names.into_iter() {
      match name {
         "Mohtashim" => println!("There is a rustacean among us!"),
         _ => println!("Hello {}", name),
      }
   }
   // cannot reuse the collection after iteration
   //println!("{:?}",names); 
   //Error:Cannot access after ownership move
}

Đầu ra

Hello Kannan
There is a rustacean among us!
Hello Kiran

Hình minh họa - for and iter_mut ()

Hàm này giống như hàm iter () . Tuy nhiên, chức năng này có thể sửa đổi các phần tử trong bộ sưu tập.

fn main() {
   let mut names = vec!["Kannan", "Mohtashim", "Kiran"];
   for name in names.iter_mut() {
      match name {
         &mut "Mohtashim" => println!("There is a rustacean among us!"),
         _ => println!("Hello {}", name),
      }
   }
   println!("{:?}",names);
   //// reusing the collection after iteration
}

Đầu ra

Hello Kannan
There is a rustacean among us!
Hello Kiran
["Kannan", "Mohtashim", "Kiran"]

Khép kín

Đóng cửa đề cập đến một chức năng trong một chức năng khác. Đây là những hàm ẩn danh - những hàm không có tên. Đóng cửa có thể được sử dụng để gán một hàm cho một biến. Điều này cho phép một chương trình truyền một hàm dưới dạng tham số cho các hàm khác. Đóng cửa còn được gọi là một hàm nội tuyến. Các biến trong hàm ngoài có thể được truy cập bằng các hàm nội tuyến.

Cú pháp: Định nghĩa một Đóng cửa

Định nghĩa đóng có thể có các tham số tùy ý. Các thông số được bao trong hai thanh dọc.

let closure_function = |parameter| {
   //logic
}

Cú pháp gọi một C Close thực hiện Fncác tính trạng. Vì vậy, nó có thể được gọi bằng() cú pháp.

closure_function(parameter);    //invoking

Hình minh họa

Ví dụ sau định nghĩa một hàm is_even trong hàm main () . Bao đóng trả về true nếu một số chẵn và trả về false nếu số lẻ.

fn main(){
   let is_even = |x| {
      x%2==0
   };
   let no = 13;
   println!("{} is even ? {}",no,is_even(no));
}

Đầu ra

13 is even ? false

Hình minh họa

fn main(){
   let val = 10; 
   // declared outside
   let closure2 = |x| {
      x + val //inner function accessing outer fn variable
   };
   println!("{}",closure2(2));
}

Hàm main () khai báo một biến val và một bao đóng. Bao đóng truy cập vào biến được khai báo trong hàm ngoài main () .

Đầu ra

12

Rust phân bổ mọi thứ trên ngăn xếp theo mặc định. Bạn có thể lưu trữ mọi thứ trên heap bằng cách gói chúng trong các con trỏ thông minh như Box . Các loại như Vec và String giúp phân bổ một cách ngầm định đống. Con trỏ thông minh triển khai các đặc điểm được liệt kê trong bảng dưới đây. Những đặc điểm này của con trỏ thông minh phân biệt chúng với một cấu trúc thông thường -

Sr.No Tên đặc điểm Gói & Mô tả
1 Deref

std::ops::Deref

Được sử dụng cho các hoạt động hội nghị bất biến, như * v.

2 Rơi vãi

std::ops::Drop

Được sử dụng để chạy một số mã khi một giá trị nằm ngoài phạm vi. Điều này đôi khi được gọi là trình hủy

Trong chương này, chúng ta sẽ tìm hiểu về Boxcon trỏ thông minh. Chúng ta cũng sẽ học cách tạo một con trỏ thông minh tùy chỉnh như Box.

Cái hộp

Con trỏ thông minh Box còn được gọi là hộp cho phép bạn lưu trữ dữ liệu trên heap hơn là ngăn xếp. Ngăn xếp chứa con trỏ tới dữ liệu đống. Hộp không có chi phí hiệu suất, ngoài việc lưu trữ dữ liệu của chúng trên heap.

Hãy để chúng tôi xem cách sử dụng một hộp để lưu trữ giá trị i32 trên heap.

fn main() {
   let var_i32 = 5; 
   //stack
   let b = Box::new(var_i32); 
   //heap
   println!("b = {}", b);
}

Đầu ra

b = 5

Để truy cập một giá trị được trỏ bởi một biến, hãy sử dụng tham chiếu. Dấu * được sử dụng như một toán tử hội nghị. Hãy để chúng tôi xem cách sử dụng dereference với Box.

fn main() {
   let x = 5; 
   //value type variable
   let y = Box::new(x); 
   //y points to a new value 5 in the heap

   println!("{}",5==x);
   println!("{}",5==*y); 
   //dereferencing y
}

Biến x là một kiểu giá trị với giá trị 5. Vì vậy, biểu thức 5 == x sẽ trả về true. Biến y trỏ đến đống. Để truy cập giá trị trong heap, chúng ta cần tham khảo bằng cách sử dụng * y. * y trả về giá trị 5. Vì vậy, biểu thức 5 == * y trả về giá trị true.

Đầu ra

true
true

Hình minh họa - Đặc điểm Deref

Đặc điểm Deref, được cung cấp bởi thư viện chuẩn, yêu cầu chúng ta triển khai một phương thức có tên là deref , phương thức này tự vay và trả về một tham chiếu đến dữ liệu bên trong. Ví dụ sau tạo một cấu trúc MyBox , là một kiểu chung. Nó thực hiện đặc điểm Deref . Đặc điểm này giúp chúng ta truy cập các giá trị heap được bao bọc bởi y bằng cách sử dụng * y .

use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> { 
   // Generic structure with static method new
   fn new(x:T)-> MyBox<T> {
      MyBox(x)
   }
}
impl<T> Deref for MyBox<T> {
   type Target = T;
   fn deref(&self) -> &T {
      &self.0 //returns data
   }
}
fn main() {
   let x = 5;
   let y = MyBox::new(x); 
   // calling static method
   
   println!("5==x is {}",5==x);
   println!("5==*y is {}",5==*y); 
   // dereferencing y
   println!("x==*y is {}",x==*y);
   //dereferencing y
}

Đầu ra

5==x is true
5==*y is true
x==*y is true

Hình minh họa - Drop Trait

Đặc điểm Drop chứa phương thức drop () . Phương thức này được gọi khi một cấu trúc đã triển khai đặc điểm này vượt ra ngoài phạm vi. Trong một số ngôn ngữ, lập trình viên phải gọi mã để giải phóng bộ nhớ hoặc tài nguyên mỗi khi họ sử dụng xong một phiên bản của con trỏ thông minh. Trong Rust, bạn có thể tự động phân bổ bộ nhớ bằng cách sử dụng đặc điểm Drop.

use std::ops::Deref;

struct MyBox<T>(T);
impl<T> MyBox<T> {
   fn new(x:T)->MyBox<T>{
      MyBox(x)
   }
}
impl<T> Deref for MyBox<T> {
   type Target = T;
      fn deref(&self) -< &T {
      &self.0
   }
}
impl<T> Drop for MyBox<T>{
   fn drop(&mut self){
      println!("dropping MyBox object from memory ");
   }
}
fn main() {
   let x = 50;
   MyBox::new(x);
   MyBox::new("Hello");
}

Trong ví dụ trên, phương thức drop sẽ được gọi hai lần khi chúng ta đang tạo hai đối tượng trong heap.

dropping MyBox object from memory
dropping MyBox object from memory

Trong lập trình đồng thời, các phần khác nhau của chương trình thực thi độc lập. Mặt khác, trong lập trình song song, các phần khác nhau của chương trình thực thi cùng một lúc. Cả hai mô hình đều quan trọng như nhau vì ngày càng nhiều máy tính tận dụng nhiều bộ vi xử lý của chúng.

Chủ đề

Chúng ta có thể sử dụng các luồng để chạy mã đồng thời. Trong các hệ điều hành hiện tại, mã của chương trình đã thực thi được chạy trong một quá trình và hệ điều hành quản lý nhiều quá trình cùng một lúc. Trong chương trình của bạn, bạn cũng có thể có các phần độc lập chạy đồng thời. Các tính năng chạy các phần độc lập này được gọi là luồng.

Tạo một chủ đề

Các thread::spawnđược sử dụng để tạo một chủ đề mới. Hàm spawn nhận một tham số đóng làm tham số. Đóng định nghĩa mã sẽ được thực thi bởi luồng. Ví dụ sau in một số văn bản từ một chuỗi chính và văn bản khác từ một chuỗi mới.

//import the necessary modules
use std::thread;
use std::time::Duration;

fn main() {
   //create a new thread
   thread::spawn(|| {
      for i in 1..10 {
         println!("hi number {} from the spawned thread!", i);
         thread::sleep(Duration::from_millis(1));
      }
   });
   //code executed by the main thread
   for i in 1..5 {
      println!("hi number {} from the main thread!", i);
      thread::sleep(Duration::from_millis(1));
   }
}

Đầu ra

hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the main thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 4 from the main thread!

Luồng chính in các giá trị từ 1 đến 4.

NOTE- Luồng mới sẽ bị dừng khi luồng chính kết thúc. Đầu ra từ chương trình này có thể khác một chút mỗi lần.

Các thread::sleephàm buộc một luồng ngừng thực thi trong một thời gian ngắn, cho phép một luồng khác chạy. Các luồng có thể sẽ thay phiên nhau, nhưng điều đó không được đảm bảo - nó phụ thuộc vào cách hệ điều hành lập lịch cho các luồng. Trong lần chạy này, luồng chính được in đầu tiên, mặc dù câu lệnh in từ luồng được tạo xuất hiện đầu tiên trong mã. Hơn nữa, ngay cả khi luồng sinh sản được lập trình để in các giá trị cho đến 9, nó chỉ đến 5 trước khi luồng chính tắt.

Tham gia Xử lý

Một chuỗi được tạo có thể không có cơ hội chạy hoặc chạy hoàn toàn. Điều này là do luồng chính hoàn thành nhanh chóng. Hàm sinh ra <F, T> (f: F) -> JoinHandlelt; T> trả về một JoinHandle. Phương thức join () trên JoinHandle đợi chuỗi liên kết kết thúc.

use std::thread;
use std::time::Duration;

fn main() {
   let handle = thread::spawn(|| {
      for i in 1..10 {
         println!("hi number {} from the spawned thread!", i);
         thread::sleep(Duration::from_millis(1));
      }
   });
   for i in 1..5 {
      println!("hi number {} from the main thread!", i);
      thread::sleep(Duration::from_millis(1));
   }
   handle.join().unwrap();
}

Đầu ra

hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the spawned thread!
hi number 2 from the main thread!
hi number 3 from the spawned thread!
hi number 3 from the main thread!
hi number 4 from the main thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!

Chủ đề chính và luồng sinh sản tiếp tục chuyển đổi.

NOTE - Luồng chính chờ luồng sinh sản hoàn thành vì lệnh gọi đến join() phương pháp.