Учебники

Rust — Краткое руководство

Rust — Введение

Rust — язык программирования системного уровня, разработанный Грейдоном Хоаром. Позже Mozilla Labs приобрела эту программу.

Применение v / s Языки системного программирования

Языки прикладного программирования, такие как Java / C #, используются для создания программного обеспечения, которое предоставляет услуги непосредственно пользователю. Они помогают нам создавать бизнес-приложения, такие как электронные таблицы, текстовые процессоры, веб-приложения или мобильные приложения.

Языки системного программирования, такие как C / C ++, используются для создания программных и программных платформ. Их можно использовать для создания операционных систем, игровых движков, компиляторов и т. Д. Эти языки программирования требуют большой степени взаимодействия с оборудованием.

Системы и языки программирования приложений сталкиваются с двумя основными проблемами:

  • Трудно написать безопасный код.
  • Сложно писать многопоточный код.

Почему ржавчина?

Руст фокусируется на трех целях —

  • безопасности
  • скорость
  • совпадение

Язык был разработан для простой и быстрой разработки высоконадежного и быстрого программного обеспечения. Rust может быть использован для написания высокоуровневых программ для аппаратно-ориентированных программ.

Спектакль

Язык программирования Rust не имеет сборщика мусора (GC). Это повышает производительность во время выполнения.

Безопасность памяти во время компиляции

Программное обеспечение, созданное с использованием Rust, защищено от проблем с памятью, таких как висячие указатели, переполнения буфера и утечки памяти.

Многопоточные приложения

Правила владения и безопасности памяти Rust обеспечивают параллелизм без гонок данных.

Поддержка веб-сборки (WASM)

Веб-сборка помогает выполнять высокоинтенсивные алгоритмы в браузере, на встроенных устройствах или где-либо еще. Он работает со скоростью нативного кода. Rust может быть скомпилирован в Web Assembly для быстрого и надежного выполнения.

Rust — Настройка среды

Установка Rust упрощается благодаря инструменту Rustup , основанному на консоли, для управления версиями Rust и соответствующими инструментами.

Установка на Windows

Давайте узнаем, как установить RUST на Windows.

  • Установка Visual Studio 2013 или более поздней версии с инструментами C ++ является обязательной для запуска программы Rust в Windows. Во-первых, скачайте Visual Studio отсюда VS 2013 Express

  • Скачайте и установите инструмент ржавчины для Windows. rustup-init.exe доступен для скачивания здесь — Rust Lang

  • Дважды щелкните файл rustup-init.exe . После нажатия появится следующий экран.

Установка Visual Studio 2013 или более поздней версии с инструментами C ++ является обязательной для запуска программы Rust в Windows. Во-первых, скачайте Visual Studio отсюда VS 2013 Express

Скачайте и установите инструмент ржавчины для Windows. rustup-init.exe доступен для скачивания здесь — Rust Lang

Дважды щелкните файл rustup-init.exe . После нажатия появится следующий экран.

Установка на Windows

  • Нажмите ввод для установки по умолчанию. После завершения установки появится следующий экран.

Нажмите ввод для установки по умолчанию. После завершения установки появится следующий экран.

Установка завершена

  • С экрана установки видно, что файлы, связанные с Rust, хранятся в папке —

    C:. \ Users \ {PC} \ груз \ Bin

С экрана установки видно, что файлы, связанные с Rust, хранятся в папке —

C:. \ Users \ {PC} \ груз \ Bin

Содержимое папки —

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
  • Cargo — менеджер пакетов для Rust. Чтобы проверить, установлен ли груз , выполните следующую команду —

Cargo — менеджер пакетов для Rust. Чтобы проверить, установлен ли груз , выполните следующую команду —

C:\Users\Admin>cargo -V
cargo 1.29.0 (524a578d7 2018-08-05)
  • Компилятор для Rust является rustc . Чтобы проверить версию компилятора, выполните следующую команду —

Компилятор для Rust является rustc . Чтобы проверить версию компилятора, выполните следующую команду —

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

Установка на Linux / Mac

Чтобы установить rustup в Linux или macOS, откройте терминал и введите следующую команду.

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

Команда загружает скрипт и запускает установку инструмента rustup , которая устанавливает последнюю стабильную версию Rust. Вам может быть предложено ввести пароль. Если установка прошла успешно, появится следующая строка:

Rust is installed now. Great!

Сценарий установки автоматически добавляет Rust к вашей системной переменной PATH после вашего следующего входа в систему. Чтобы сразу начать использовать Rust вместо перезапуска терминала, выполните следующую команду в своей оболочке, чтобы вручную добавить Rust в системный PATH —

$ source $HOME/.cargo/env

Кроме того, вы можете добавить следующую строку в ваш ~ / .bash_profile —

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

ПРИМЕЧАНИЕ. — Когда вы пытаетесь скомпилировать программу Rust и получаете ошибки, указывающие на то, что компоновщик не может работать, это означает, что компоновщик не установлен в вашей системе, и вам нужно будет установить его вручную.

Использование учебников Точка кодирования для RUST

Цикл чтения-оценки-печати (REPL) — это простая в использовании интерактивная оболочка для компиляции и выполнения компьютерных программ. Если вы хотите компилировать и запускать программы Rust онлайн в браузере, используйте Tutorialspoint Coding Ground .

Rust — пример HelloWorld

В этой главе объясняется основной синтаксис языка Rust на примере HelloWorld .

  • Создайте папку HelloWorld-App и перейдите к этой папке на терминале.

Создайте папку HelloWorld-App и перейдите к этой папке на терминале.

C:\Users\Admin>mkdir HelloWorld-App
C:\Users\Admin>cd HelloWorld-App
C:\Users\Admin\HelloWorld-App>
  • Чтобы создать файл Rust, выполните следующую команду —

Чтобы создать файл Rust, выполните следующую команду —

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

Программные файлы Rust имеют расширение .rs. Приведенная выше команда создает пустой файл Hello.rs и открывает его в NOTEpad. Добавьте приведенный ниже код в этот файл —

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

Вышеприведенная программа определяет функцию main для main () . Ключевое слово fn используется для определения функции. Main () — это предопределенная функция, которая действует как точка входа в программу. Println! предопределенный макрос в Rust Он используется для вывода строки (здесь Hello) на консоль. Макрозвонки всегда помечаются восклицательным знаком — ! ,

  • Скомпилируйте файл Hello.rs с помощью rustc .

Скомпилируйте файл Hello.rs с помощью rustc .

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

После успешной компиляции программы создается исполняемый файл ( file_name.exe ). Чтобы проверить, создан ли файл .exe , выполните следующую команду.

C:\Users\Admin\HelloWorld-App>dir
//lists the files in folder
Hello.exe
Hello.pdb
Hello.rs
  • Запустите файл Hello.exe и проверьте вывод.

Что такое макрос?

Rust предоставляет мощную систему макросов, которая позволяет метапрограммировать. Как вы видели в предыдущем примере, макросы выглядят как функции, за исключением того, что их имя оканчивается на удар (!), Но вместо генерации вызова функции макросы расширяются до исходного кода, который компилируется с остальной частью программы. Следовательно, они предоставляют больше функций времени выполнения программе, чем функции. Макросы — это расширенная версия функций.

Использование println! Макрос — Синтаксис

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

Комментарии в Rust

Комментарии — это способ улучшить читаемость программы. Комментарии могут использоваться для включения дополнительной информации о программе, такой как автор кода, подсказки о функции / конструкции и т. Д. Компилятор игнорирует комментарии.

Rust поддерживает следующие типы комментариев —

  • Однострочные комментарии (//) — любой текст между // и концом строки рассматривается как комментарий

  • Многострочные комментарии (/ * * /) — эти комментарии могут занимать несколько строк.

Однострочные комментарии (//) — любой текст между // и концом строки рассматривается как комментарий

Многострочные комментарии (/ * * /) — эти комментарии могут занимать несколько строк.

пример

//this is single line comment

/* This is a
   Multi-line comment
*/

Выполнить онлайн

Программы Rust могут быть выполнены онлайн через Tutorialspoint Coding Ground . Напишите программу HelloWorld на вкладке «Редактор» и нажмите «Выполнить», чтобы просмотреть результат.

Выполнить онлайн

Rust — Типы данных

Система типов представляет различные типы значений, поддерживаемые языком. Система типов проверяет достоверность предоставленных значений, прежде чем они будут сохранены или обработаны программой. Это гарантирует, что код ведет себя как ожидалось. Система типов, кроме того, допускает более богатую подсказку по коду и автоматизированную документацию.

Rust — статически типизированный язык. Каждое значение в Rust имеет определенный тип данных. Компилятор может автоматически определить тип данных переменной на основе присвоенного ему значения.

Объявить переменную

Используйте ключевое слово let для объявления переменной.

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);
}

В приведенном выше примере тип данных переменных будет выведен из значений, присвоенных им. Например, Rust назначит строковый тип данных для переменной company_string , тип данных float для rating_float и т. Д.

Печать! макрос принимает два аргумента —

  • Специальный синтаксис {} , который является заполнителем
  • Имя переменной или константа

Заполнитель будет заменен значением переменной

Вывод приведенного выше фрагмента кода будет —

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

Скалярные Типы

Скалярный тип представляет одно значение. Например, 10,3.14, «с». Ржавчина имеет четыре основных скалярных типа.

  • целое число
  • Плавающая запятая
  • Булевы
  • Персонажи

Мы узнаем о каждом типе в наших последующих разделах.

целое число

Целое число — это число без дробного компонента. Проще говоря, целочисленный тип данных используется для представления целых чисел.

Целые числа могут быть далее классифицированы как подписанные и неподписанные. Целые числа со знаком могут хранить как отрицательные, так и положительные значения. Целые числа без знака могут хранить только положительные значения. Подробное описание, если целочисленные типы приведены ниже —

Sr.No. Размер подписанный неподписанный
1 8 бит i8 u8
2 16 бит i16 u16
3 32 бит i32 u32
4 64 бит i64 u64
5 128 бит i128 u128
6 арочный ISize usize

Размер целого числа может быть аркой . Это означает, что размер типа данных будет получен из архитектуры машины. Целое число, размер которого равен arch, будет 32 бита на компьютере x86 и 64 бита на компьютере x64. Целое число арки в основном используется при индексации некоторой коллекции.

иллюстрация

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);
}

Выход будет как указано ниже —

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

Приведенный выше код вернет ошибку компиляции, если вы замените значение age значением с плавающей запятой.

Целочисленный диапазон

Каждый подписанный вариант может хранить числа от — (2 ^ (n-1) до 2 ^ (n-1) -1 , где n — количество битов, которые использует вариант. Например, i8 может хранить числа от — (2 ^ 7) до 2 ^ 7 -1 — здесь мы заменили n на 8.

Каждый вариант без знака может хранить числа от 0 до 2 ^ (n-1) . Например, u8 может хранить числа от 0 до 2 ^ 7 , что равно от 0 до 255.

Целочисленное переполнение

Целочисленное переполнение происходит, когда значение, назначенное целочисленной переменной, превышает определенный Rust диапазон для типа данных. Позвольте нам понять это на примере —

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);
}

Допустимый диапазон переменных без знака u8 — от 0 до 255. В приведенном выше примере переменным присваиваются значения, превышающие 255 (верхний предел для целочисленной переменной в Rust). При выполнении приведенный выше код вернет предупреждение — предупреждение — литерал вне диапазона для u8 для переменных веса, роста и оценки. Значения переполнения после 255 начнутся с 0, 1, 2 и т. Д. Окончательный результат без предупреждения, как показано ниже —

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

терка

Тип данных с плавающей точкой в ​​Rust можно классифицировать как f32 и f64 . Тип f32 является плавающим с одинарной точностью, а f64 имеет двойную точность. Тип по умолчанию — f64. Рассмотрим следующий пример, чтобы лучше понять тип данных с плавающей точкой.

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);
}

Вывод будет таким, как показано ниже —

interest is 8.35
cost is 15000.6

Автоматическое литье типа

Автоматическое приведение типов не разрешено в Rust. Рассмотрим следующий фрагмент кода. Целочисленное значение присваивается переменной с плавающей запятой.

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

Компилятор выдает ошибку несоответствующих типов, как указано ниже.

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)

Числовой разделитель

Для удобства чтения больших чисел мы можем использовать визуальный разделитель _, подчеркивание для разделения цифр. То есть 50 000 можно записать как 50_000. Это показано в примере ниже.

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);
}

Выход дан ниже —

float value 11000.555001
int value 50000

