Rust – Constant ”; Previous Next Constants represent values that cannot be changed. If you declare a constant then there is no way its value changes. The keyword for using constants is const. Constants must be explicitly typed. Following is the syntax to declare a constant. const VARIABLE_NAME:dataType = value; Rust Constant Naming Convention The naming convention for Constants are similar to that of variables. All characters in a constant name are usually in uppercase. Unlike declaring variables, the let keyword is not used to declare a constant. We have used constants in Rust in the example below − 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 } Constants v/s Variables In this section, we will learn about the differentiating factors between constants and variables. Constants are declared using the const keyword while variables are declared using the let keyword. A variable declaration can optionally have a data type whereas a constant declaration must specify the data type. This means const USER_LIMIT=100 will result in an error. A variable declared using the let keyword is by default immutable. However, you have an option to mutate it using the mut keyword. Constants are immutable. Constants can be set only to a constant expression and not to the result of a function call or any other value that will be computed at runtime. Constants can be declared in any scope, including the global scope, which makes them useful for values that many parts of the code need to know about. Shadowing of Variables and Constants Rust allows programmers to declare variables with the same name. In such a case, the new variable overrides the previous variable. Let us understand this with an example. fn main() { let salary = 100.00; let salary = 1.50 ; // reads first salary println!(“The value of salary is :{}”,salary); } The above code declares two variables by the name salary. The first declaration is assigned a 100.00 while the second declaration is assigned value 1.50. The second variable shadows or hides the first variable while displaying output. Output The value of salary is :1.50 Rust supports variables with different data types while shadowing. Consider the following example. The code declares two variables by the name uname. The first declaration is assigned a string value, whereas the second declaration is assigned an integer. The len function returns the total number of characters in a string value. fn main() { let uname = “Mohtashim”; let uname = uname.len(); println!(“name changed to integer : {}”,uname); } Output name changed to integer: 9 Unlike variables, constants cannot be shadowed. If variables in the above program are replaced with constants, the compiler will throw an error. fn main() { const NAME:&str = “Mohtashim”; const NAME:usize = NAME.len(); //Error : `NAME` already defined println!(“name changed to integer : {}”,NAME); } Print Page Previous Next Advertisements ”;
Category: rust
Rust – Iterator and Closure
Rust – Iterator and Closure ”; Previous Next In this chapter, we will learn how iterators and closures work in RUST. Iterators An iterator helps to iterate over a collection of values such as arrays, vectors, maps, etc. Iterators implement the Iterator trait that is defined in the Rust standard library. The iter() method returns an iterator object of the collection. Values in an iterator object are called items. The next() method of the iterator can be used to traverse through the items. The next() method returns a value None when it reaches the end of the collection. The following example uses an iterator to read values from an array. 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()); } Output Iter([10, 20, 30]) Some(10) Some(20) Some(30) None If a collection like array or Vector implements Iterator trait then it can be traversed using the for…in syntax as shown below- fn main() { let a = [10,20,30]; let iter = a.iter(); for data in iter{ print!(“{}t”,data); } } Output 10 20 30 The following 3 methods return an iterator object from a collection, where T represents the elements in a collection. Sr.No Methods & Description 1 iter() gives an iterator over &T(reference to T) 2 into_iter() gives an iterator over T 3 iter_mut() gives an iterator over &mut T Illustration:iter() The iter() function uses the concept of borrowing. It returns a reference to each element of the collection, leaving the collection untouched and available for reuse after the loop. 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 } Output Hello Kannan There is a rustacean among us! Hello Kiran [“Kannan”, “Mohtashim”, “Kiran”] Illustration – into_iter() This function uses the concept of ownership. It moves values in the collection into an iter object, i.e., the collection is consumed and it is no longer available for reuse. 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 } Output Hello Kannan There is a rustacean among us! Hello Kiran Illustration – for and iter_mut() This function is like the iter() function. However, this function can modify elements within the collection. 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 } Output Hello Kannan There is a rustacean among us! Hello Kiran [“Kannan”, “Mohtashim”, “Kiran”] Closure Closure refers to a function within another function. These are anonymous functions – functions without a name. Closure can be used to assign a function to a variable. This allows a program to pass a function as a parameter to other functions. Closure is also known as an inline function. Variables in the outer function can be accessed by inline functions. Syntax: Defining a Closure A closure definition may optionally have parameters. Parameters are enclosed within two vertical bars. let closure_function = |parameter| { //logic } The syntax invoking a Closure implements Fn traits. So, it can be invoked with () syntax. closure_function(parameter); //invoking Illustration The following example defines a closure is_even within the function main(). The closure returns true if a number is even and returns false if the number is odd. fn main(){ let is_even = |x| { x%2==0 }; let no = 13; println!(“{} is even ? {}”,no,is_even(no)); } Output 13 is even ? false Illustration fn main(){ let val = 10; // declared outside let closure2 = |x| { x + val //inner function accessing outer fn variable }; println!(“{}”,closure2(2)); } The main() function declares a variable val and a closure. The closure accesses the variable declared in the outer function main(). Output 12 Print Page Previous Next Advertisements ”;
Rust – File Input/ Output
Rust – File Input/ Output ”; Previous Next In addition to reading and writing to console, Rust allows reading and writing to files. The File struct represents a file. It allows a program to perform read-write operations on a file. All methods in the File struct return a variant of the io::Result enumeration. The commonly used methods of the File struct are listed in the table below − Sr.No Module Method Signature Description 1 std::fs::File open() pub fn open<P: AsRef>(path: P) -> Result The open static method can be used to open a file in read-only mode. 2 std::fs::File create() pub fn create<P: AsRef>(path: P) -> Result Static method opens a file in write-only mode. If the file already existed, the old content is destroyed. Otherwise, a new file is created. 3 std::fs::remove_file remove_file() pub fn remove_file<P: AsRef>(path: P) -> Result<()> Removes a file from the filesystem. There is no guarantee that the file is immediately deleted. 4 std::fs::OpenOptions append() pub fn append(&mut self, append: bool) -> &mut OpenOptions Sets the option for the append mode of file. 5 std::io::Writes write_all() fn write_all(&mut self, buf: &[u8]) -> Result<()> Attempts to write an entire buffer into this write. 6 std::io::Read read_to_string() fn read_to_string(&mut self, buf: &mut String) -> Result Reads all bytes until EOF in this source, appending them to buf. Write to a File Let us see an example to understand how to write a file. The following program creates a file ”data.txt”. The create() method is used to create a file. The method returns a file handle if the file is created successfully. The last line write_all function will write bytes in newly created file. If any of the operations fail, the expect() function returns an error message. 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” ); } Output data written to file Read from a File The following program reads the contents in a file data.txt and prints it to the console. The “open” function is used to open an existing file. An absolute or relative path to the file is passed to the open() function as a parameter. The open() function throws an exception if the file does not exist, or if it is not accessible for whatever reason. If it succeeds, a file handle to such file is assigned to the “file” variable. The “read_to_string” function of the “file” handle is used to read contents of that file into a string variable. 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); } Output Hello World TutorialsPoint Delete a file The following example uses the remove_file() function to delete a file. The expect() function returns a custom message in case an error occurs. use std::fs; fn main() { fs::remove_file(“data.txt”).expect(“could not remove file”); println!(“file is removed”); } Output file is removed Append data to a file The append() function writes data to the end of the file. This is shown in the example given below − 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”); } Output file append success Copy a file The following example copies the contents in a file to a new file. 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; } } } Execute the above program as main.exe data.txt datacopy.txt. Two command line arguments are passed while executing the file − the path to the source file the destination file Print Page Previous Next Advertisements ”;
Rust – Home
Rust Tutorial PDF Version Quick Guide Resources Job Search Discussion 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. Audience This tutorial has been prepared for beginners to help them understand the basic and advanced concepts of Rust. Prerequisites We assume that the reader has an understanding of basic programming concepts is necessary for this course. Print Page Previous Next Advertisements ”;
Rust – Smart Pointers
Rust – Smart Pointers ”; Previous Next Rust allocates everything on the stack by default. You can store things on the heap by wrapping them in smart pointers like Box. Types like Vec and String implicitly help heap allocation. Smart pointers implement traits listed in the table below. These traits of the smart pointers differentiate them from an ordinary struct − Sr.No Trait name Package & Description 1 Deref std::ops::Deref Used for immutable dereferencing operations, like *v. 2 Drop std::ops::Drop Used to run some code when a value goes out of scope. This is sometimes called a destructor In this chapter, we will learn about the Box smart pointer. We will also learn how to create a custom smart pointer like Box. Box The Box smart pointer also called a box allows you to store data on the heap rather than the stack. The stack contains the pointer to the heap data. A Box does not have performance overhead, other than storing their data on the heap. Let us see how to use a box to store an i32 value on the heap. fn main() { let var_i32 = 5; //stack let b = Box::new(var_i32); //heap println!(“b = {}”, b); } Output b = 5 In order to access a value pointed by a variable, use dereferencing. The * is used as a dereference operator. Let us see how to use dereference with 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 } The variable x is a value-type with the value 5. So, the expression 5==x will return true. Variable y points to the heap. To access the value in heap, we need to dereference using *y. *y returns value 5. So, the expression 5==*y returns true. Output true true Illustration – Deref Trait The Deref trait, provided by the standard library, requires us to implement one method named deref, that borrows self and returns a reference to the inner data. The following example creates a structure MyBox, which is a generic type. It implements the trait Deref. This trait helps us access heap values wrapped by y using *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 } Output 5==x is true 5==*y is true x==*y is true Illustration – Drop Trait The Drop trait contains the drop() method. This method is called when a structure that implemented this trait goes out of scope. In some languages, the programmer must call code to free memory or resources every time they finish using an instance of a smart pointer. In Rust, you can achieve automatic memory deallocation using Drop trait. 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”); } In the above example, the drop method will be called twice as we are creating two objects in the heap. dropping MyBox object from memory dropping MyBox object from memory Print Page Previous Next Advertisements ”;
Rust – Tuple
Rust – Tuple ”; Previous Next Tuple is a compound data type. A scalar type can store only one type of data. For example, an i32 variable can store only a single integer value. In compound types, we can store more than one value at a time and it can be of different types. Tuples have a fixed length – once declared they cannot grow or shrink in size. The tuple index starts from 0. Syntax //Syntax1 let tuple_name:(data_type1,data_type2,data_type3) = (value1,value2,value3); //Syntax2 let tuple_name = (value1,value2,value3); Illustration The following example displays the values in a tuple. fn main() { let tuple:(i32,f64,u8) = (-325,4.9,22); println!(“{:?}”,tuple); } The println!(“{ }”,tuple) syntax cannot be used to display values in a tuple. This is because a tuple is a compound type. Use the println!(“{:?}”, tuple_name) syntax to print values in a tuple. Output (-325, 4.9, 22) Illustration The following example prints individual values in a tuple. 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); } Output integer is :-325 float is :4.9 unsigned integer is :2 Illustration The following example passes a tuple as parameter to a function. Tuples are passed by value to functions. 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); } Output Inside print method (110, true, 10.9) Destructing Destructing assignment is a feature of rust wherein we unpack the values of a tuple. This is achieved by assigning a tuple to distinct variables. Consider the following example − 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); } Variable x is a tuple which is assigned to the let statement. Each variable – age, is_male and cgpa will contain the corresponding values in a tuple. Output Inside print method Age is 30 , isMale? true,cgpa is 7.9 Print Page Previous Next Advertisements ”;
Rust – Error Handling
Rust – Error Handling ”; Previous Next In Rust, errors can be classified into two major categories as shown in the table below. Sr.No Name & Description Usage 1 Recoverable Errors which can be handled Result enum 2 UnRecoverable Errors which cannot be handled panic macro A recoverable error is an error that can be corrected. A program can retry the failed operation or specify an alternate course of action when it encounters a recoverable error. Recoverable errors do not cause a program to fail abruptly. An example of a recoverable error is File Not Found error. Unrecoverable errors cause a program to fail abruptly. A program cannot revert to its normal state if an unrecoverable error occurs. It cannot retry the failed operation or undo the error. An example of an unrecoverable error is trying to access a location beyond the end of an array. Unlike other programming languages, Rust does not have exceptions. It returns an enum Result<T, E> for recoverable errors, while it calls the panic macro if the program encounters an unrecoverable error. The panic macro causes the program to exit abruptly. Panic Macro and Unrecoverable Errors panic! macro allows a program to terminate immediately and provide feedback to the caller of the program. It should be used when a program reaches an unrecoverable state. fn main() { panic!(“Hello”); println!(“End of main”); //unreachable statement } In the above example, the program will terminate immediately when it encounters the panic! macro. Output thread ”main” panicked at ”Hello”, main.rs:3 Illustration: panic! macro fn main() { let a = [10,20,30]; a[10]; //invokes a panic since index 10 cannot be reached } Output is as shown below − 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. A program can invoke the panic! macro if business rules are violated as shown in the example below − 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”); } The above example returns an error if the value assigned to the variable is odd. Output thread ”main” panicked at ”NOT_AN_EVEN”, main.rs:9 note: Run with `RUST_BACKTRACE=1` for a backtrace. Result Enum and Recoverable Errors Enum Result – <T,E> can be used to handle recoverable errors. It has two variants − OK and Err. T and E are generic type parameters. T represents the type of the value that will be returned in a success case within the OK variant, and E represents the type of the error that will be returned in a failure case within the Err variant. enum Result<T,E> { OK(T), Err(E) } Let us understand this with the help of an example − use std::fs::File; fn main() { let f = File::open(“main.jpg”); //this file does not exist println!(“{:?}”,f); } The program returns OK(File) if the file already exists and Err(Error) if the file is not found. Err(Error { repr: Os { code: 2, message: “No such file or directory” } }) Let us now see how to handle the Err variant. The following example handles an error returned while opening file using the match statement use std::fs::File; fn main() { let f = File::open(“main.jpg”); // main.jpg doesn”t exist match f { Ok(f)=> { println!(“file found {:?}”,f); }, Err(e)=> { println!(“file not found n{:?}”,e); //handled error } } println!(“end of main”); } NOTE − The program prints end of the main event though file was not found. This means the program has handled error gracefully. Output file not found Os { code: 2, kind: NotFound, message: “The system cannot find the file specified.” } end of main Illustration The is_even function returns an error if the number is not an even number. The main() function handles this error. fn main(){ let result = is_even(13); match result { Ok(d)=>{ println!(“no is even {}”,d); }, Err(msg)=>{ println!(“Error msg is {}”,msg); } } println!(“end of main”); } fn is_even(no:i32)->Result<bool,String> { if no%2==0 { return Ok(true); } else { return Err(“NOT_AN_EVEN”.to_string()); } } NOTE − Since the main function handles error gracefully, the end of main statement is printed. Output Error msg is NOT_AN_EVEN end of main unwrap() and expect() The standard library contains a couple of helper methods that both enums − Result<T,E> and Option<T> implement. You can use them to simplify error cases where you really do not expect things to fail. In case of success from a method, the “unwrap” function is used to extract the actual result. Sr.No Method Signature & Description 1 unwrap unwrap(self): T Expects self to be Ok/Some and returns the value contained within. If it is Err or None instead, it raises a panic with the contents of the error displayed. 2 expect expect(self, msg: &str): T Behaves like unwrap, except that it outputs a custom message before panicking in addition to the contents of the error. unwrap() The unwrap() function returns the actual result an operation succeeds. It returns a panic with a default error message if an operation fails. This function is a shorthand for match statement. This is shown in the example below − 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 Modify the above code to pass an odd number to the is_even() function. The unwrap() function will panic and return a default error message as shown below thread ”main” panicked at ”called `Result::unwrap()` on an `Err` value: “NOT_AN_EVEN””, libcoreresult.rs:945:5 note: Run with `RUST_BACKTRACE=1` for a backtrace expect() The program can return a custom error message in case of a panic. This is shown in the following
Rust – Useful Resources
Rust – Useful Resources ”; Previous Next The following resources contain additional information on Rust. Please use them to get more in-depth knowledge on this. Useful Links on Rust Rust − Official Website of Rust Rust @ Wikipedia − Rust, its history and various other terms has been explained in simple language. Useful Books on Rust To enlist your site on this page, please drop an email to [email protected] Print Page Previous Next Advertisements ”;
Rust – Input Output
Rust – Input Output ”; Previous Next This chapter discusses how to accept values from the standard input (keyboard) and display values to the standard output (console). In this chapter, we will also discuss passing command line arguments. Reader and Writer Types Rust’s standard library features for input and output are organized around two traits − Read Write Sr.No Trait & Description Example 1 Read Types that implement Read have methods for byte-oriented input. They’re called readers Stdin,File 2 Write Types that implement Write support both byte-oriented and UTF-8 text output. They’re called writers. Stdout,File Read Trait Readers are components that your program can read bytes from. Examples include reading input from the keyboard, files, etc. The read_line() method of this trait can be used to read data, one line at a time, from a file or standard input stream. Sr.No Trait Method & Description 1 Read read_line(&mut line)->Result Reads a line of text and appends it to line, which is a String. The return value is an io::Result, the number of bytes read. Illustration − Reading from the Console − stdin() Rust programs might have to accept values from the user at runtime. The following example reads values from the standard input (Keyboard) and prints it to the console. 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); } The stdin() function returns a handle to the standard input stream of the current process, to which the read_line function can be applied. This function tries to read all the characters present in the input buffer when it encounters an end-of-line character. Output Enter your name : Mohtashim Hello , Mohtashim no of bytes read , 10 Write Trait Writers are components that your program can write bytes to. Examples include printing values to the console, writing to files, etc. The write() method of this trait can be used to write data to a file or standard output stream. Sr.No Trait Method & Description 1 Write write(&buf)->Result Writes some of the bytes in the slice buf to the underlying stream. It returns an io::Result, the number of bytes written. Illustration – Writing to the Console – stdout() The print! or println! macros can be used to display text on the console. However, you can also use the write() standard library function to display some text to the standard output. Let us consider an example to understand this. 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(); } Output Tutorials Point bytes written 15 The stdout() standard library function returns a handle to the standard output stream of the current process, to which the write function can be applied. The write() method returns an enum, Result. The unwrap() is a helper method to extract the actual result from the enumeration. The unwrap method will send panic if an error occurs. NOTE − File IO is discussed in the next chapter. CommandLine Arguments CommandLine arguments are passed to a program before executing it. They are like parameters passed to functions. CommandLine parameters can be used to pass values to the main() function. The std::env::args() returns the commandline arguments. Illustration The following example passes values as commandLine arguments to the main() function. The program is created in a file name 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 } } The program will generate a file main.exe once compiled. Multiple command line parameters should be separated by space. Execute main.exe from the terminal as main.exe hello tutorialspoint. NOTE − hello and tutorialspoint are commandline arguments. Output No of elements in arguments is :3 [main.exe] [hello] [tutorialspoint] The output shows 3 arguments as the main.exe is the first argument. Illustration The following program calculates the sum of values passed as commandline arguments. A list integer values separated by space is passed to program. 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); } On executing the program as main.exe 1 2 3 4, the output will be − No of elements in arguments is :5 sum is 10 Print Page Previous Next Advertisements ”;
Rust – Data Types
Rust – Data Types ”; Previous Next The Type System represents the different types of values supported by the language. The Type System checks validity of the supplied values, before they are stored or manipulated by the program. This ensures that the code behaves as expected. The Type System further allows for richer code hinting and automated documentation too. Rust is a statically typed language. Every value in Rust is of a certain data type. The compiler can automatically infer data type of the variable based on the value assigned to it. Declare a Variable Use the let keyword to declare a variable. 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); } In the above example, data type of the variables will be inferred from the values assigned to them. For example, Rust will assign string data type to the variable company_string, float data type to rating_float, etc. The println! macro takes two arguments − A special syntax { }, which is the placeholder The variable name or a constant The placeholder will be replaced by the variable’s value The output of the above code snippet will be − company name is: TutorialsPoint company rating on 5 is:4.5 company is growing: true company icon is: ♥ Scalar Types A scalar type represents a single value. For example, 10,3.14,”c”. Rust has four primary scalar types. Integer Floating-point Booleans Characters We will learn about each type in our subsequent sections. Integer An integer is a number without a fractional component. Simply put, the integer data type is used to represent whole numbers. Integers can be further classified as Signed and Unsigned. Signed integers can store both negative and positive values. Unsigned integers can only store positive values. A detailed description if integer types is given below − Sr.No. Size Signed Unsigned 1 8 bit i8 u8 2 16 bit i16 u16 3 32 bit i32 u32 4 64 bit i64 u64 5 128 bit i128 u128 6 Arch isize usize The size of an integer can be arch. This means the size of the data type will be derived from the architecture of the machine. An integer the size of which is arch will be 32 bits on an x86 machine and 64 bits on an x64 machine. An arch integer is primarily used when indexing some sort of collection. Illustration 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); } The output will be as given below − result value is 10 sum is -10 and age is 20 mark is 10 and count is 30 The above code will return a compilation error if you replace the value of age with a floating-point value. Integer Range Each signed variant can store numbers from -(2^(n-1) to 2^(n-1) -1, where n is the number of bits that variant uses. For example, i8 can store numbers from -(2^7) to 2^7 -1 − here we replaced n with 8. Each unsigned variant can store numbers from 0 to (2^n)-1. For example, u8 can store numbers from 0 to (2^8)-1, which is equal to 0 to 255. Integer Overflow An integer overflow occurs when the value assigned to an integer variable exceeds the Rust defined range for the data type. Let us understand this with an example − 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); } The valid range of unsigned u8 variable is 0 to 255. In the above example, the variables are assigned values greater than 255 (upper limit for an integer variable in Rust). On execution, the above code will return a warning − warning − literal out of range for u8 for weight, height and score variables. The overflow values after 255 will start from 0, 1, 2, etc. The final output without warning is as shown below − age is 255 weight is 0 height is 1 score is 2 Float Float data type in Rust can be classified as f32 and f64. The f32 type is a single-precision float, and f64 has double precision. The default type is f64. Consider the following example to understand more about the float data type. 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); } The output will be as shown below − interest is 8.35 cost is 15000.6 Automatic Type Casting Automatic type casting is not allowed in Rust. Consider the following code snippet. An integer value is assigned to the float variable interest. fn main() { let interest:f32 = 8; // integer assigned to float variable println!(“interest is {}”,interest); }