Industrial Training




Rust Strings

Rust contains the two types of strings: &str and String.


String:


    A string is encoded as a UTF-8 sequence.
    A string is allocated on the heap memory.
    A string is growable in size.
    It is not a null-terminated sequence.

& str


    '&str' is also known as a string slice.
    It is represented by &[u8] to point the UTP-8 sequence.
    '&str' is used to view the data present in the string.
    It is fixed in size, i.e., it cannot be resized.

Difference b/w 'String' and '& str'.


    A String is a mutable reference while &str is an immutable reference to the string, i.e., we can change the data of String, but the data of &str cannot be manipulated.
    A String contains the ownership on its data while &str does not have ownership, it borrows it from another variable.

Creating a new String


A String is created similarly as we create the vector. Let's look at this:


Creating an empty String:
Let mut s = String::new();  

In the above declaration, String s is created by using new() function. Now, if we want to initialize the String at the time of declaration, we can achieve this by using the to_string() method.


    Implementing the to_string() method on the data:

let a = "javaTpoint";  
let s = a.to_string();  

    We can also implement the to_string method directly on the string literal:

let s = "javaTpoint".to_string(); 

Let's understand this through an example:
fn main()  
  
 let data="javaTpoint";  
 let s=data.to_string();  
 print!("{} ",s);  
 let str="tutorial".to_string();  
 print!("{}",str);  

Output:
javaTpoint tutorial

    The second way to create the String is to use String::from function, and this is equivalent to the String::new() function.

Let's understand this through a simple example:
fn main()  
    {  
          let str = String::from("javaTpoint tutorial");  
          print!("{}",str);  
    }  

Output:
javaTpoint tutorial

Updating a String


We can change the size of the String and content of the String as well by pushing more data into the String. We can also use the '+' operator of the format macro! To concatenate the string values.



    Appending to a string with push_str and push

push_str() : We can grow the size of the String by using the push_str() function. It appends the content at the end of the string. Suppose s1 and s2 are two strings and we want to append the string s2 to the string s1.


s1.push_str(s2); 

Let's understand this through a simple example:
fn main()  
{  
  let mut s=String::from("java is a");  
  s.push_str(" programming language");  
  print!("{}",s);  
}  

Output:
java is a programming language

The push_str() function does not take the ownership of the parameter. Let's understand this scenario through a simple example.


fn main()  
{  
  let mut s1 = String::from("Hello");  
  let s2 = "World";  
  s1.push_str(s2);  
  print!("{}",s2);  
}  

Output:
World

If push_str() function takes the ownership of the parameter, then the last line of the program would not work, and the value of the s2 will not be printed.
push() : The push() function is used to add a single character at the end of the string. Suppose the string is s1 and character ch which is to be added at the end of the string s1.


s1.push(ch);  

Let's see a simple example:
fn main()  
{  
  let mut s = String::from("java");  
  s.push('c');  
  print!("{}",s);  
}  

Output:
javac

    Concatenation with the '+' operator or format macro

'+' operator: The '+' operator is used to concatenate two strings. Let' look:


let s1 = String::from("javaTpoint ");  
let s2 = String::from("tutorial!!");  
let s3 = s1+&s2;  

Let's see a simple example:
  fn main()  
{  
 let s1 = String::from("javaTpoint");  
 let s2 = String::from(" tutorial!!");  
 let s3 = s1+&s2;   
 print!("{}",s3);  
}  

Output:
javaTpoint tutorial!!

In the above example, s3 contains the result of the concatenation of two strings, i.e., javaTpoint tutorial. The 's1' is no longer valid, and we use the reference of the s2, i.e., &s2 according to the signature of the method which is called when we use the '+' operator. The '+' operator calls the add() method whose declaration is given below:


fn add(self,s:&str)->String  
{  
 }    

Firstly, s2 has '&' operator means that we are adding a reference to the s1. According to the signature of the add() function, we can add &str to a String, and we cannot add two string values together. But the type of s2 is &String not &str according to the second parameter specified in the add() method. But still, we able to use the s2 in the add method because the compiler coerces the &string into &str. Therefore, we can say that when we call the add() method, then Rust uses deref coercion.
Secondly, the first parameter of the add() function is self and add() takes the ownership of self. This means that s1 is no longer valid after the statement let s3=s1+&s2;


format! Macro
    When we want to concatenate multiple strings, then the use of '+' operator becomes very clumsy in this case. To concatenate the multiple strings, use of format macro is preferred.
    The format macro works similarly as println! macro. The difference between format macro and println! macro is that format macro does not print on the screen, it returns the content of the string.

Let's understand this through a simple example:
fn main()  
{  
 let s1 = String::from("C");  
 let s2 = String::from("is");  
 let s3 = String::from("a");  
 let s4 = String::from("programming");  
 let s5 = String::from("language.");  
 let s = format!("{} {} {} {} {}",s1,s2,s3,s4,s5);  
 print!("{}",s);  
}   

Output:
      C is a programming language.

Indexing into Strings


A String is encoded in a UTF-8 sequence. Therefore, the string cannot be indexed. Let's understand this concept through an example:


fn main()   
{  
    let s = String::from("javaTpoint");  
    print!("{}",s[1]);  
}  

Output:
error[E0277]: the trait bound `std::string::String: std::ops::Index< {integer}>` is not satisfied
 --> jdoodle.rs:4:17
  |
4 |     print!("{}",s[1]);
  |                 ^^^^ the type `std::string::String` cannot be indexed by `{integer}`
  |
  = help: the trait `std::ops::Index< {integer}>` is not implemented for `std::string::String`

error: aborting due to previous error

Accessing through an index is very fast. But, the string is encoded in a UTF-8 sequence which can have multiple bytes and to find the nth character in a string will prove expensive operation.


Slicing Strings


Indexing is not provided in the string as it is not known about the return type of the indexing operation should have byte value, character or a string slice. Rust provides a more specific way to index the string by providing a range within [] rather than a single number.


Let's look:
let s = "Hello World";  
let a = &s[1..4];  

In the above scenario, s contains the string literal, i.e., Hello World. We specify [1..4] indices means that we are fetching the substring from a string s indexing from 1 to 3.


fn main() {  
      
    let s = "Hello World";  
    let a = &s[1..4];  
    print!("{}",a);  
}  

Output:
ell

Methods for iterating over strings


We can access the string in other ways also. We can use the chars() method to iterate over each element of the string.


Let's see a simple example:
fn main()   
{  
    let s = "C is a programming language";  
    for i in s.chars()  
    {  
      print!("{}",i);  
    }  
}  

Output:
C is a programming language



Hi I am Pluto.