логический

Булевы типы имеют два возможных значения — true или false . Используйте ключевое слово bool для объявления логической переменной.

иллюстрация

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

Вывод приведенного выше кода будет —

Is Rust Programming Fun ? true

символ

Тип символьных данных в Rust поддерживает цифры, алфавиты, Unicode и специальные символы. Используйте ключевое слово char, чтобы объявить переменную символьного типа данных. Тип char в Rust представляет скалярное значение Unicode, что означает, что он может представлять гораздо больше, чем просто ASCII. Скалярные значения Юникода варьируются от U + 0000 до U + D7FF и от U + E000 до U + 10FFFF включительно.

Давайте рассмотрим пример, чтобы лучше понять тип данных Character.

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);
}

Вывод приведенного выше кода будет —

special character is @
alphabet is A
emoji is ?

Rust — Переменные

Переменная — это именованное хранилище, которым могут манипулировать программы. Проще говоря, переменная помогает программам хранить значения. Переменные в Rust связаны с конкретным типом данных. Тип данных определяет размер и расположение памяти переменной, диапазон значений, которые могут храниться в этой памяти, и набор операций, которые можно выполнять с переменной.

Правила именования переменных

В этом разделе мы узнаем о различных правилах именования переменных.

  • Имя переменной может состоять из букв, цифр и символа подчеркивания.

  • Он должен начинаться либо с буквы, либо с подчеркивания.

  • Прописные и строчные буквы различаются, поскольку в Rust учитывается регистр.

Имя переменной может состоять из букв, цифр и символа подчеркивания.

Он должен начинаться либо с буквы, либо с подчеркивания.

Прописные и строчные буквы различаются, поскольку в Rust учитывается регистр.

Синтаксис

Тип данных является необязательным при объявлении переменной в Rust. Тип данных определяется по значению, присвоенному переменной.

Синтаксис объявления переменной приведен ниже.

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

иллюстрация

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

Результатом вышеприведенного кода будет плата 25000 и зарплата 35000 .

Неизменный

По умолчанию переменные являются неизменяемыми — только для чтения в Rust. Другими словами, значение переменной не может быть изменено, если значение привязано к имени переменной.

Позвольте нам понять это на примере.

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

Вывод будет таким, как показано ниже —

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)

Сообщение об ошибке указывает причину ошибки — вы не можете назначать значения дважды для неизменяемых переменных сборов. Это один из многих способов, с помощью которых Rust позволяет программистам писать код и использует преимущества безопасности и простого параллелизма.

изменчивый

Переменные являются неизменяемыми по умолчанию. Префикс имени переменной с ключевым словом mut, чтобы сделать его изменяемым. Значение изменяемой переменной может быть изменено.

Синтаксис объявления изменяемой переменной показан ниже:

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);
}

Вывод фрагмента приведен ниже —

fees is 25000
fees changed is 35000

Руст — Константа

Константы представляют значения, которые нельзя изменить. Если вы объявите константу, то ее значение не изменится. Ключевое слово для использования констант — const . Константы должны быть явно напечатаны. Ниже приведен синтаксис объявления константы.

const VARIABLE_NAME:dataType = value;

Соглашение о присвоении имен в Rust Constant

Соглашение об именовании для констант аналогично соглашению о переменных. Все символы в имени константы обычно в верхнем регистре. В отличие от объявления переменных, ключевое слово let не используется для объявления константы.

Мы использовали константы в Rust в примере ниже —

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
}

Константы v / s Переменные

В этом разделе мы узнаем о различиях между константами и переменными.

  • Константы объявляются с помощью ключевого слова const, а переменные объявляются с помощью ключевого слова let .

  • Объявление переменной может опционально иметь тип данных, тогда как объявление константы должно указывать тип данных. Это означает, что const USER_LIMIT = 100 приведет к ошибке.

  • Переменная, объявленная с использованием ключевого слова let, по умолчанию неизменна. Однако у вас есть возможность изменить его, используя ключевое слово mut . Константы неизменны.

  • Константы могут быть установлены только для константного выражения, а не для результата вызова функции или любого другого значения, которое будет вычислено во время выполнения.

  • Константы могут быть объявлены в любой области, включая глобальную область, что делает их полезными для значений, о которых должны знать многие части кода.

Константы объявляются с помощью ключевого слова const, а переменные объявляются с помощью ключевого слова let .

Объявление переменной может опционально иметь тип данных, тогда как объявление константы должно указывать тип данных. Это означает, что const USER_LIMIT = 100 приведет к ошибке.

Переменная, объявленная с использованием ключевого слова let, по умолчанию неизменна. Однако у вас есть возможность изменить его, используя ключевое слово mut . Константы неизменны.

Константы могут быть установлены только для константного выражения, а не для результата вызова функции или любого другого значения, которое будет вычислено во время выполнения.

Константы могут быть объявлены в любой области, включая глобальную область, что делает их полезными для значений, о которых должны знать многие части кода.

Затенение переменных и констант

Rust позволяет программистам объявлять переменные с одинаковыми именами. В таком случае новая переменная переопределяет предыдущую переменную.

Позвольте нам понять это на примере.

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

Приведенный выше код объявляет две переменные с именем зарплата. Первому объявлению присваивается значение 100,00, а второму объявлению присваивается значение 1,50. Вторая переменная скрывает или скрывает первую переменную при отображении вывода.

Выход

The value of salary is :1.50

Rust поддерживает переменные с разными типами данных при теневом копировании.

Рассмотрим следующий пример.

Код объявляет две переменные с именем uname . Первому объявлению присваивается строковое значение, тогда как второму объявлению присваивается целое число. Функция len возвращает общее количество символов в строковом значении.

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

Выход

name changed to integer: 9

В отличие от переменных, константы не могут быть скрыты. Если переменные в вышеупомянутой программе заменяются константами, компилятор выдаст ошибку.

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

Rust — String

Тип данных String в Rust можно классифицировать на следующие:

  • Строковый литерал (& str)

  • String Object (Строка)

Строковый литерал (& str)

String Object (Строка)

Строковый литерал

Строковые литералы (& str) используются, когда значение строки известно во время компиляции. Строковые литералы — это набор символов, которые жестко закодированы в переменную. Например, пусть company = «Tutorials Point» . Строковые литералы находятся в модуле std :: str. Строковые литералы также известны как строковые фрагменты.

В следующем примере объявляются два строковых литерала — компания и местоположение .

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

Строковые литералы являются статическими по умолчанию. Это означает, что строковые литералы гарантированно будут действительны в течение всей программы. Мы также можем явно указать переменную как статическую, как показано ниже —

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

Вышеуказанная программа сгенерирует следующий вывод —

company is : TutorialsPoint location :Hyderabad

String Object

Тип объекта String предоставляется в стандартной библиотеке. В отличие от строкового литерала, тип строкового объекта не является частью основного языка. Он определен как публичная структура в стандартной библиотеке pub struct String . String — это растущая коллекция. Это изменчивый тип UTF-8. Тип объекта String может использоваться для представления строковых значений, которые предоставляются во время выполнения. Строковый объект размещается в куче.

Синтаксис

Чтобы создать объект String, мы можем использовать любой из следующих синтаксисов:

String::new()

Приведенный выше синтаксис создает пустую строку

String::from()

Это создает строку с некоторым значением по умолчанию, переданным в качестве параметра методу from () .

Следующий пример иллюстрирует использование объекта 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());
}

В приведенном выше примере создаются две строки — пустой строковый объект с использованием нового метода и строковый объект из строкового литерала с использованием метода from .

Вывод как показано ниже —

length is 0
length is 14

Общие методы — String Object

Sr.No. метод Подпись Описание
1 новый () pub const fn new () → Строка Создает новую пустую строку.
2 нанизывать() fn to_string (& self) → String Преобразует данное значение в строку.
3 заменить () pub fn replace <‘a, P> (&’ a self, from: P, to: & str) → String Заменяет все совпадения шаблона другой строкой.
4 as_str () паб fn as_str (& self) → & str Извлекает фрагмент строки, содержащий всю строку.
5 От себя() паб fn push (& mut self, ch: char) Добавляет указанный символ в конец этой строки.
6 push_str () pub fn push_str (& mut self, string: & str) Добавляет данный фрагмент строки в конец этой строки.
7 LEN () паб fn len (& self) → usize Возвращает длину этой строки в байтах.
8 отделка() Паб fn trim (& self) → & str Возвращает фрагмент строки с удаленными начальными и конечными пробелами.
9 split_whitespace () pub fn split_whitespace (& self) → SplitWhitespace Разбивает фрагмент строки по пробелам и возвращает итератор.
10 Трещина() pub fn split <‘a, P> (&’ a self, pat: P) → Split <‘a, P>, где P — это шаблон, который может быть & str, char или замыканием, определяющим разделение. Возвращает итератор для подстрок этого среза строки, разделенных символами, соответствующими шаблону.
11 символы () паб fn chars (& self) → Чарс Возвращает итератор для символов фрагмента строки.

Иллюстрация: новый ()

Пустой строковый объект создается с использованием метода new (), и его значение устанавливается в hello .

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

Выход

Вышеуказанная программа генерирует следующий вывод —

hello

Иллюстрация: to_string ()

Чтобы получить доступ ко всем методам объекта String, преобразуйте строковый литерал в тип объекта с помощью функции to_string () .

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

Выход

Вышеуказанная программа генерирует следующий вывод —

Hello TutorialsPoint , Hello!

Иллюстрация: заменить ()

Функция replace () принимает два параметра: первый параметр — это строковый шаблон для поиска, а второй параметр — новое значение, которое необходимо заменить. В приведенном выше примере Hello появляется два раза в строке name1 .

Функция replace заменяет все вхождения строки Hello на Howdy .

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

Выход

Вышеуказанная программа генерирует следующий вывод —

Howdy TutorialsPoint , Howdy!

Иллюстрация: as_str ()

Функция as_str () извлекает фрагмент строки, содержащий всю строку.

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);
}

Выход

Вышеуказанная программа генерирует следующий вывод —

displaying string literal example_string

Иллюстрация: push ()

Функция push () добавляет указанный символ в конец этой строки.

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

Выход

Вышеуказанная программа генерирует следующий вывод —

Tutorials

Иллюстрация: push_str ()

Функция push_str () добавляет данный фрагмент строки в конец строки.

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

Выход

Вышеуказанная программа генерирует следующий вывод —

Tutorials Point

Иллюстрация: len ()

Функция len () возвращает общее количество символов в строке (включая пробелы).

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

Выход

Вышеуказанная программа генерирует следующий вывод —

length is 20

Иллюстрация: отделка ()

Функция trim () удаляет начальные и конечные пробелы в строке. Обратите внимание, что эта функция не удалит встроенные пробелы.

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());
}

Выход

Вышеуказанная программа генерирует следующий вывод —

Before trim
length is 24

After trim
length is 15

Иллюстрация: split_whitespace ()

Split_whitespace () разбивает входную строку на разные строки. Он возвращает итератор, поэтому мы перебираем токены, как показано ниже —

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;
   }
}

Выход

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

Иллюстрация: строка split ()

Метод строки split () возвращает итератор для подстрок фрагмента строки, разделенных символами, соответствующими шаблону. Ограничение метода split () состоит в том, что результат не может быть сохранен для дальнейшего использования. Метод collect может использоваться для хранения результата, возвращаемого split (), как вектора.

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]);
}

Приведенный выше пример разбивает строку на полное имя всякий раз, когда встречается запятая (,) .

Выход

token is Kannan
token is Sudhakaran
token is Tutorialspoint

firstName is Kannan
lastname is Sudhakaran
company is Tutorialspoint

Иллюстрация: символы ()

Отдельные символы в строке могут быть доступны с помощью метода chars. Давайте рассмотрим пример, чтобы понять это.

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

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

Выход

T
u
t
o
r
i
a
l
s

Конкатенация строк с оператором +

Строковое значение может быть добавлено к другой строке. Это называется конкатенацией или интерполяцией. Результатом конкатенации строк является новый строковый объект. Оператор + внутренне использует метод add . Синтаксис функции add принимает два параметра. Первый параметр self — сам строковый объект, а второй параметр — ссылка на второй строковый объект. Это показано ниже —

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

Иллюстрация: Конкатенация строк

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

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

Выход будет как указано ниже

TutorialsPoint

Иллюстрация: Тип литья

В следующем примере показано преобразование числа в строковый объект —

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");
}

Выход будет как указано ниже

2020
true

Иллюстрация: формат! макрос

