Rust – Variables ”; Previous Next A variable is a named storage that programs can manipulate. Simply put, a variable helps programs to store values. Variables in Rust are associated with a specific data type. The data type determines the size and layout of the variable”s memory, the range of values that can be stored within that memory and the set of operations that can be performed on the variable. Rules for Naming a Variable In this section, we will learn about the different rules for naming a variable. The name of a variable can be composed of letters, digits, and the underscore character. It must begin with either a letter or an underscore. Upper and lowercase letters are distinct because Rust is case-sensitive. Syntax The data type is optional while declaring a variable in Rust. The data type is inferred from the value assigned to the variable. The syntax for declaring a variable is given below. let variable_name = value; // no type specified let variable_name:dataType = value; //type specified Illustration fn main() { let fees = 25_000; let salary:f64 = 35_000.00; println!(“fees is {} and salary is {}”,fees,salary); } The output of the above code will be fees is 25000 and salary is 35000. Immutable By default, variables are immutable − read only in Rust. In other words, the variable”s value cannot be changed once a value is bound to a variable name. Let us understand this with an example. fn main() { let fees = 25_000; println!(“fees is {} “,fees); fees = 35_000; println!(“fees changed is {}”,fees); } The output will be as shown below − 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) The error message indicates the cause of the error – you cannot assign values twice to immutable variable fees. This is one of the many ways Rust allows programmers to write code and takes advantage of the safety and easy concurrency. Mutable Variables are immutable by default. Prefix the variable name with mut keyword to make it mutable. The value of a mutable variable can be changed. The syntax for declaring a mutable variable is as shown below − 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); } The output of the snippet is given below − fees is 25000 fees changed is 35000 Print Page Previous Next Advertisements ”;
Category: rust
Rust – Discussion
Discuss Rust ”; Previous Next Rust is a modern systems programming language developed by the Mozilla Corporation. It is intended to be a language for highly concurrent and highly secure systems. It compiles to native code; hence, it is blazingly fast like C and C++. This tutorial adopts a simple and practical approach to describe the concepts of Rust programming. Print Page Previous Next Advertisements ”;
Rust – Collections
Rust – Collections ”; Previous Next Rust”s standard collection library provides efficient implementations of the most common general-purpose programming data structures. This chapter discusses the implementation of the commonly used collections − Vector, HashMap and HashSet. Vector A Vector is a resizable array. It stores values in contiguous memory blocks. The predefined structure Vec can be used to create vectors. Some important features of a Vector are − A Vector can grow or shrink at runtime. A Vector is a homogeneous collection. A Vector stores data as sequence of elements in a particular order. Every element in a Vector is assigned a unique index number. The index starts from 0 and goes up to n-1 where, n is the size of the collection. For example, in a collection of 5 elements, the first element will be at index 0 and the last element will be at index 4. A Vector will only append values to (or near) the end. In other words, a Vector can be used to implement a stack. Memory for a Vector is allocated in the heap. Syntax – Creating a Vector let mut instance_name = Vec::new(); The static method new() of the Vecstructure is used to create a vector instance. Alternatively, a vector can also be created using the vec! macro. The syntax is as given below − let vector_name = vec![val1,val2,val3] The following table lists some commonly used functions of the Vec structure. Sr.No Method Signature & Description 1 new() pub fn new()->Vect Constructs a new, empty Vec. The vector will not allocate until elements are pushed onto it. 2 push() pub fn push(&mut self, value: T) Appends an element to the back of a collection. 3 remove() pub fn remove(&mut self, index: usize) -> T Removes and returns the element at position index within the vector, shifting all elements after it to the left. 4 contains() pub fn contains(&self, x: &T) -> bool Returns true if the slice contains an element with the given value. 5 len() pub fn len(&self) -> usize Returns the number of elements in the vector, also referred to as its ”length”. Illustration: Creating a Vector – new() To create a vector, we use the static method 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); } The above example creates a Vector using the static method new() that is defined in structure Vec. The push(val) function appends the value passed as parameter to the collection. The len() function returns the length of the vector. Output size of vector is :3 [20, 30, 40] Illustration: Creating a Vector – vec! Macro The following code creates a vector using the vec! macro. The data type of the vector is inferred the first value that is assigned to it. fn main() { let v = vec![1,2,3]; println!(“{:?}”,v); } Output [1, 2, 3] As mentioned earlier, a vector can only contain values of the same data type. The following snippet will throw a error[E0308]: mismatched types error. fn main() { let v = vec![1,2,3,”hello”]; println!(“{:?}”,v); } Illustration: push() Appends an element to the end of a collection. fn main() { let mut v = Vec::new(); v.push(20); v.push(30); v.push(40); println!(“{:?}”,v); } Output [20, 30, 40] Illustration: remove() Removes and returns the element at position index within the vector, shifting all elements after it to the left. fn main() { let mut v = vec![10,20,30]; v.remove(1); println!(“{:?}”,v); } Output [10, 30] Illustration – contains() Returns true if the slice contains an element with the given value − fn main() { let v = vec![10,20,30]; if v.contains(&10) { println!(“found 10”); } println!(“{:?}”,v); } Output found 10 [10, 20, 30] Illustration: len() Returns the number of elements in the vector, also referred to as its ”length”. fn main() { let v = vec![1,2,3]; println!(“size of vector is :{}”,v.len()); } Output size of vector is :3 Accessing values from a Vector Individual elements in a vector can be accessed using their corresponding index numbers. The following example creates a vector ad prints the value of the first element. fn main() { let mut v = Vec::new(); v.push(20); v.push(30); println!(“{:?}”,v[0]); } Output: `20` Values in a vector can also be fetched using reference to the collection. 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); } Output 20 30 40 500 [20, 30, 40, 500] HashMap A map is a collection of key-value pairs (called entries). No two entries in a map can have the same key. In short, a map is a lookup table. A HashMap stores the keys and values in a hash table. The entries are stored in an arbitrary order. The key is used to search for values in the HashMap. The HashMap structure is defined in the std::collections module. This module should be explicitly imported to access the HashMap structure. Syntax: Creating a HashMap let mut instance_name = HashMap::new(); The static method new() of the HashMap structure is used to create a HashMap object. This method creates an empty HashMap. The commonly used functions of HashMap are discussed below − Sr.No Method Signature & Description 1 insert() pub fn insert(&mut self, k: K, v: V) -> Option Inserts a key/value pair, if no key then None is returned. After update, old value is returned. 2 len() pub fn len(&self) -> usize Returns the number of elements in the map. 3 get() pub fn get<Q: ?Sized>(&lself, k: &Q) -> Option<&V> where K:Borrow Q:Hash+ Eq Returns a reference to the value corresponding to the key. 4 iter() pub fn iter(&self) -> Iter<K, V> An iterator visiting all key-value pairs in arbitrary order. The iterator element type is (&”a K, &”a V). 5 contains_key pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool Returns true if the map contains a value for the specified key. 6 remove() pub fn remove_entry<Q: ?Sized>(&mut self, k: &Q) -> Option<(K, V)> Removes a key from the map, returning
Rust – Enums
Rust – Enums ”; Previous Next In Rust programming, when we have to select a value from a list of possible variants we use enumeration data types. An enumerated type is declared using the enum keyword. Following is the syntax of enum − enum enum_name { variant1, variant2, variant3 } Illustration: Using an Enumeration The example declares an enum − GenderCategory, which has variants as Male and Female. The print! macro displays value of the enum. The compiler will throw an error the trait std::fmt::Debug is not implemented for GenderCategory. The attribute #[derive(Debug)] is used to suppress this error. // 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); } Output Male Female Struct and Enum The following example defines a structure Person. The field gender is of the type GenderCategory (which is an enum) and can be assigned either Male or Female as value. // 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); } The example creates objects p1 and p2 of type Person and initializes the attributes, name and gender for each of these objects. Output Person { name: “Mohtashim”, gender: Male } Person { name: “Amy”, gender: Female } Option Enum Option is a predefined enum in the Rust standard library. This enum has two values − Some(data) and None. Syntax enum Option<T> { Some(T), //used to return a value None // used to return null, as Rust doesn”t support the null keyword } Here, the type T represents value of any type. Rust does not support the null keyword. The value None, in the enumOption, can be used by a function to return a null value. If there is data to return, the function can return Some(data). Let us understand this with an example − The program defines a function is_even(), with a return type Option. The function verifies if the value passed is an even number. If the input is even, then a value true is returned, else the function returns 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 } } Output None Some(true) Match Statement and Enum The match statement can be used to compare values stored in an enum. The following example defines a function, print_size, which takes CarType enum as parameter. The function compares the parameter values with a pre-defined set of constants and displays the appropriate message. 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); } Output Large sized Sports Utility car Small sized car medium sized car Match with Option The example of is_even function, which returns Option type, can also be implemented with match statement as shown below − 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 } } Output not even Match & Enum with Data Type It is possible to add data type to each variant of an enum. In the following example, Name and Usr_ID variants of the enum are of String and integer types respectively. The following example shows the use of match statement with an enum having a data type. // 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); } } } Output Name(“Mohtashim”) Usr_ID(100) Mohtashim Print Page Previous Next Advertisements ”;
Rust – Slices
Rust – Slices ”; Previous Next A slice is a pointer to a block of memory. Slices can be used to access portions of data stored in contiguous memory blocks. It can be used with data structures like arrays, vectors and strings. Slices use index numbers to access portions of data. The size of a slice is determined at runtime. Slices are pointers to the actual data. They are passed by reference to functions, which is also known as borrowing. For example, slices can be used to fetch a portion of a string value. A sliced string is a pointer to the actual string object. Therefore, we need to specify the starting and ending index of a String. Index starts from 0 just like arrays. Syntax let sliced_value = &data_structure[start_index..end_index] The minimum index value is 0 and the maximum index value is the size of the data structure. NOTE that the end_index will not be included in final string. The diagram below shows a sample string Tutorials, that has 9 characters. The index of the first character is 0 and that of the last character is 8. The following code fetches 5 characters from the string (starting from index 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); } Output length of string is 9 rials Illustration – Slicing an integer array The main() function declares an array with 5 elements. It invokes the use_slice() function and passes to it a slice of three elements (points to the data array). The slices are passed by reference. The use_slice() function prints the value of the slice and its length. 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); } Output length of slice is 3 [20, 30, 40] Mutable Slices The &mut keyword can be used to mark a slice as mutable. 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 } Output length of slice is 3 [20, 30, 40] [10, 1010, 30, 40, 50] The above code passes a mutable slice to the use_slice() function. The function modifies the second element of the original array. Print Page Previous Next Advertisements ”;
Rust – Generic Types
Rust – Generic Types ”; Previous Next Generics are a facility to write code for multiple contexts with different types. In Rust, generics refer to the parameterization of data types and traits. Generics allows to write more concise and clean code by reducing code duplication and providing type-safety. The concept of Generics can be applied to methods, functions, structures, enumerations, collections and traits. The <T> syntax known as the type parameter, is used to declare a generic construct. T represents any data-type. Illustration: Generic Collection The following example declares a vector that can store only integers. fn main(){ let mut vector_integer: Vec<i32> = vec![20,30]; vector_integer.push(40); println!(“{:?}”,vector_integer); } Output [20, 30, 40] Consider the following snippet − 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); } The above example shows that a vector of integer type can only store integer values. So, if we try to push a string value into the collection, the compiler will return an error. Generics make collections more type safe. Illustration: Generic Structure The type parameter represents a type, which the compiler will fill in later. 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); } The above example declares a generic structure named Data. The <T> type indicates some data type. The main() function creates two instances − an integer instance and a string instance, of the structure. Output value is :350 value is :Tom Traits Traits can be used to implement a standard set of behaviors (methods) across multiple structures. Traits are like interfaces in Object-oriented Programming. The syntax of trait is as shown below − Declare a Trait 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 } } Traits can contain concrete methods (methods with body) or abstract methods (methods without a body). Use a concrete method if the method definition will be shared by all structures implementing the Trait. However, a structure can choose to override a function defined by the trait. Use abstract methods if the method definition varies for the implementing structures. Syntax – Implement a Trait impl some_trait for structure_name { // implement method1() there.. fn method1(&self ){ } } The following examples defines a trait Printable with a method print(), which is implemented by the structure book. 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) } } Output Printing book with id:1001 and name Rust in Action Generic Functions The example defines a generic function that displays a parameter passed to it. The parameter can be of any type. The parameter’s type should implement the Display trait so that its value can be printed by the println! macro. 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); } Output Inside print_pro generic function: 10 Inside print_pro generic function: 20 Inside print_pro generic function: Hello TutorialsPoint Print Page Previous Next Advertisements ”;
Rust – Decision Making
Rust – Decision Making ”; Previous Next Decision-making structures require that the programmer specify one or more conditions to be evaluated or tested by the program, along with a statement or statements to be executed if the condition is determined to be true, and optionally, other statements to be executed if the condition is determined to be false. Shown below is the general form of a typical decision-making structure found in most of the programming languages − Sr.No Statement & Description 1 if statement An if statement consists of a Boolean expression followed by one or more statements. 2 if…else statement An if statement can be followed by an optional else statement, which executes when the Boolean expression is false. 3 else…if and nested ifstatement You can use one if or else if statement inside another if or else if statement(s). 4 match statement A match statement allows a variable to be tested against a list of values. If Statement The if…else construct evaluates a condition before a block of code is executed. Syntax if boolean_expression { // statement(s) will execute if the boolean expression is true } If the Boolean expression evaluates to true, then the block of code inside the if statement will be executed. If the Boolean expression evaluates to false, then the first set of code after the end of the if statement (after the closing curly brace) will be executed. fn main(){ let num:i32 = 5; if num > 0 { println!(“number is positive”) ; } } The above example will print number is positive as the condition specified by the if block is true. if else statement An if can be followed by an optional else block. The else block will execute if the Boolean expression tested by the if statement evaluates to false. Syntax if boolean_expression { // statement(s) will execute if the boolean expression is true } else { // statement(s) will execute if the boolean expression is false } FlowChart The if block guards the conditional expression. The block associated with the if statement is executed if the Boolean expression evaluates to true. The if block may be followed by an optional else statement. The instruction block associated with the else block is executed if the expression evaluates to false. Illustration – Simple if…else fn main() { let num = 12; if num % 2==0 { println!(“Even”); } else { println!(“Odd”); } } The above example prints whether the value in a variable is even or odd. The if block checks the divisibility of the value by 2 to determine the same. Here is the output of the above code − Even Nested If The else…if ladder is useful to test multiple conditions. The syntax is as shown below − Syntax 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 } When using if…else…if and else statements, there are a few points to keep in mind. An if can have zero or one else”s and it must come after any else..if. An if can have zero to many else..if and they must come before the else. Once an else..if succeeds, none of the remaining else..if or else will be tested. Example: else…if ladder 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) ; } } The snippet displays whether the value is positive, negative or zero. Output 2 is positive Match Statement The match statement checks if a current value is matching from a list of values, this is very much similar to the switch statement in C language. In the first place, notice that the expression following the match keyword does not have to be enclosed in parentheses. The syntax is as shown below. let expressionResult = match variable_expression { constant_expr1 => { //statements; }, constant_expr2 => { //statements; }, _ => { //default } }; In the example given below, state_code is matched with a list of values MH, KL, KA, GA − if any match is found, a string value is returned to variable state. If no match is found, the default case _ matches and value Unkown is returned. 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); } Output Found match for MH State name is Maharashtra Print Page Previous Next Advertisements ”;
Rust – Ownership
Rust – Ownership ”; Previous Next The memory for a program can be allocated in the following − Stack Heap Stack A stack follows a last in first out order. Stack stores data values for which the size is known at compile time. For example, a variable of fixed size i32 is a candidate for stack allocation. Its size is known at compile time. All scalar types can be stored in stack as the size is fixed. Consider an example of a string, which is assigned a value at runtime. The exact size of such a string cannot be determined at compile time. So it is not a candidate for stack allocation but for heap allocation. Heap The heap memory stores data values the size of which is unknown at compile time. It is used to store dynamic data. Simply put, a heap memory is allocated to data values that may change throughout the life cycle of the program. The heap is an area in the memory which is less organized when compared to stack. What is Ownership? Each value in Rust has a variable that is called owner of the value. Every data stored in Rust will have an owner associated with it. For example, in the syntax − let age = 30, age is the owner of the value 30. Each data can have only one owner at a time. Two variables cannot point to the same memory location. The variables will always be pointing to different memory locations. Transferring Ownership The ownership of value can be transferred by − Assigning value of one variable to another variable. Passing value to a function. Returning value from a function. Assigning value of one variable to another variable The key selling point of Rust as a language is its memory safety. Memory safety is achieved by tight control on who can use what and when restrictions. Consider the following snippet − 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); } The above example declares a vector v. The idea of ownership is that only one variable binds to a resource, either v binds to resource or v2 binds to the resource. The above example throws an error − use of moved value: `v`. This is because the ownership of the resource is transferred to v2. It means the ownership is moved from v to v2 (v2=v) and v is invalidated after the move. Passing value to a function The ownership of a value also changes when we pass an object in the heap to a closure or function. 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); } Returning value from a function Ownership passed to the function will be invalidated as function execution completes. One work around for this is let the function return the owned object back to the caller. 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); } Ownership and Primitive Types In case of primitive types, contents from one variable is copied to another. So, there is no ownership move happening. This is because a primitive variable needs less resources than an object. Consider the following example − fn main(){ let u1 = 10; let u2 = u1; // u1 value copied(not moved) to u2 println!(“u1 = {}”,u1); } The output will be – 10. Print Page Previous Next Advertisements ”;
Rust – Concurrency
Rust – Concurrency ”; Previous Next In Concurrent programming, different parts of a program execute independently. On the other hand, in parallel programming, different parts of a program execute at the same time. Both the models are equally important as more computers take advantage of their multiple processors. Threads We can use threads to run codes simultaneously. In current operating systems, an executed program’s code is run in a process, and the operating system manages multiple processes at once. Within your program, you can also have independent parts that run simultaneously. The features that run these independent parts are called threads. Creating a Thread The thread::spawn function is used to create a new thread. The spawn function takes a closure as parameter. The closure defines code that should be executed by the thread. The following example prints some text from a main thread and other text from a new thread. //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)); } } Output 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! The main thread prints values from 1 to 4. NOTE − The new thread will be stopped when the main thread ends. The output from this program might be a little different every time. The thread::sleep function forces a thread to stop its execution for a short duration, allowing a different thread to run. The threads will probably take turns, but that is not guaranteed – it depends on how the operating system schedules the threads. In this run, the main thread is printed first, even though the print statement from the spawned thread appears first in the code. Moreover, even if the spawned thread is programmed to print values till 9, it only got to 5 before the main thread shut down. Join Handles A spawned thread may not get a chance to run or run completely. This is because the main thread completes quickly. The function spawn<F, T>(f: F) -> JoinHandlelt;T> returns a JoinHandle. The join() method on JoinHandle waits for the associated thread to finish. 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(); } Output 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! The main thread and spawned thread continue switching. NOTE − The main thread waits for spawned thread to complete because of the call to the join() method. Print Page Previous Next Advertisements ”;
Rust – Quick Guide
Rust – Quick Guide ”; Previous Next Rust – Introduction Rust is a systems level programming language, developed by Graydon Hoare. Mozilla Labs later acquired the programme. Application v/s Systems Programming Languages Application programming languages like Java/C# are used to build software, which provide services to the user directly. They help us build business applications like spreadsheets, word processors, web applications or mobile applications. Systems programming languages like C/C++ are used to build software and software platforms. They can be used to build operating systems, game engines, compilers, etc. These programming languages require a great degree of hardware interaction. Systems and application programming languages face two major problems − It is difficult to write secure code. It is difficult to write multi-threaded code. Why Rust? Rust focuses on three goals − Safety Speed Concurrency The language was designed for developing highly reliable and fast software in a simple way. Rust can be used to write high-level programs down to hardware-specific programs. Performance Rust programming language does not have a Garbage Collector (GC) by design. This improves the performance at runtime. Memory safety at compile time Software built using Rust is safe from memory issues like dangling pointers, buffer overruns and memory leaks. Multi-threaded applications Rust’s ownership and memory safety rules provide concurrency without data races. Support for Web Assembly (WASM) Web Assembly helps to execute high computation intensive algorithms in the browser, on embedded devices, or anywhere else. It runs at the speed of native code. Rust can be compiled to Web Assembly for fast, reliable execution. Rust – Environment Setup Installation of Rust is made easy through rustup, a console-based tool for managing Rust versions and associated tools. Installation on Windows Let us learn how to install RUST on Windows. Installation of Visual Studio 2013 or higher with C++ tools is mandatory to run the Rust program on windows. First, download Visual Studio from here VS 2013 Express Download and install rustup tool for windows. rustup-init.exe is available for download here − Rust Lang Double-click rustup-init.exe file. Upon clicking, the following screen will appear. Press enter for default installation. Once installation is completed, the following screen appears. From the installation screen, it is clear that Rust related files are stored in the folder − C:Users{PC}.cargobin The contents of the folder are − 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 is the package manager for Rust. To verify if cargo is installed, execute the following command − C:UsersAdmin>cargo -V cargo 1.29.0 (524a578d7 2018-08-05) The compiler for Rust is rustc. To verify the compiler version, execute the following command − C:UsersAdmin>cargo -V cargo 1.29.0 (524a578d7 2018-08-05) Installation on Linux / Mac To install rustup on Linux or macOS, open a terminal and enter the following command. $ curl https://sh.rustup.rs -sSf | sh The command downloads a script and starts the installation of the rustup tool, which installs the latest stable version of Rust. You might be prompted for your password. If the installation is successful, the following line will appear − Rust is installed now. Great! The installation script automatically adds Rust to your system PATH after your next login. To start using Rust right away instead of restarting your terminal, run the following command in your shell to add Rust to your system PATH manually − $ source $HOME/.cargo/env Alternatively, you can add the following line to your ~/.bash_profile − $ export PATH=”$HOME/.cargo/bin:$PATH” NOTE − When you try to compile a Rust program and get errors indicating that a linker could not execute, that means a linker is not installed on your system and you will need to install one manually. Using Tutorials Point Coding Ground for RUST A Read-Evaluate-Print Loop (REPL) is an easy to use interactive shell to compile and execute computer programs. If you want to compile and execute Rust programs online within the browser, use Tutorialspoint Coding Ground. Rust – HelloWorld Example This chapter explains the basic syntax of Rust language through a HelloWorld example. Create a HelloWorld-App folder and navigate to that folder on terminal C:UsersAdmin>mkdir HelloWorld-App C:UsersAdmin>cd HelloWorld-App C:UsersAdminHelloWorld-App> To create a Rust file, execute the following command − C:UsersAdminHelloWorld-App>notepad Hello.rs Rust program files have an extension .rs. The above command creates an empty file Hello.rs and opens it in NOTEpad. Add the code given below to this file − fn main(){ println!(“Rust says Hello to TutorialsPoint !!”); } The above program defines a function main fn main(). The fn keyword is used to define a function. The main() is a predefined function that acts as an entry point to the program. println! is a predefined macro in Rust. It is used to print a string (here Hello) to the console. Macro calls are always marked with an exclamation mark – !. Compile the Hello.rs file using rustc. C:UsersAdminHelloWorld-App>rustc Hello.rs Upon successful compilation of the program, an executable file (file_name.exe) is generated. To verify if the .exe file is generated, execute the following command. C:UsersAdminHelloWorld-App>dir //lists the files in folder Hello.exe Hello.pdb Hello.rs Execute the Hello.exe file and verify the output. What is a macro? Rust provides a powerful macro system that allows meta-programming. As you have seen in the previous example, macros look like functions, except that their name ends with a bang(!), but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program. Therefore, they provide more runtime features to a program unlike functions. Macros are an extended version of functions. Using the println! Macro – Syntax println!(); // prints just a newline println!(“hello “);//prints hello println!(“format {} arguments”, “some”); //prints format some arguments Comments in Rust Comments are a way to improve the readability of a program. Comments can be used to include additional information about a program like author of the code, hints about a function/ construct, etc. The compiler ignores comments. Rust supports the following types of comments − Single-line comments ( // )