Еще один способ добавить к объектам String вместе — это использовать макро-функцию, называемую format. Использование формата! как показано ниже.

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

Выход будет как указано ниже

Tutorials Point

Руст — Операторы

Оператор определяет некоторую функцию, которая будет выполняться над данными. Данные, с которыми работают операторы, называются операндами. Рассмотрим следующее выражение —

7 + 5 = 12

Здесь значения 7, 5 и 12 являются операндами, а + и = — операторами.

Основные операторы в Rust могут быть классифицированы как —

  • арифметика
  • побитовое
  • сравнение
  • логический
  • побитовое
  • условный

Арифметические Операторы

Предположим, значения в переменных a и b равны 10 и 5 соответственно.

Показать примеры

Sr.No оператор Описание пример
1 + (Сложение) возвращает сумму операндов А + В 15
2 -(Вычитание) возвращает разницу значений аб 5
3 * (Умножение) возвращает произведение значений а * б 50
4 / (Отдел) выполняет операцию деления и возвращает частное а / б 2
5 % (Модуль) выполняет операцию деления и возвращает остаток % b равно 0

ПРИМЕЧАНИЕ. — Операторы ++ и — не поддерживаются в Rust.

Операторы отношений

Реляционные операторы проверяют или определяют тип отношений между двумя объектами. Реляционные операторы используются для сравнения двух или более значений. Реляционные операторы возвращают логическое значение — true или false.

Предположим, что значение A равно 10, а B равно 20.

Показать примеры

Sr.No оператор Описание пример
1 > Лучше чем (A> B) Неверно
2 < Меньше чем (A <B) верно
3 > = Больше или равно (A> = B) Неверно
4 <= Меньше или равно (A <= B) верно
5 == равенство (A == B) это фальш
6 знак равно Не равный (A! = B) верно

Логические Операторы

Логические операторы используются для объединения двух или более условий. Логические операторы тоже возвращают логическое значение. Предположим, что значение переменной A равно 10, а B равно 20.

Показать примеры

Sr.No оператор Описание пример
1 && (А также) Оператор возвращает true, только если все указанные выражения возвращают true (A> 10 && B> 10) Неверно
2 || (ИЛИ) Оператор возвращает true, если хотя бы одно из указанных выражений возвращает true (A> 10 || B> 10) верно
3 ! (НЕ) Оператор возвращает значение, обратное результату выражения. Например,! (> 5) возвращает false ! (A> 10) верно

Битовые операторы

Предположим, переменная A = 2 и B = 3.

Показать примеры

Sr.No оператор Описание пример
1 & (Побитовое И) Он выполняет логическую операцию И над каждым битом своих целочисленных аргументов. (A & B) составляет 2
2 | (BitWise ИЛИ) Он выполняет логическую операцию ИЛИ для каждого бита своих целочисленных аргументов. (A | B) равно 3
3 ^ (Побитовый XOR) Он выполняет логическую исключающую операцию ИЛИ для каждого бита своих целочисленных аргументов. Исключающее ИЛИ означает, что либо операнд один является истинным, либо операнд два является истинным, но не оба. (A ^ B) равно 1
4 ! (Поразрядно нет) Это унарный оператор, действующий путем обращения всех битов в операнде. (! B) -4
5 << (Сдвиг влево) Он перемещает все биты в своем первом операнде влево на количество мест, указанное во втором операнде. Новые биты заполнены нулями. Смещение значения влево на одну позицию эквивалентно умножению его на 2, смещение двух позиций эквивалентно умножению на 4 и так далее. (A << 1) равно 4
6 >> (Сдвиг вправо) Оператор двоичного правого сдвига. Значение левого операнда перемещается вправо на количество битов, указанное правым операндом. (A >> 1) равно 1
7 >>> (Сдвиг вправо с нулем) Этот оператор аналогичен оператору >>, за исключением того, что биты, сдвинутые влево, всегда равны нулю. (A >>> 1) равно 1

Rust — Принятие решений

Структуры принятия решений требуют, чтобы программист указал одно или несколько условий, которые должны быть оценены или протестированы программой, вместе с оператором или инструкциями, которые должны быть выполнены, если условие определено как истинное, и, необязательно, другие операторы, которые должны быть выполнены, если условие определяется как ложное.

Ниже показана общая форма типичной структуры принятия решений, встречающейся в большинстве языков программирования.

Принятие решения

Sr.No Заявление и описание
1

если заявление

Оператор if состоит из логического выражения, за которым следует один или несколько операторов.

2

если … еще заявление

За оператором if может следовать необязательный оператор else , который выполняется, когда логическое выражение имеет значение false.

3

еще … если и вложенный ifstatement

Вы можете использовать один оператор if или else if внутри другого оператора if или else if .

4

заявление о совпадении

Оператор соответствия позволяет проверять переменную на соответствие списку значений.

если заявление

Оператор if состоит из логического выражения, за которым следует один или несколько операторов.

если … еще заявление

За оператором if может следовать необязательный оператор else , который выполняется, когда логическое выражение имеет значение false.

еще … если и вложенный ifstatement

Вы можете использовать один оператор if или else if внутри другого оператора if или else if .

заявление о совпадении

Оператор соответствия позволяет проверять переменную на соответствие списку значений.

Если заявление

Конструкция if… else оценивает условие перед выполнением блока кода.

Синтаксис

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

Если логическое выражение оценивается как true, тогда будет выполнен блок кода внутри оператора if. Если логическое выражение имеет значение false, то будет выполнен первый набор кода после конца оператора if (после закрывающей фигурной скобки).

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

В приведенном выше примере будет напечатано число положительно, поскольку условие, указанное в блоке if, имеет значение true.

если еще заявление

За if может следовать необязательный блок else . Блок else будет выполнен, если логическое выражение, проверенное оператором if, оценивается как false.

Синтаксис

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

блок-схема

блок-схема

Блок if защищает условное выражение. Блок, связанный с оператором if, выполняется, если логическое выражение имеет значение true.

Блок if может сопровождаться необязательным оператором else. Блок инструкций, связанный с блоком else, выполняется, если выражение оценивается как false.

Иллюстрация — Просто если… еще

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

Приведенный выше пример показывает, является ли значение в переменной четным или нечетным. Блок if проверяет делимость значения на 2, чтобы определить то же самое. Вот вывод приведенного выше кода —

Even

Вложенный, если

Еще … если лестница полезна для тестирования нескольких условий. Синтаксис как показано ниже —

Синтаксис

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
}

При использовании операторов if… else… if и else необходимо учитывать несколько моментов.

  • У if может быть ноль или еще один, и он должен следовать за любым другим .. if.
  • Если может иметь ноль для многих других .. если и они должны предшествовать остальному.
  • Как только else..if будет успешным, ни один из оставшихся else..if или else не будет проверен.

Пример: еще … если лестница

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) ;
   }
}

Фрагмент показывает, является ли значение положительным, отрицательным или нулевым.

Выход

2 is positive

Заявление о совпадении

Оператор match проверяет, совпадает ли текущее значение из списка значений, это очень похоже на оператор switch в языке Си. Во-первых, обратите внимание, что выражение после ключевого слова соответствия не обязательно заключать в скобки.

Синтаксис как показано ниже.

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

В примере , приведенном ниже, state_code сравнивается со списком значений МН, KL, К., Г. А. — если совпадение найдено, то значение возвращается строка переменной состояния. Если совпадений не найдено, регистр по умолчанию _ совпадает и возвращается значение Unkown .

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);
}

Выход

Found match for MH
State name is Maharashtra

Ржавчина — петля

Могут быть случаи, когда блок кода должен выполняться повторно. В общем, инструкции программирования выполняются последовательно: первый оператор в функции выполняется первым, затем второй и так далее.

Языки программирования предоставляют различные управляющие структуры, которые допускают более сложные пути выполнения.

Оператор цикла позволяет нам выполнять оператор или группу операторов несколько раз. Ниже приведена общая форма оператора цикла в большинстве языков программирования.

оператор цикла

Rust предоставляет различные типы циклов для обработки требований циклов —

  • в то время как
  • петля
  • за

Определенная петля

Цикл, число итераций которого является определенным / фиксированным, называется определенным циклом. Цикл for является реализацией определенного цикла.

Для петли

Цикл for выполняет блок кода указанное количество раз. Его можно использовать для перебора фиксированного набора значений, например массива. Синтаксис цикла for приведен ниже.

Синтаксис

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

Пример цикла for показан ниже

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

ПРИМЕЧАНИЕ : переменная x доступна только в блоке for.

Выход

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

Неопределенный цикл

Неопределенный цикл используется, когда число итераций в цикле не определено или неизвестно.

Неопределенные циклы могут быть реализованы с помощью —

Sr.No Имя и описание
1

В то время как

Цикл while выполняет инструкции каждый раз, когда указанное условие оценивается как истинное

2

петля

Цикл — это неопределенный цикл while (true)

В то время как

Цикл while выполняет инструкции каждый раз, когда указанное условие оценивается как истинное

петля

Цикл — это неопределенный цикл while (true)

Иллюстрация — на время

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);
}

Вывод как показано ниже —

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

Иллюстрация-петля

fn main(){
   //while true

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

      if x==15 {
         break;
      }
   }
}

Оператор break используется для извлечения элемента управления из конструкции. Использование прерывания в цикле приводит к тому, что программа выходит из цикла.

Выход

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

Продолжить заявление

Оператор continue пропускает последующие операторы в текущей итерации и возвращает элемент управления в начало цикла. В отличие от оператора break, continue не завершает цикл. Он завершает текущую итерацию и начинает последующую итерацию.

Пример оператора 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
}

В приведенном выше примере отображается число четных значений от 0 до 20. Цикл выходит из текущей итерации, если число четное. Это достигается с помощью оператора continue.

Количество нечетных значений от 0 до 20 равно 10

Rust — Функции

Функции являются строительными блоками читаемого, обслуживаемого и многократно используемого кода. Функция — это набор операторов для выполнения конкретной задачи. Функции организуют программу в логические блоки кода. После определения функции могут вызываться для доступа к коду. Это делает код многоразовым. Кроме того, функции позволяют легко читать и поддерживать код программы.

Объявление функции сообщает компилятору об имени функции, типе возврата и параметрах. Определение функции обеспечивает фактическое тело функции.

Sr.No Описание функции
1

Определение функции

Определение функции TA определяет, что и как будет выполнена конкретная задача.

2

Вызов или вызов функции

Функция должна быть вызвана для ее выполнения.

3

Возврат Функции

Функции могут также возвращать значение вместе с контролем обратно к вызывающей стороне.

4

Параметризованная функция

Параметры — это механизм для передачи значений в функции.

Определение функции

Определение функции TA определяет, что и как будет выполнена конкретная задача.

Вызов или вызов функции

Функция должна быть вызвана для ее выполнения.

Возврат Функции

Функции могут также возвращать значение вместе с контролем обратно к вызывающей стороне.

Параметризованная функция

Параметры — это механизм для передачи значений в функции.

Определение функции

Определение функции определяет, что и как будет выполнена конкретная задача. Перед использованием функции ее необходимо определить. Тело функции содержит код, который должен быть выполнен функцией. Правила именования функции аналогичны правилам для переменной. Функции определяются с помощью ключевого слова fn . Синтаксис для определения стандартной функции приведен ниже

Синтаксис

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

Объявление функции может содержать параметры / аргументы. Параметры используются для передачи значений в функции.

Пример — простое определение функции

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

Вызов функции

Функция должна быть вызвана для ее выполнения. Этот процесс называется вызовом функции . Значения параметров должны передаваться при вызове функции. Функция, которая вызывает другую функцию, называется функцией вызова.

Синтаксис

function_name(val1,val2,valN)

Пример: вызов функции

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

Здесь main () — это функция вызова.

иллюстрация

В следующем примере определяется функция fn_hello () . Функция выводит сообщение на консоль. Функция main () вызывает функцию fn_hello () .

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

Выход

hello from function fn_hello

Возвращаемое значение из функции

Функции могут также возвращать значение вместе с контролем обратно вызывающей стороне. Такие функции называются возвращающими функциями.

Синтаксис

Любой из следующего синтаксиса может использоваться для определения функции с типом возврата.

С возвратом

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

Сокращенный синтаксис без оператора возврата

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

lllustration

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

Выход

pi value is 3.142857142857143

Функция с параметрами

Параметры — это механизм для передачи значений в функции. Параметры составляют часть сигнатуры функции. Значения параметров передаются функции во время ее вызова. Если явно не указано, количество значений, передаваемых функции, должно соответствовать количеству определенных параметров.

Параметры могут быть переданы в функцию одним из следующих способов:

Передать по значению

Когда метод вызывается, для каждого параметра значения создается новое место хранения. Значения фактических параметров копируются в них. Следовательно, изменения, внесенные в параметр внутри вызванного метода, не влияют на аргумент.

В следующем примере объявляется переменная no, которая изначально равна 5. Переменная передается как параметр (по значению) в функцию mutate_no_to_zero () , которая изменяет значение на ноль. После вызова функции, когда управление возвращается к основному методу, значение будет таким же.

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);
}

Выход

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

Передача по ссылке

Когда вы передаете параметры по ссылке, в отличие от значений параметров, для этих параметров не создается новое место хранения. Эталонные параметры представляют ту же область памяти, что и фактические параметры, которые передаются в метод. Значения параметров могут быть переданы по ссылке путем добавления префикса имени переменной к & .

В приведенном ниже примере у нас есть переменная no , которая изначально равна 5. Ссылка на переменную no передается функции mutate_no_to_zero () . Функция работает с исходной переменной. После вызова функции, когда управление возвращается обратно в метод main, значение исходной переменной будет равно нулю.

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
}

Оператор * используется для доступа к значению, хранящемуся в ячейке памяти, на которую указывает переменная param_no . Это также известно как разыменование.

Выход будет —

The value of no is 0.

Передача строки в функцию

Функция main () передает строковый объект в функцию 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);
}

Выход

param_name value is :TutorialsPoint

Rust — Tuple

Tuple является составным типом данных. Скалярный тип может хранить только один тип данных. Например, переменная i32 может хранить только одно целочисленное значение. В составных типах мы можем хранить более одного значения за раз, и оно может быть разных типов.

Кортежи имеют фиксированную длину — после объявления они не могут увеличиваться или уменьшаться в размере. Индекс кортежа начинается с 0 .

Синтаксис

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

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

иллюстрация

В следующем примере значения отображаются в кортеже.

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

Синтаксис println! («{}», Tuple) нельзя использовать для отображения значений в кортеже. Это потому, что кортеж является составным типом. Используйте синтаксис println! («{:?}», tuple_name) для печати значений в кортеже.

Выход

(-325, 4.9, 22)

иллюстрация

В следующем примере отдельные значения печатаются в кортеже.

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);
}

Выход

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

иллюстрация

Следующий пример передает кортеж в качестве параметра функции. Кортежи передаются по значению в функции.

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);
}

Выход

Inside print method
(110, true, 10.9)

подрывающий

Разрушение присваивания — это особенность ржавчины, при которой мы распаковываем значения кортежа. Это достигается путем назначения кортежа различным переменным.

Рассмотрим следующий пример —

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);
}

Переменная x — это кортеж, который присваивается оператору let. Каждая переменная — age, is_male и cgpa будет содержать соответствующие значения в кортеже.

Выход

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

Rust — Array

В этой главе мы узнаем о массиве и различных функциях, связанных с ним. Прежде чем мы узнаем о массивах, давайте посмотрим, чем массив отличается от переменной.

Переменные имеют следующие ограничения —

  • Переменные имеют скалярный характер. Другими словами, объявление переменной может содержать только одно значение за раз. Это означает, что для хранения n значений в программе потребуется объявление переменных n. Следовательно, использование переменных неосуществимо, когда нужно хранить большую коллекцию значений.

  • Переменные в программе выделяются памяти в случайном порядке, что затрудняет получение / чтение значений в порядке их объявления.

Переменные имеют скалярный характер. Другими словами, объявление переменной может содержать только одно значение за раз. Это означает, что для хранения n значений в программе потребуется объявление переменных n. Следовательно, использование переменных неосуществимо, когда нужно хранить большую коллекцию значений.

Переменные в программе выделяются памяти в случайном порядке, что затрудняет получение / чтение значений в порядке их объявления.

Массив представляет собой однородную коллекцию значений. Проще говоря, массив — это набор значений одного и того же типа данных.

Особенности массива

Особенности массива перечислены ниже:

  • Объявление массива выделяет последовательные блоки памяти.

  • Массивы статичны. Это означает, что массив после инициализации не может быть изменен.

  • Каждый блок памяти представляет элемент массива.

  • Элементы массива идентифицируются уникальным целым числом, называемым индексом / индексом элемента.

  • Заполнение элементов массива называется инициализацией массива.

  • Значения элементов массива могут быть обновлены или изменены, но не могут быть удалены.

Объявление массива выделяет последовательные блоки памяти.

Массивы статичны. Это означает, что массив после инициализации не может быть изменен.

Каждый блок памяти представляет элемент массива.

Элементы массива идентифицируются уникальным целым числом, называемым индексом / индексом элемента.

Заполнение элементов массива называется инициализацией массива.

Значения элементов массива могут быть обновлены или изменены, но не могут быть удалены.

Объявление и инициализация массивов

Используйте синтаксис, приведенный ниже, чтобы объявить и инициализировать массив в Rust.

Синтаксис

//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];

В первом синтаксисе тип массива выводится из типа данных первого элемента массива во время инициализации.

Иллюстрация: Простой массив

В следующем примере явно указывается размер и тип данных массива. Синтаксис {:?} Функции println! () Используется для печати всех значений в массиве. Функция len () используется для вычисления размера массива.

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

Выход

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

Иллюстрация: массив без типа данных

Следующая программа объявляет массив из 4 элементов. Тип данных не указывается явно во время объявления переменной. В этом случае массив будет иметь тип integer. Функция len () используется для вычисления размера массива.

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

Выход

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

Иллюстрация: значения по умолчанию

В следующем примере создается массив и инициализируются все его элементы со значением по умолчанию -1 .

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

Выход

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

Иллюстрация: Массив с циклом for

В следующем примере выполняется перебор массива и выводятся индексы и их соответствующие значения. Цикл извлекает значения из индекса от 0 до 4 (индекс последнего элемента массива).

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]);
   }
}

Выход

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

Иллюстрация: использование функции iter ()

Функция iter () извлекает значения всех элементов в массиве.

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);
   }
}

Выход

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

Иллюстрация: изменяемый массив

Ключевое слово mut может использоваться для объявления изменяемого массива. В следующем примере объявляется изменяемый массив и изменяется значение второго элемента массива.

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

Выход

[10, 0, 30, 40]

Передача массивов в качестве параметров в функции

Массив может быть передан по значению или по ссылке на функции.

Иллюстрация: передача по значению

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);
}

Выход

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

Иллюстрация: Передача по ссылке

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);
}

Выход

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

Декларация массива и константы

Давайте рассмотрим пример, приведенный ниже, чтобы понять объявление массива и константы.

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

Компилятор приведет к исключению. Это потому, что длина массива должна быть известна во время компиляции. Здесь значение переменной «N» будет определено во время выполнения. Другими словами, переменные не могут быть использованы для определения размера массива.

Тем не менее, следующая программа действительна —

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

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

Значение идентификатора с префиксом ключевого слова const определяется во время компиляции и не может быть изменено во время выполнения. usize имеет размер указателя, поэтому его фактический размер зависит от архитектуры, для которой вы компилируете программу.

Руст — Собственность

Память для программы может быть выделена следующим образом:

  • стек
  • отвал

стек

Стек следует за последним в первом порядке. В стеке хранятся значения данных, размер которых известен во время компиляции. Например, переменная фиксированного размера i32 является кандидатом на выделение стека. Его размер известен во время компиляции. Все скалярные типы могут храниться в стеке, так как размер фиксирован.

Рассмотрим пример строки, которой присваивается значение во время выполнения. Точный размер такой строки не может быть определен во время компиляции. Так что это не кандидат на выделение стека, а на выделение кучи.

отвал

Куча памяти хранит значения данных, размер которых неизвестен во время компиляции. Он используется для хранения динамических данных. Проще говоря, куча памяти выделяется для значений данных, которые могут изменяться в течение жизненного цикла программы. Куча — это область в памяти, которая менее организована по сравнению со стеком.

Что такое собственность?

Каждое значение в Rust имеет переменную, которая называется владельцем значения. Каждые данные, хранящиеся в Rust, будут связаны с владельцем. Например, в синтаксисе — пусть age = 30, age является владельцем значения 30 .

  • Каждые данные могут иметь только одного владельца за раз.

  • Две переменные не могут указывать на одну и ту же область памяти. Переменные всегда будут указывать на разные области памяти.

Каждые данные могут иметь только одного владельца за раз.

Две переменные не могут указывать на одну и ту же область памяти. Переменные всегда будут указывать на разные области памяти.

Передача права собственности

Право собственности на стоимость может быть передано:

  • Присвоение значения одной переменной другой переменной.

  • Передача значения в функцию.

  • Возвращаемое значение из функции.

Присвоение значения одной переменной другой переменной.

Передача значения в функцию.

Возвращаемое значение из функции.

Присвоение значения одной переменной другой переменной

Основное преимущество Rust как языка — безопасность памяти. Безопасность памяти достигается за счет жесткого контроля над тем, кто может использовать что и когда ограничения.

Рассмотрим следующий фрагмент —

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. Идея владения заключается в том, что только одна переменная связывается с ресурсом, либо v связывается с ресурсом, либо v2 связывается с ресурсом. Приведенный выше пример выдает ошибку — использование перемещенного значения: `v` . Это связано с тем, что право собственности на ресурс передается в v2. Это означает, что владение перемещено с v на v2 (v2 = v) и v становится недействительным после перемещения.

Передача значения в функцию

Владение значением также изменяется, когда мы передаем объект в куче замыканию или функции.

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);
}

Возвращаемое значение из функции

Право собственности, переданное функции, будет признано недействительным по завершении выполнения функции. Чтобы обойти это, пусть функция возвращает принадлежащий объект обратно вызывающей стороне.

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);
}

Владение и примитивные типы

В случае примитивных типов содержимое одной переменной копируется в другую. Таким образом, никакого перехода собственности не происходит. Это потому, что примитивная переменная требует меньше ресурсов, чем объект. Рассмотрим следующий пример —

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

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

Выход будет — 10.

Руст — Заимствование

Очень неудобно передавать владение переменной другой функции, а затем возвращать владение. Rust поддерживает концепцию заимствования, когда право собственности на стоимость временно передается сущности, а затем возвращается первоначальному владельцу сущности.

Рассмотрим следующее —

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);
}

Основная функция вызывает функцию print_vector () . Вектор передается в качестве параметра этой функции. Владение вектором также передается функции print_vector () из main () . Приведенный выше код приведет к ошибке, как показано ниже, когда функция main () пытается получить доступ к вектору v .

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

Это связано с тем, что переменная или значение больше не могут использоваться функцией, которой она изначально принадлежала, после передачи права собственности на другую функцию.

Что такое заимствование?

Когда функция временно передает контроль над переменной / значением другой функции, это называется заимствованием. Это достигается передачей ссылки на переменную (& var_name) вместо передачи самой переменной / значения в функцию. Владение переменной / значением передается первоначальному владельцу переменной после завершения выполнения функции, которой был передан элемент управления.

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);
}

Выход

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

Изменчивые ссылки

Функция может изменить заемный ресурс, используя изменяемую ссылку на такой ресурс. Изменяемая ссылка имеет префикс & mut . Изменяемые ссылки могут работать только с изменяемыми переменными.

Иллюстрация: мутация целочисленной ссылки

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

Функция main () объявляет изменяемую целочисленную переменную i и передает изменяемую ссылку i в add_one () . Add_one () увеличивает значение переменной 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
}

Функция main () передает изменяемую ссылку имени переменной в функцию display () . Функция отображения добавляет дополнительную строку к исходной переменной имени .

Выход

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

Ржавчина — ломтики

Срез — это указатель на блок памяти. Срезы могут использоваться для доступа к частям данных, хранящимся в смежных блоках памяти. Его можно использовать со структурами данных, такими как массивы, векторы и строки. Срезы используют индексные номера для доступа к частям данных. Размер среза определяется во время выполнения.

Ломтики — это указатели на фактические данные. Они передаются по ссылке на функции, которые также известны как заимствование.

Например, срезы могут использоваться для извлечения части строкового значения. Нарезанная строка — это указатель на фактический строковый объект. Поэтому нам нужно указать начальный и конечный индекс строки. Индекс начинается с 0, как массивы.

Синтаксис

let sliced_value = &data_structure[start_index..end_index]

Минимальное значение индекса равно 0, а максимальное значение индекса — это размер структуры данных. Обратите внимание, что end_index не будет включен в окончательную строку.

На приведенной ниже схеме показан пример строки учебников , которая состоит из 9 символов. Индекс первого символа равен 0, а индекс последнего символа — 8.

Учебные пособия по струнам

Следующий код выбирает 5 символов из строки (начиная с индекса 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);
}

Выход

length of string is 9
rials

Иллюстрация — Нарезка целочисленного массива

Функция main () объявляет массив из 5 элементов. Он вызывает функцию use_slice () и передает ей фрагмент из трех элементов (указывает на массив данных). Срезы передаются по ссылке. Функция use_slice () печатает значение среза и его длину.

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);
}

Выход

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

Изменчивые ломтики

Ключевое слово & mut может использоваться для пометки фрагмента как изменяемого.

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
}

Выход

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

Приведенный выше код передает изменяемый фрагмент функции use_slice () . Функция изменяет второй элемент исходного массива.

Ржавчина — Структура

Массивы используются для представления однородного набора значений. Аналогично, структура — это еще один определенный пользователем тип данных, доступный в Rust, который позволяет объединять элементы данных разных типов, включая другую структуру. Структура определяет данные как пару ключ-значение.

Синтаксис — Объявление структуры

Ключевое слово struct используется для объявления структуры. Поскольку структуры статически типизированы, каждое поле в структуре должно быть связано с типом данных. Правила именования и соглашения для структуры аналогичны переменным. Структурный блок должен заканчиваться точкой с запятой.

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

Синтаксис — Инициализация структуры

После объявления структуры каждому полю должно быть присвоено значение. Это известно как инициализация.

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);
}

В приведенном выше примере объявляется структура Employee с тремя полями — имя, компания и возраст типов. Функция main () инициализирует структуру. Он использует println! макрос для печати значений полей, определенных в структуре.

Выход

Name is :Mohtashim company is TutorialsPoint age is 50

Изменение экземпляра структуры

Чтобы изменить экземпляр, переменная экземпляра должна быть помечена как изменяемая. В приведенном ниже примере объявляется и инициализируется структура с именем Employee, а затем изменяется значение поля age до 40 с 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);

Выход

Name is :Mohtashim company is TutorialsPoint age is 40

Передача структуры в функцию

В следующем примере показано, как передать экземпляр структуры в качестве параметра. Метод display принимает экземпляр 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
}
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);
}

Выход

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

Возвращение структуры из функции

Давайте рассмотрим функцию who_is_elder () , которая сравнивает возраст двух сотрудников и возвращает старшего.

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

Вот полная программа —

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
}

Выход

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

Метод в структуре

Методы похожи на функции. Это логическая группа инструкций по программированию. Методы объявлены с ключевым словом fn . Область действия метода находится внутри структурного блока.

Методы объявляются вне структурного блока. Ключевое слово impl используется для определения метода в контексте структуры. Первый параметр метода всегда будет self , который представляет вызывающий экземпляр структуры. Методы работают с данными-членами структуры.

Чтобы вызвать метод, нам нужно сначала создать экземпляр структуры. Метод может быть вызван с использованием экземпляра структуры.

Синтаксис

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

иллюстрация

В следующем примере определяется структура Rectangle с полями — ширина и высота . Область метода определяется в контексте структуры. Метод area получает доступ к полям структуры через ключевое слово self и вычисляет площадь прямоугольника.

//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());
}

Выход

width is 10 height is 20 area of Rectangle is 200

Статический метод в структуре

Статические методы могут быть использованы в качестве служебных методов. Эти методы существуют еще до создания структуры. Статические методы вызываются с использованием имени структуры и могут быть доступны без экземпляра. В отличие от обычных методов, статический метод не будет принимать параметр & self .

Синтаксис — Объявление статического метода

Статический метод, такой как функции и другие методы, может содержать параметры.

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

Синтаксис — вызов статического метода

Имя_структуры :: синтаксис используется для доступа к статическому методу.

structure_name::method_name(v1,v2)

иллюстрация

В следующем примере метод getInstance используется в качестве фабричного класса, который создает и возвращает экземпляры структуры 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();
}

Выход

x =10 y=20

Rust — Enums

В программировании Rust, когда нам нужно выбрать значение из списка возможных вариантов, мы используем типы данных перечисления. Перечислимый тип объявляется с использованием ключевого слова enum . Ниже приводится синтаксис enum —

enum enum_name {
   variant1,
   variant2,
   variant3
}

Иллюстрация: использование перечисления

В этом примере объявляется enum — GenderCategory , в котором есть варианты для мужчин и женщин. Печать! макрос отображает значение перечисления. Компилятор выдаст ошибку, признак std :: fmt :: Debug не реализован для GenderCategory . Атрибут # [Derve (Debug)] используется для подавления этой ошибки.

// 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);
}

Выход

Male
Female

Структура и перечисление

В следующем примере определяется структура Person. Пол поля имеет тип GenderCategory (который является перечислением) и может быть назначен как мужской или женский как значение.

// 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);
}

В этом примере создаются объекты p1 и p2 типа Person и инициализируются атрибуты, имя и пол для каждого из этих объектов.

Выход

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

Вариант Enum

Опция — это предопределенное перечисление в стандартной библиотеке Rust. Это перечисление имеет два значения — Some (data) и None.

Синтаксис

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

Здесь тип T представляет значение любого типа.

Rust не поддерживает нулевое ключевое слово. Значение None в enumOption может использоваться функцией для возврата нулевого значения. Если есть данные для возврата, функция может вернуть некоторые (данные) .

Позвольте нам понять это на примере —

Программа определяет функцию is_even () с типом возвращаемого значения Option. Функция проверяет, является ли переданное значение четным числом. Если вход является четным, то возвращается значение true, иначе функция возвращает None .

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
   }
}

Выход

None
Some(true)

Заявление о совпадении и Enum

Оператор соответствия можно использовать для сравнения значений, хранящихся в перечислении. В следующем примере определяется функция print_size , которая принимает перечисление CarType в качестве параметра. Функция сравнивает значения параметров с заранее заданным набором констант и отображает соответствующее сообщение.

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);
}

Выход

Large sized Sports Utility car
Small sized car
medium sized car

Матч с опцией

Пример функции is_even , которая возвращает тип Option, также может быть реализован с помощью оператора match, как показано ниже —

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
   }
}

Выход

not even

Совпадение и перечисление с типом данных

Можно добавить тип данных к каждому варианту перечисления. В следующем примере варианты перечисления Name и Usr_ID имеют строковый и целочисленный типы соответственно. В следующем примере показано использование оператора match с перечислением, имеющим тип данных.

// 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);
      }
   }
}

Выход

Name("Mohtashim")
Usr_ID(100)
Mohtashim

Rust — Модули

Логическая группа кода называется модулем. Несколько модулей скомпилированы в единицу, называемую crate . Программы Rust могут содержать бинарный ящик или библиотечный ящик. Бинарный ящик — это исполняемый проект с методом main () . Ящик библиотеки — это группа компонентов, которые можно использовать в других проектах. В отличие от бинарного ящика, у библиотечного ящика нет точки входа (метод main ()). Инструмент Cargo используется для управления ящиками в Rust. Например, сетевой модуль содержит функции, связанные с сетью, а графический модуль содержит функции, связанные с рисованием. Модули похожи на пространства имен в других языках программирования. Ящики сторонних производителей можно загрузить с помощью груза с сайта crates.io .

Sr.No Срок и описание
1

клеть

Является ли блок компиляции в Rust; Ящик компилируется в двоичный файл или библиотеку.

2

грузовой

Официальный инструмент управления пакетами Rust для ящиков.

3

модуль

Логически группирует код внутри ящика.

4

crates.io

Официальный реестр пакетов Rust.

клеть

Является ли блок компиляции в Rust; Ящик компилируется в двоичный файл или библиотеку.

грузовой

Официальный инструмент управления пакетами Rust для ящиков.

модуль

Логически группирует код внутри ящика.

crates.io

Официальный реестр пакетов Rust.

Синтаксис

//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() {
   }
}

Модули могут быть общедоступными или частными. Компоненты в частном модуле не могут быть доступны для других модулей. Модули в Rust являются частными по умолчанию. Напротив, функции в общедоступном модуле могут быть доступны для других модулей. Модули должны иметь префикс с ключевым словом pub, чтобы сделать его общедоступным. Функции в публичном модуле также должны быть общедоступными.

Иллюстрация: определение модуля

Пример определяет публичный модуль — фильмы . Модуль содержит функцию play (), которая принимает параметр и печатает его значение.

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

Выход

Playing movie Herold and Kumar

Используйте ключевое слово

Ключевое слово use помогает импортировать публичный модуль.

Синтаксис

use public_module_name::function_name;

иллюстрация

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

Выход

Playing movie Herold and Kumar

Вложенные модули

Модули также могут быть вложенными. Комедийный модуль вложен в английский модуль, который далее вложен в модуль фильмов . Пример, приведенный ниже, определяет функцию воспроизведения внутри модуля movies / english / comedy .

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());
}

Выход

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

Иллюстрация — Создайте библиотечный ящик и используйте его в двоичном ящике

Давайте создадим библиотечный ящик с именем movie_lib , который содержит модуль кино . Для создания ящика библиотеки movie_lib мы будем использовать инструмент Cargo .

Шаг 1 — Создать папку проекта

Создайте папку movie-app, а затем подпапку movie-lib . После создания папки и подпапки создайте в этом каталоге папку src и файл Cargo.toml. Исходный код должен находиться в папке src . Создайте файлы lib.rs и movies.rs в папке src. Файл Cargo.toml будет содержать метаданные проекта, такие как номер версии, имя автора и т. Д.

Структура каталога проекта будет такой, как показано ниже —

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

Шаг 2 — Отредактируйте файл Cargo.toml, чтобы добавить метаданные проекта

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

Шаг 3 — Отредактируйте файл lib.rs.

Добавьте следующее определение модуля в этот файл.

pub mod movies;

Вышеуказанная строка создает публичный модуль — фильмы .

Шаг 4 — Отредактируйте файл movies.rs

Этот файл будет определять все функции для модуля фильмов.

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

Приведенный выше код определяет функцию play (), которая принимает параметр и выводит его на консоль.

Шаг 5 — Постройте библиотечный ящик

Создайте приложение, используя команду cargo build, чтобы проверить, правильно ли структурирован ящик библиотеки. Убедитесь, что вы находитесь в корне проекта — папка фильма-приложения. Следующее сообщение будет отображаться в терминале, если сборка завершится успешно.

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

Шаг 6 — Создайте тестовое приложение

Создайте еще одну папку movie-lib-test в папке movie-app, затем файл Cargo.toml и папку src. В этом проекте должен быть метод main, так как это двоичный ящик, который будет использовать созданный ранее ящик библиотеки. Создайте файл main.rs в папке src. Структура папок будет такой, как показано.

movie-app
   movie-lib 
   // already completed

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

Шаг 7 — Добавьте следующее в файл Cargo.toml

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

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

ПРИМЕЧАНИЕ. — Путь к папке библиотеки устанавливается как зависимости. Следующая диаграмма показывает содержание обоих проектов.

Грузовой Томль Файл

Шаг 8 — Добавьте следующее в файл main.rs

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

Приведенный выше код импортирует внешний пакет movies_lib. Проверьте Cargo.toml текущего проекта, чтобы проверить имя ящика.

Шаг 9 — Использование грузовой сборки и грузовой пробег

Мы будем использовать сборку грузов и запуск грузов, чтобы построить бинарный проект и выполнить его, как показано ниже —

грузовой пробег

Руст — Коллекции

Стандартная библиотека коллекции Rust обеспечивает эффективную реализацию наиболее распространенных структур данных программирования общего назначения. В этой главе обсуждается реализация часто используемых коллекций — Vector, HashMap и HashSet.

Вектор

Вектор — это массив с изменяемым размером. Он хранит значения в смежных блоках памяти. Предопределенная структура Vec может использоваться для создания векторов. Некоторые важные особенности вектора:

  • Вектор может расти или уменьшаться во время выполнения.

  • Вектор — это однородная коллекция.

  • Вектор хранит данные в виде последовательности элементов в определенном порядке. Каждому элементу в векторе присваивается уникальный номер индекса. Индекс начинается с 0 и продолжается до n-1, где n — размер коллекции. Например, в коллекции из 5 элементов первый элемент будет иметь индекс 0, а последний элемент — индекс 4.

  • Вектор будет добавлять значения только (или около) к концу. Другими словами, вектор может использоваться для реализации стека.

  • Память для вектора выделяется в куче.

Вектор может расти или уменьшаться во время выполнения.

Вектор — это однородная коллекция.

Вектор хранит данные в виде последовательности элементов в определенном порядке. Каждому элементу в векторе присваивается уникальный номер индекса. Индекс начинается с 0 и продолжается до n-1, где n — размер коллекции. Например, в коллекции из 5 элементов первый элемент будет иметь индекс 0, а последний элемент — индекс 4.

Вектор будет добавлять значения только (или около) к концу. Другими словами, вектор может использоваться для реализации стека.

Память для вектора выделяется в куче.

Синтаксис — Создание вектора

let mut instance_name = Vec::new();

Статический метод new () структуры Vec используется для создания экземпляра вектора.

Кроме того, вектор также может быть создан с помощью vec! макро. Синтаксис как указано ниже —

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

В следующей таблице перечислены некоторые часто используемые функции структуры Vec.

Sr.No метод Подпись и описание
1 новый ()

паб fn new () -> Vect

Создает новый, пустой Vec. Вектор не будет выделяться, пока элементы не будут помещены в него.

2 От себя()

pub fn push (& mut self, значение: T)

Добавляет элемент в конец коллекции.

3 Удалить()

pub fn удалить (& mut self, index: usize) -> T

Удаляет и возвращает элемент с индексом позиции внутри вектора, смещая все элементы после него влево.

4 содержит()

pub fn содержит (& self, x: & T) -> bool

Возвращает true, если срез содержит элемент с заданным значением.

5 LEN ()

паб fn len (& self) -> использовать

Возвращает количество элементов в векторе, также называемое его «длиной».

паб fn new () -> Vect

Создает новый, пустой Vec. Вектор не будет выделяться, пока элементы не будут помещены в него.

pub fn push (& mut self, значение: T)

Добавляет элемент в конец коллекции.

pub fn удалить (& mut self, index: usize) -> T

Удаляет и возвращает элемент с индексом позиции внутри вектора, смещая все элементы после него влево.

pub fn содержит (& self, x: & T) -> bool

Возвращает true, если срез содержит элемент с заданным значением.

паб fn len (& self) -> использовать

Возвращает количество элементов в векторе, также называемое его «длиной».

Иллюстрация: Создание вектора — new ()

Чтобы создать вектор, мы используем статический метод 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);
}

Приведенный выше пример создает вектор с использованием статического метода new (), который определен в структуре Vec . Функция push (val) добавляет значение, переданное в качестве параметра, в коллекцию. Функция len () возвращает длину вектора.

Выход

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

Иллюстрация: Создание вектора — vec! макрос

Следующий код создает вектор, используя vec! макро. Тип данных вектора выводится по первому значению, которое ему присвоено.

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

Выход

[1, 2, 3]

Как упоминалось ранее, вектор может содержать только значения одного и того же типа данных. Следующий фрагмент кода выдаст ошибку [E0308]: ошибка несовпадения типов .

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

Иллюстрация: push ()

Добавляет элемент в конец коллекции.

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

Выход

[20, 30, 40]

Иллюстрация: удалить ()

Удаляет и возвращает элемент с индексом позиции внутри вектора, смещая все элементы после него влево.

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

Выход

[10, 30]

Иллюстрация — содержит ()

Возвращает true, если срез содержит элемент с заданным значением —

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

Выход

found 10
[10, 20, 30]

Иллюстрация: len ()

Возвращает количество элементов в векторе, также называемое его «длиной».

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

Выход

size of vector is :3

Доступ к значениям из вектора

К отдельным элементам в векторе можно получить доступ, используя их соответствующие номера индекса. В следующем примере создается векторная реклама, в которой печатается значение первого элемента.

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

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

Значения в векторе также могут быть получены с использованием ссылки на коллекцию.

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);
}

Выход

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

HashMap

Карта — это набор пар ключ-значение (называемых записями). Никакие две записи на карте не могут иметь одинаковый ключ. Короче говоря, карта — это справочная таблица. HashMap хранит ключи и значения в хеш-таблице. Записи хранятся в произвольном порядке. Ключ используется для поиска значений в HashMap. Структура HashMap определяется в модуле std :: collection . Этот модуль должен быть явно импортирован для доступа к структуре HashMap.

Синтаксис: создание HashMap

let mut instance_name = HashMap::new();

Статический метод new () структуры HashMap используется для создания объекта HashMap. Этот метод создает пустой HashMap.

Обычно используемые функции HashMap обсуждаются ниже —

Sr.No метод Подпись и описание
1 вставить ()

pub fn insert (& mut self, k: K, v: V) -> Опция

Вставляет пару ключ / значение, если ключа нет, то возвращается None. После обновления возвращается старое значение.

2 LEN ()

паб fn len (& self) -> использовать

Возвращает количество элементов на карте.

3 получить()

pub fn get <Q:? Sized> (& lself, k: & Q) -> Option <& V>, где K: Заимствовать Q: Hash + Eq

Возвращает ссылку на значение, соответствующее ключу.

4 ITER ()

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

Итератор, посещающий все пары ключ-значение в произвольном порядке. Тип элемента итератора — (& ‘a K, &’ a V).

5 contains_key

pub fn содержит ключ <Q:? Sized> (& self, k: & Q) -> bool

Возвращает true, если карта содержит значение для указанного ключа.

6 Удалить()

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

Удаляет ключ с карты, возвращая сохраненный ключ и значение, если ключ был ранее на карте.

pub fn insert (& mut self, k: K, v: V) -> Опция

Вставляет пару ключ / значение, если ключа нет, то возвращается None. После обновления возвращается старое значение.

паб fn len (& self) -> использовать

Возвращает количество элементов на карте.

pub fn get <Q:? Sized> (& lself, k: & Q) -> Option <& V>, где K: Заимствовать Q: Hash + Eq

Возвращает ссылку на значение, соответствующее ключу.

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

Итератор, посещающий все пары ключ-значение в произвольном порядке. Тип элемента итератора — (& ‘a K, &’ a V).

pub fn содержит ключ <Q:? Sized> (& self, k: & Q) -> bool

Возвращает true, если карта содержит значение для указанного ключа.

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

Удаляет ключ с карты, возвращая сохраненный ключ и значение, если ключ был ранее на карте.

Иллюстрация: вставка ()

Вставляет пару ключ / значение в HashMap.

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

Вышеприведенная программа создает HashMap и инициализирует его двумя парами ключ-значение.

Выход

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

Иллюстрация: len ()

Возвращает количество элементов на карте

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());
}

Приведенный выше пример создает HashMap и печатает общее количество элементов в нем.

Выход

size of map is 2

Иллюстрация — получить ()

Возвращает ссылку на значение, соответствующее ключу. В следующем примере извлекается значение для ключа KL в 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");
      }
   }
}

Выход

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

Иллюстрация — iter ()

Возвращает итератор, содержащий ссылку на все пары ключ-значение в произвольном порядке.

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);
   }
}

Выход

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

Иллюстрация: contains_key ()

Возвращает true, если карта содержит значение для указанного ключа.

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");
   }
}

Выход

found key

Иллюстрация: удалить ()

Удаляет ключ с карты.

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());
}

Выход

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

HashSet

HashSet — это набор уникальных значений типа T. Быстрое добавление и удаление значений позволяет быстро узнать, есть ли заданное значение в наборе или нет. Структура HashSet определяется в модуле std :: collection. Этот модуль должен быть явно импортирован для доступа к структуре HashSet.

Синтаксис: Создание HashSet

let mut hash_set_name = HashSet::new();

Статический метод new структуры HashSet используется для создания HashSet. Этот метод создает пустой HashSet.

В следующей таблице перечислены некоторые из наиболее часто используемых методов структуры HashSet.

Sr.No метод Подпись и описание
1 вставить ()

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

Добавляет значение в набор. Если в наборе не было этого значения, возвращается true, иначе false.

2 LEN ()

паб fn len (& self) -> использовать

Возвращает количество элементов в наборе.

3 получить()

pub fn get <Q:? Sized> (& self, value: & Q) -> Option <& T>, где T: Заимствовать, Q: Hash + Eq,

Возвращает ссылку на значение в наборе, если оно совпадает с заданным значением.

4 ITER ()

pub fn iter (& self) -> Iter

Возвращает итератор, посещающий все элементы в произвольном порядке. Тип элемента итератора — & ‘T.

5 contains_key

pub fn содержит <Q:? Sized> (& self, value: & Q) -> bool

Возвращает true, если набор содержит значение.

6 Удалить()

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

Удаляет значение из набора. Возвращает true, если значение присутствовало в наборе.

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

Добавляет значение в набор. Если в наборе не было этого значения, возвращается true, иначе false.

паб fn len (& self) -> использовать

Возвращает количество элементов в наборе.

pub fn get <Q:? Sized> (& self, value: & Q) -> Option <& T>, где T: Заимствовать, Q: Hash + Eq,

Возвращает ссылку на значение в наборе, если оно совпадает с заданным значением.

pub fn iter (& self) -> Iter

Возвращает итератор, посещающий все элементы в произвольном порядке. Тип элемента итератора — & ‘T.

pub fn содержит <Q:? Sized> (& self, value: & Q) -> bool

Возвращает true, если набор содержит значение.

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

Удаляет значение из набора. Возвращает true, если значение присутствовало в наборе.

Иллюстрация — вставить ()

Добавляет значение в набор. HashSet не добавляет повторяющиеся значения в коллекцию.

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);
}

Выход

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

Иллюстрация: len ()

Возвращает количество элементов в наборе.

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());
}

Выход

size of the set is 3

Иллюстрация — iter ()

Перезапускает итератор, посещающий все элементы в произвольном порядке.

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);
   }
}

Выход

TutorialsPoint
Mohtashim
Kannan

Иллюстрация: get ()

Возвращает ссылку на значение в наборе, если оно есть, которое равно заданному значению.

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);
}

Выход

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

Иллюстрация — содержит ()

Возвращает true, если набор содержит значение.

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");
   }  
}

Выход

found name

Иллюстрация: удалить ()

Удаляет значение из набора.

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());
}

Выход

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

Rust — Обработка ошибок

В Rust ошибки можно разделить на две основные категории, как показано в таблице ниже.

Sr.No Имя и описание использование
1

Извлекаемые

Ошибки, которые могут быть обработаны

Результат enum
2

UNRECOVERABLE

Ошибки, которые не могут быть обработаны

макрос паники

Извлекаемые

Ошибки, которые могут быть обработаны

UNRECOVERABLE

Ошибки, которые не могут быть обработаны

Исправимая ошибка — это ошибка, которую можно исправить. Программа может повторить неудачную операцию или указать альтернативный порядок действий при обнаружении исправимой ошибки. Исправимые ошибки не приводят к внезапному сбою программы. Примером исправимой ошибки является ошибка « Файл не найден» .

Неустранимые ошибки приводят к внезапному сбою программы. Программа не может вернуться к своему нормальному состоянию, если возникает неисправимая ошибка. Он не может повторить неудачную операцию или отменить ошибку. Пример неисправимой ошибки — попытка получить доступ к расположению за концом массива.

В отличие от других языков программирования, Rust не имеет исключений. Он возвращает Enum Result <T, E> для исправляемых ошибок, а также вызывает макрос паники, если программа обнаруживает неисправимую ошибку. Макрос паники вызывает внезапное завершение программы.

Макрос паники и неисправимые ошибки

паника! макрос позволяет программе немедленно завершать работу и предоставлять обратную связь вызывающей программе. Его следует использовать, когда программа достигает неисправимого состояния.

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

В приведенном выше примере, программа будет завершена немедленно, когда она сталкивается с паникой! макро.

Выход

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

Иллюстрация: паника! макрос

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

Вывод как показано ниже —

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.

Программа может вызвать панику! макрос, если бизнес-правила нарушаются, как показано в примере ниже —

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");
}

Приведенный выше пример возвращает ошибку, если значение, присвоенное переменной, является нечетным.

Выход

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

Перечисление результата и исправимые ошибки

Enum Result — <T, E> может использоваться для обработки исправимых ошибок. У него есть два варианта — OK и Err . T и E — параметры универсального типа. T представляет тип значения, которое будет возвращено в случае успеха в варианте OK, а E представляет тип ошибки, которая будет возвращена в случае ошибки в варианте Err.

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

Позвольте нам понять это с помощью примера —

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

Программа возвращает OK (Файл), если файл уже существует, и Err (Ошибка), если файл не найден.

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

Давайте теперь посмотрим, как работать с вариантом Err.

В следующем примере обрабатывается ошибка, возвращаемая при открытии файла с помощью оператора match

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");
}

ПРИМЕЧАНИЕ. — Программа печатает конец основного события, хотя файл не был найден. Это означает, что программа корректно обработала ошибку.

Выход

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

иллюстрация

Функция is_even возвращает ошибку, если число не является четным числом. Функция main () обрабатывает эту ошибку.

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());
   }
}

ПРИМЕЧАНИЕ. — Так как основная функция корректно обрабатывает ошибки, выводится конец основного оператора.

Выход

Error msg is NOT_AN_EVEN
end of main

развернуть () и ожидать ()

Стандартная библиотека содержит несколько вспомогательных методов, которые оба перечисляют — Result <T, E> и Option <T> . Вы можете использовать их для упрощения случаев ошибок, когда вы действительно не ожидаете, что что-то не получится. В случае успеха метода, функция «развернуть» используется для извлечения фактического результата.

Sr.No метод Подпись и описание
1 разворачивать

развернуть (самостоятельно): T

Ожидает, что self будет Ok / Some и вернет значение, содержащееся в. Если это Err или None, вместо этого возникает паника с содержанием отображаемой ошибки.

2 ожидать

ожидайте (self, msg: & str): T

Ведет себя как развертка, за исключением того, что выводит собственное сообщение перед паникой в ​​дополнение к содержанию ошибки.

развернуть (самостоятельно): T

Ожидает, что self будет Ok / Some и вернет значение, содержащееся в. Если это Err или None, вместо этого возникает паника с содержанием отображаемой ошибки.

ожидайте (self, msg: & str): T

Ведет себя как развертка, за исключением того, что выводит собственное сообщение перед паникой в ​​дополнение к содержанию ошибки.

Развертка ()

Функция unwrap () возвращает фактический результат успешной операции. В случае сбоя операции возвращается паника с сообщением об ошибке по умолчанию. Эта функция является сокращением для выражения соответствия. Это показано в примере ниже —

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

Измените приведенный выше код, чтобы передать нечетное число в функцию is_even () .

Функция unwrap () вызовет панику и вернет сообщение об ошибке по умолчанию, как показано ниже

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

ожидать ()

Программа может вернуть пользовательское сообщение об ошибке в случае паники. Это показано в следующем примере —

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");
}

Функция wait () похожа на unwrap (). Разница лишь в том, что пользовательское сообщение об ошибке может отображаться с использованием ожидаемого.

Выход

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.

Rust — Общие типы

Обобщения являются средством для написания кода для нескольких контекстов с различными типами. В Rust дженерики относятся к параметризации типов данных и признаков. Обобщения позволяют писать более сжатый и чистый код, уменьшая дублирование кода и обеспечивая безопасность типов. Концепция Generics может применяться к методам, функциям, структурам, перечислениям, коллекциям и признакам.

Синтаксис <T>, известный как параметр типа, используется для объявления универсальной конструкции. T представляет любой тип данных.

Иллюстрация: Общая коллекция

В следующем примере объявляется вектор, который может хранить только целые числа.

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

Выход

[20, 30, 40]

Рассмотрим следующий фрагмент —

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);
}

Приведенный выше пример показывает, что вектор целочисленного типа может хранить только целочисленные значения. Итак, если мы попытаемся вставить строковое значение в коллекцию, компилятор вернет ошибку. Дженерики делают коллекции более безопасными.

Иллюстрация: Общая структура

Параметр type представляет тип, который компилятор заполнит позже.

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);
}

В приведенном выше примере объявляется общая структура с именем Data . Тип <T> указывает некоторый тип данных. Функция main () создает два экземпляра — целочисленный и строковый экземпляр структуры.

Выход

value is :350
value is :Tom

Черты

Черты могут быть использованы для реализации стандартного набора поведений (методов) в нескольких структурах. Черты подобны интерфейсам в объектно-ориентированном программировании. Синтаксис черты, как показано ниже —

Объявить черту

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
   }
}

Черты могут содержать конкретные методы (методы с телом) или абстрактные методы (методы без тела). Используйте конкретный метод, если определение метода будет общим для всех структур, реализующих черту. Однако структура может переопределить функцию, определенную признаком.

Используйте абстрактные методы, если определение метода варьируется для реализующих структур.

Синтаксис — реализовать черту

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

В следующих примерах определяется черта Printable с помощью метода print () , который реализуется структурной книгой .

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)
   }
}

Выход

Printing book with id:1001 and name Rust in Action

Общие функции

В примере определяется универсальная функция, которая отображает переданный ей параметр. Параметр может быть любого типа. Тип параметра должен реализовывать черту Display, чтобы его значение могло быть напечатано println! макро.

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);
}

Выход

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

Rust — Входной Выход

В этой главе обсуждается, как принимать значения из стандартного ввода (клавиатура) и отображать значения в стандартный вывод (консоль). В этой главе мы также обсудим передачу аргументов командной строки.

Типы чтения и записи

Стандартные библиотечные функции Rust для ввода и вывода организованы по двум признакам:

  • Читать
  • Написать
Sr.No Черта и описание пример
1

Читать

Типы, реализующие Read, имеют методы для байтово-ориентированного ввода. Они называются читателями

STDIN, File
2

Написать

Типы, которые реализуют запись, поддерживают как байт-ориентированный, так и текстовый вывод UTF-8. Их называют писателями.

Stdout, File

Читать

Типы, реализующие Read, имеют методы для байтово-ориентированного ввода. Они называются читателями

Написать

Типы, которые реализуют запись, поддерживают как байт-ориентированный, так и текстовый вывод UTF-8. Их называют писателями.

Чтение черт

Считыватели — это компоненты, с которых ваша программа может читать байты. Примеры включают чтение ввода с клавиатуры, файлов и т. Д. Метод read_line () этой черты можно использовать для чтения данных, по одной строке за раз, из файла или стандартного потока ввода.

Sr.No Черта характера Метод и описание
1 Читать

read_line (& mut line) -> Результат

Читает строку текста и добавляет ее к строке, которая является строкой. Возвращаемое значение — io :: Result, количество прочитанных байтов.

read_line (& mut line) -> Результат

Читает строку текста и добавляет ее к строке, которая является строкой. Возвращаемое значение — io :: Result, количество прочитанных байтов.

Иллюстрация — Чтение с консоли — stdin ()

Программы Rust могут принимать значения от пользователя во время выполнения. В следующем примере значения считываются из стандартного ввода (клавиатура) и выводятся на консоль.

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);
}

Функция stdin () возвращает дескриптор стандартного входного потока текущего процесса, к которому может быть применена функция read_line . Эта функция пытается прочитать все символы, присутствующие во входном буфере, когда она встречает символ конца строки.

Выход

Enter your name :
Mohtashim
Hello , Mohtashim
no of bytes read , 10

Написать черта

Писатели — это компоненты, в которые ваша программа может записывать байты. Примеры включают печать значений в консоль, запись в файлы и т. Д. Метод write () этой черты можно использовать для записи данных в файл или стандартный поток вывода.

Sr.No Черта характера Метод и описание
1 Написать

написать (& ЬиЕ) -> Результат

Записывает некоторые байты в буфере слайса в основной поток. Возвращает io :: Result, количество записанных байтов.

написать (& ЬиЕ) -> Результат

Записывает некоторые байты в буфере слайса в основной поток. Возвращает io :: Result, количество записанных байтов.

Иллюстрация — Запись в консоль — stdout ()

Печать! или распечатать! макросы можно использовать для отображения текста на консоли. Однако вы также можете использовать стандартную библиотечную функцию write () для отображения некоторого текста в стандартный вывод.

Давайте рассмотрим пример, чтобы понять это.

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();
}

Выход

Tutorials Point
bytes written 15

Функция стандартной библиотеки stdout () возвращает дескриптор стандартного потока вывода текущего процесса, к которому может быть применена функция записи . Метод write () возвращает перечисление Result. Unwrap () — это вспомогательный метод для извлечения фактического результата из перечисления. Метод unwrap отправит панику, если произойдет ошибка.

ПРИМЕЧАНИЕ. — Файл IO обсуждается в следующей главе.

Аргументы командной строки

Аргументы CommandLine передаются программе перед ее выполнением. Они как параметры, передаваемые в функции. Параметры CommandLine могут использоваться для передачи значений в функцию main () . Std :: env :: args () возвращает аргументы командной строки.

иллюстрация

В следующем примере значения передаются в качестве аргументов commandLine в функцию main (). Программа создана в файле с именем 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
   }
}

Программа сгенерирует файл main.exe после компиляции. Несколько параметров командной строки должны быть разделены пробелом. Выполните main.exe из терминала, как main.exe hello tutorialspoint .

ПРИМЕЧАНИЕ.hello и tutorialspoint являются аргументами командной строки.

Выход

No of elements in arguments is :3
[main.exe]
[hello]
[tutorialspoint]

Вывод показывает 3 аргумента, так как main.exe является первым аргументом.

иллюстрация

Следующая программа вычисляет сумму значений, переданных в качестве аргументов командной строки. Список целочисленных значений, разделенных пробелом, передается программе.

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);
}

При выполнении программы от имени main.exe 1 2 3 4, вывод будет —

No of elements in arguments is :5
sum is 10

Rust — Файл ввода / вывода

Помимо чтения и записи в консоль, Rust позволяет читать и писать в файлы.

Структура File представляет файл. Это позволяет программе выполнять операции чтения-записи файла. Все методы в структуре File возвращают вариант перечисления io :: Result.

Обычно используемые методы структуры File перечислены в таблице ниже —

Sr.No модуль метод Подпись Описание
1 СТД :: фс :: Файловые открыть() pub fn open <P: AsRef> (путь: P) -> Результат Открытый статический метод может быть использован для открытия файла в режиме только для чтения.
2 СТД :: фс :: Файловые Создайте() pub fn create <P: AsRef> (путь: P) -> Результат Статический метод открывает файл в режиме только для записи. Если файл уже существовал, старый контент уничтожается. В противном случае создается новый файл.
3 станд :: фс :: remove_file удалить файл() pub fn remove_file <P: AsRef> (путь: P) -> Результат <()> Удаляет файл из файловой системы. Нет никаких гарантий, что файл будет немедленно удален.
4 СТД :: фс :: OpenOptions добавление () pub fn append (& mut self, append: bool) -> & mut OpenOptions Устанавливает опцию для режима добавления файла.
5 станд :: IO :: Записывает write_all () fn write_all (& mut self, buf: & [u8]) -> Result <()> Попытки записать весь буфер в эту запись.
6 станд :: IO :: Читать read_to_string () fn read_to_string (& mut self, buf: & mut String) -> Результат Читает все байты до EOF в этом источнике, добавляя их в buf.

Записать в файл

Давайте посмотрим на пример, чтобы понять, как написать файл.

Следующая программа создает файл «data.txt». Метод create () используется для создания файла. Метод возвращает дескриптор файла, если файл создан успешно. В последней строке функция write_all запишет байты во вновь созданный файл. Если какая-либо из операций завершается неудачей, функция wait () возвращает сообщение об ошибке.

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" );
}

Выход

data written to file

Читать из файла

Следующая программа читает содержимое файла data.txt и выводит его на консоль. Функция «открыть» используется для открытия существующего файла. Абсолютный или относительный путь к файлу передается функции open () в качестве параметра. Функция open () выдает исключение, если файл не существует или если он недоступен по какой-либо причине. Если это удается, дескриптор файла для такого файла назначается переменной «file».

Функция read_to_string дескриптора file используется для чтения содержимого этого файла в строковую переменную.

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);
}

Выход

Hello World
TutorialsPoint

Удалить файл

В следующем примере для удаления файла используется функция remove_file (). Функция wait () возвращает пользовательское сообщение в случае возникновения ошибки.

use std::fs;
fn main() {
   fs::remove_file("data.txt").expect("could not remove file");
   println!("file is removed");
}

Выход

file is removed

Добавить данные в файл

Функция append () записывает данные в конец файла. Это показано в примере, приведенном ниже —

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");
}

Выход

file append success

Скопируйте файл

В следующем примере содержимое файла копируется в новый файл.

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; }
   }
}

Выполните вышеуказанную программу как main.exe data.txt datacopy.txt . При выполнении файла передаются два аргумента командной строки:

  • путь к исходному файлу
  • файл назначения

Rust — менеджер пакетов

Cargo — менеджер пакетов для RUST. Это действует как инструмент и управляет проектами Rust.

Некоторые часто используемые грузовые команды перечислены в таблице ниже —

Sr.No Команда и описание
1

сборка груза

Компилирует текущий проект.

2

проверка груза

Анализирует текущий проект и сообщает об ошибках, но не создает объектные файлы.

3

грузовой пробег

Сборка и выполнение src / main.rs.

4

чистый груз

Удаляет целевой каталог.

5

обновление груза

Обновляет зависимости, перечисленные в Cargo.lock.

6

новый груз

Создает новый грузовой проект.

сборка груза

Компилирует текущий проект.

проверка груза

Анализирует текущий проект и сообщает об ошибках, но не создает объектные файлы.

грузовой пробег

Сборка и выполнение src / main.rs.

чистый груз

Удаляет целевой каталог.

обновление груза

Обновляет зависимости, перечисленные в Cargo.lock.

новый груз

Создает новый грузовой проект.

Cargo помогает загружать сторонние библиотеки. Поэтому он действует как менеджер пакетов. Вы также можете создавать свои собственные библиотеки. Cargo устанавливается по умолчанию при установке Rust.

Для создания нового грузового проекта мы можем использовать команды, приведенные ниже.

Создать бинарный ящик

cargo new project_name --bin

Создать библиотечный ящик

cargo new project_name --lib

Чтобы проверить текущую версию груза, выполните следующую команду —

cargo --version

Иллюстрация — Создание проекта Binary Cargo

Игра генерирует случайное число и предлагает пользователю угадать число.

Шаг 1 — Создать папку проекта

Откройте терминал и введите следующую команду cargo new Угадать-игра-приложение —bin .

Это создаст следующую структуру папок.

guess-game-app/
   -->Cargo.toml
   -->src/
      main.rs

Команда « Новый груз» используется для создания ящика. Флаг —bin указывает, что создаваемый ящик является двоичным. Общедоступные ящики хранятся в центральном хранилище, которое называется crates.io https://crates.io/ .

Шаг 2 — Включите ссылки на внешние библиотеки

Этот пример должен генерировать случайное число. Поскольку внутренняя стандартная библиотека не обеспечивает логику генерации случайных чисел, нам нужно взглянуть на внешние библиотеки или ящики. Давайте использовать rand crate, который доступен на сайте crates.io crates.io

Https://crates.io/crates/rand — это библиотека ржавчины для генерации случайных чисел. Rand предоставляет утилиты для генерации случайных чисел, преобразования их в полезные типы и распределения и некоторые алгоритмы, связанные со случайностью.

Следующая диаграмма показывает веб-сайт crate.io и результаты поиска для rand crate.

внешние библиотеки

Скопируйте версию rand crate в файл Cargo.toml rand = «0.5.5» .

[package]
name = "guess-game-app"
version = "0.1.0"
authors = ["Mohtashim"]

[dependencies]
rand = "0.5.5"

Шаг 3: Скомпилируйте проект

Перейдите в папку проекта. Выполните команду «Сборка груза» в окне терминала —

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

Ящик rand и все переходные зависимости (внутренние зависимости rand) будут автоматически загружены.

Шаг 4 — Понимание бизнес-логики

Давайте теперь посмотрим, как работает бизнес-логика для игры в угадывание чисел —

  • Игра изначально генерирует случайное число.

  • Пользователю предлагается ввести данные и угадать число.

  • Если число меньше сгенерированного числа, печатается сообщение «Слишком низкое».

  • Если число больше сгенерированного числа, печатается сообщение «Слишком высокое».

  • Если пользователь вводит число, сгенерированное программой, игра завершается.

Игра изначально генерирует случайное число.

Пользователю предлагается ввести данные и угадать число.

Если число меньше сгенерированного числа, печатается сообщение «Слишком низкое».

Если число больше сгенерированного числа, печатается сообщение «Слишком высокое».

Если пользователь вводит число, сгенерированное программой, игра завершается.

Шаг 5 — Редактируйте файл main.rs

Добавьте бизнес-логику в файл 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;
      }
   }
}

Шаг 6 — Компиляция и выполнение проекта

Выполните команду грузовой пробег на терминале. Убедитесь, что терминал указывает на каталог проекта.

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 ..

Rust — Итератор и Закрытие

В этой главе мы узнаем, как итераторы и замыкания работают в RUST.

итераторы

Итератор помогает перебирать коллекцию значений, таких как массивы, векторы, карты и т. Д. Итераторы реализуют черту Iterator, определенную в стандартной библиотеке Rust. Метод iter () возвращает объект-итератор коллекции. Значения в объекте итератора называются элементами. Метод next () итератора может использоваться для перемещения по элементам. Метод next () возвращает значение None, когда оно достигает конца коллекции.

В следующем примере используется итератор для чтения значений из массива.

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());
}

Выход

Iter([10, 20, 30])
Some(10)
Some(20)
Some(30)
None

Если коллекция, такая как массив или Vector, реализует черту Iterator, то ее можно просмотреть, используя синтаксис for … in, как показано ниже:

fn main() {
   let a = [10,20,30];
   let iter = a.iter();
   for data in iter{
      print!("{}\t",data);
   }
}

Выход

10 20 30

Следующие 3 метода возвращают объект итератора из коллекции, где T представляет элементы в коллекции.

Sr.No Методы и описание
1

ITER ()

дает итератор над & T (ссылка на T)

2

into_iter ()

дает итератор над T

3

iter_mut ()

дает итератор над & Mut T

ITER ()

дает итератор над & T (ссылка на T)

into_iter ()

дает итератор над T

iter_mut ()

дает итератор над & Mut T

Иллюстрация: ИТЭР ()

Функция iter () использует концепцию заимствования. Он возвращает ссылку на каждый элемент коллекции, оставляя коллекцию нетронутой и доступной для повторного использования после цикла.

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
}

Выход

Hello Kannan
There is a rustacean among us!
Hello Kiran
["Kannan", "Mohtashim", "Kiran"]

Иллюстрация — into_iter ()

Эта функция использует концепцию собственности. Он перемещает значения в коллекции в объект iter, т. Е. Коллекция используется и больше не доступна для повторного использования.

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
}

Выход

Hello Kannan
There is a rustacean among us!
Hello Kiran

Иллюстрация — для и iter_mut ()

Эта функция похожа на функцию iter () . Однако эта функция может изменять элементы в коллекции.

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
}

Выход

Hello Kannan
There is a rustacean among us!
Hello Kiran
["Kannan", "Mohtashim", "Kiran"]

закрытие

Закрытие относится к функции внутри другой функции. Это анонимные функции — функции без имени. Замыкание может быть использовано для назначения функции переменной. Это позволяет программе передавать функцию в качестве параметра другим функциям. Закрытие также известно как встроенная функция. Переменные во внешней функции могут быть доступны с помощью встроенных функций.

Синтаксис: определение замыкания

Определение замыкания может иметь параметры. Параметры заключены в две вертикальные полосы.

let closure_function = |parameter| {
   //logic
}

Синтаксис, вызывающий Closure, реализует черты Fn . Таким образом, он может быть вызван с помощью синтаксиса () .

closure_function(parameter);    //invoking

иллюстрация

В следующем примере определяется замыкание is_even внутри функции main () . Закрытие возвращает true, если число четное, и возвращает false, если число нечетное.

fn main(){
   let is_even = |x| {
      x%2==0
   };
   let no = 13;
   println!("{} is even ? {}",no,is_even(no));
}

Выход

13 is even ? false

иллюстрация

fn main(){
   let val = 10; 
   // declared outside
   let closure2 = |x| {
      x + val //inner function accessing outer fn variable
   };
   println!("{}",closure2(2));
}

Функция main () объявляет переменную val и замыкание. Замыкание обращается к переменной, объявленной во внешней функции main () .

Выход

12

Rust — умные указатели

Rust распределяет все по стеку по умолчанию. Вы можете хранить вещи в куче, оборачивая их в умные указатели, такие как Box . Такие типы, как Vec и String, неявно помогают распределению кучи. Умные указатели реализуют черты, перечисленные в таблице ниже. Эти черты умных указателей отличают их от обычной структуры —

Sr.No Название черты Пакет и описание
1 Deref

СТД :: опс :: Deref

Используется для неизменяемых операций разыменования, например * v.

2 Капля

СТД :: опс :: Капля

Используется для запуска некоторого кода, когда значение выходит из области видимости. Это иногда называют деструктором

СТД :: опс :: Deref

Используется для неизменяемых операций разыменования, например * v.

СТД :: опс :: Капля

Используется для запуска некоторого кода, когда значение выходит из области видимости. Это иногда называют деструктором

В этой главе мы узнаем о умном указателе Box . Мы также узнаем, как создать собственный умный указатель, такой как Box.

коробка

Умный указатель Box, также называемый ящиком, позволяет хранить данные в куче, а не в стеке. Стек содержит указатель на данные кучи. У Box нет потери производительности, кроме хранения данных в куче.

Давайте посмотрим, как использовать ящик для хранения значения i32 в куче.

fn main() {
   let var_i32 = 5; 
   //stack
   let b = Box::new(var_i32); 
   //heap
   println!("b = {}", b);
}

Выход

b = 5

Чтобы получить доступ к значению, указанному переменной, используйте разыменование. * Используется в качестве оператора разыменования. Давайте посмотрим, как использовать разыменование с 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
}

Переменная x является типом значения со значением 5. Таким образом, выражение 5 == x вернет true. Переменная y указывает на кучу. Чтобы получить доступ к значению в куче, нам нужно разыменовать, используя * y. * y возвращает значение 5. Таким образом, выражение 5 == * y возвращает true.

Выход

true
true

Иллюстрация — Черта Дерефа

Черта Deref, предоставляемая стандартной библиотекой, требует от нас реализации одного метода с именем deref , который заимствует self и возвращает ссылку на внутренние данные. В следующем примере создается структура MyBox , которая является универсальным типом. В нем реализована черта Дерефа . Эта особенность помогает нам получить доступ к значениям кучи, заключенным в y, используя * 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
}

Выход

5==x is true
5==*y is true
x==*y is true

Иллюстрация — Drop Trait

Черта Drop содержит метод drop () . Этот метод вызывается, когда структура, которая реализовала эту черту, выходит из области видимости. В некоторых языках программист должен вызывать код для освобождения памяти или ресурсов каждый раз, когда они заканчивают использовать экземпляр умного указателя. В Rust вы можете добиться автоматического освобождения памяти, используя черту 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");
}

В приведенном выше примере метод drop будет вызываться дважды, когда мы создаем два объекта в куче.

dropping MyBox object from memory
dropping MyBox object from memory

Rust — параллелизм

В параллельном программировании разные части программы выполняются независимо. С другой стороны, в параллельном программировании различные части программы выполняются одновременно. Обе модели одинаково важны, так как все больше компьютеров используют преимущества своих нескольких процессоров.

Потоки

Мы можем использовать потоки для одновременного запуска кодов. В современных операционных системах код исполняемой программы запускается в процессе, и операционная система управляет несколькими процессами одновременно. В вашей программе вы также можете иметь независимые части, которые работают одновременно. Функции, которые запускают эти независимые части, называются потоками.

Создание темы

Функция thread :: spawn используется для создания нового потока. Функция spawn принимает замыкание в качестве параметра. Закрытие определяет код, который должен выполняться потоком. В следующем примере печатается некоторый текст из основного потока, а другой текст из нового потока.

//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));
   }
}

Выход

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!

Основной поток печатает значения от 1 до 4.

ПРИМЕЧАНИЕ. — Новый поток будет остановлен, когда основной поток закончится. Выходные данные из этой программы могут каждый раз немного отличаться.

Функция thread :: sleep заставляет поток остановить его выполнение на короткое время, позволяя запустить другой поток. Потоки, вероятно, по очереди, но это не гарантировано — это зависит от того, как операционная система планирует потоки. В этом прогоне основной поток печатается первым, даже если оператор кода из порожденного потока появляется первым в коде. Более того, даже если порожденная нить запрограммирована на печать значений до 9, она дошла только до 5, прежде чем основная нить закрылась.

Присоединиться Ручки

Появившаяся нить может не получить шанс запустить или запустить полностью. Это потому, что основной поток завершается быстро. Функция spawn <F, T> (f: F) -> JoinHandlelt; T> возвращает JoinHandle. Метод join () в JoinHandle ожидает завершения связанного потока.

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();
}

Выход

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!

Основная нить и порожденная нить продолжают переключаться.

ПРИМЕЧАНИЕ. — Основной поток ожидает завершения созданного потока из-за вызова метода join () .