Setup XM Cloud Site to host on Vercel

In this blog post we will Link the XM Cloud to Vercel. The XM Cloud site will be hosted in Vercel by Seting up the host

In this blog post we saw how to setup the Vercel Project manually but this whole process can be automated by Creating and linking the host.

Configure Hosting Connection

Login to XM Cloud and navigate to Connections –> Hosting tab

Click on Create connection –> Vercel

Login to Vercel and add Integration

Select the account to connect. In my case its personal account

Select the all projects to integrate, dont selecy any specific project or you will get error. See errors section-

Confirm and Add Integration-

A new Hosting connection is created with the name-

Setup Hosting

Navigate to Projects ==> Environments ==> Sites Tab

Link Vercel Site (Connection been already created)

You can create Hosting connection from here too.

Click “Create and link”

Now you can see the Site is linked to host i.e. Vercel.

Once the Site is linked you should be able to see the project in Vercel

Login to vercel and see the newly created project and deployment in-progress.

In few minutes the deployment should be completed

Click on the domains and should see the Site-

Errors-

Vercel installation will require ‘All Projects’ access. Please change the access on the Vercel installation

Resolution

Delete any exisitng Hosting Connection and re-create the hosting with Integration to be allowed to all projects.

Loading

Use Experience Edge GraphQL to access XM Cloud hosted site

GraphQL delivery API is used to access the published site content. Experience Edge for Experience Manager (XM) is an API-based service from Sitecore that gives you globally replicated, scalable access to your XM Cloud items, layout, and media. You can use the standard publish tools in XM Cloud , but instead of rendering content from a self-hosted Content Delivery environment, Experience Edge provides you a Sitecore-hosted GraphQL API. 

To access the XM Cloud hosted published site content using Expereince Edge follow these steps-

Login to XM Cloud. Navigate to Projects ==> Environments ==>Details tab.

Launch IDE for Live GraphQL

The URL for GraphQL IDE –

https://edge.sitecorecloud.io/api/graphql/ide

GraphQL edge endpoint is –

https://edge.sitecorecloud.io/api/graphql/v1

Let stry a simple query to get siteinfo-

If you see this error- JSON.parse: unexpected character at line 1 column 1 of the JSON data

This means a header with GraphQL token is not provided or incorrect.

Generate Delivery API token-

Add X_GQL-TOKEN in header with newly generated token vlaue

You should now be able to access only the published Site content using Experience Edge.

If you are not able to see the items, you might have not published the ietms

Publish items to edge

Loading

Create Vercel project to host XM Cloud Site

Sitecore XM Cloud Vercel connector helps to host Website to Vercel

Before using this connector you have to create Project and Environment.

Prerequisite

Before we ould start configuring Project in Vercel we need following info-

JSS APP Name- can be found and should be same as configured in package.json in sxastarter folder

In this case it is sxastarter

GRAPHQL ENDPOINT

GraphQL endpoint for published Sitecore items i.e. Delivery/Live endpoint is –

https://edge.sitecorecloud.io/api/graphql/v1

Sitecore API Key

This should be the GraphQL Delivery token i.e. GQL token-

There are different ways to generate GQL Token. One of the way is generate from portal. Login to XM Cloud and navigate to Projects ==> Environments ==> Details tab

Click on Generate Delivery API Key (Note this key as this won’t be avilable again and needs to be re-generated again if lost)

Note this API key as this is required later whilst configuring the Vercel hosting.

Publish Site

Publish the site to Edge before starting to configure Project in Vercel, since we are configuring GraphQL Delivery API token items in Sitecore needs to be published

Create a Project in Vercel

Login to Vercel and Create Project-

Since I have logged in using GitHub account it displays the repositories in Github the project will be based on-

Import the repository-

Configure Project

Once the project is created configure the project by selecting the Framework and repo folder FE code exists-

Choose the Nextjs Framework

Choose the sxastarter folder from the repo i.e. /src/sxastarter

Add following environment variables extracted earlier-

JSS_APP_NAME – sxastarter

GRAPH_QL_ENDPOINT – https://edge.sitecorecloud.io/api/graphql/v1

SITECORE_API_KEY – Delivery API Key generated from portal.

Should have this confiogured in the Vercel Project-

Deploy the Project

Deployment complete-

Visit the Site and should get the same view as configued in local environment and Experience Editor-

Thats all for how to create a Vercel Project and deploy site.

Errors-

If you see error-

The field 'item' does not exist on the type 'query'

Resolution-

Publish the site

Loading

Enums in Rust

Enum is a data type with mutiple possible variants. To declare an enum, write enum and use a code block with the options, separated by commas. These options are called as variants.

Define Enum

#[derive(Debug)]

enum PaymentStatus{
    Succeeded,
    Processing,
    Fail,    
}

Main Block

fn main() {
 
    let payment_status = PaymentStatus::Processing;
    println!("Enum - {:?}", payment_status);
}

Output

Enum - Processing

Match Operator

Match operator compares a value to the series patterns to determine which code to execute.

fn main() {
     let payment_status = PaymentStatus::Processing;
    println!("Enum - {:?}", payment_status);

    //let status = {};
    match payment_status{
        PaymentStatus::Succeeded => println!("Payment compelte." ),
        PaymentStatus::Processing => println!("Payment processing."),
        PaymentStatus::Fail => println!("Payment failed! Please try again!"),        
    }    
}

Output

Enum - Processing
Payment processing.

Enum Methods

Enum methods are plaved in the enum implementation i.e. imp <<Enum>>

Here we use wild card case ie. _ or the default match case.

Similar to a switch statement, Match operator compares a given value to a series of patterns to determine which code to execute.

impl PaymentStatus {
    fn after_payment_action(&self) -> &str{
        match *self {
            PaymentStatus::Succeeded => "UpdateERP",
            PaymentStatus::Processing => "Wait",
            PaymentStatus::Fail => "Block",
            _ => "NoChange"
        }
    }    
}

fn main() {
 
    let payment_status = PaymentStatus::Processing;
    println!("Enum - {:?}", payment_status);

    let action = payment_status.after_payment_action();

    println!("After payment request action --> {}", action )

}

Output

Enum - Processing
After payment request action --> Wait

Loading

Borrowing, mutable and dangling references in Rust

Borrowing reference

A borrowed reference allow you to access data without taking ownership of it. The & operator creates a borrowed reference to a variable.

If we want to pass the reference of string it can be done as follows-

Here the program uses tuple to pass the string refernce back with the length

{
    let inner_variable = String::from("welcome");
    
    let (note, note_length) = wlecome_note(inner_variable);

    println!("Length of {} is {}", note, note_length);
}

fn wlecome_note(note: String) -> (String, usize){
    let note_length = note.len();
    (note, note_length)
}

Output-

Length of welcome is 7

This is a tedious way, instead the string refernce can be borrowed in the function, unaffecting the refernece in main block.

{
    let inner_variable = String::from("welcome");
    
    let  note_length = wlecome_note(&inner_variable);

    println!("Length of {} is {}", inner_variable, note_length);    
}

fn wlecome_note(note: &String) -> usize{

    let note_length = note.len();
    note_length
}

Here & operator borrows the reference temporary into the note variable and when the it comes out of scope doesn’t affect the inner_variable in main block. This process is called as borrowing refernce.

Summary

  • Borrowing – Access data without taking ownership of it
  • Borrowing – Create reference using the borrow operator &
  • When working with reference its important to understand which variable own the data

Mutable reference

In the above code we didn’t change the reference i.e. the nore variable in wlecome_note function. To do so use push_str() method.

{    
    let inner_variable = String::from("welcome");    
    let  note_length = wlecome_note(&inner_variable);
    println!("Length of {} is {}", inner_variable, note_length);    
}

fn wlecome_note(note: &String) -> usize{
    let note_length = note.len();
    note.push_str(" to the Rust programming"); // Error
    note_length
}

This gives a compiler error.

The error is data we are referencing is not mutable reference. We have to tell Rust that it is a mutable reference. For this use &mut keyword .

{
    let mut inner_variable = String::from("welcome");   
    let  note_length = wlecome_note(&mut inner_variable);
    println!("Length of {} is {}", inner_variable, note_length);   
}

fn wlecome_note(note: &mut String) -> usize{
    note.push_str(" to the Rust programming");
   
    let note_length = note.len();
    note_length
}

Output-

Length of welcome to the Rust programming is 31

This updates the borrowed reference and main block has the updated data.

Summary-

When using a mutable reference, you cannot create other references, to prevent the data races.

If you’re only working with regular immutable references, then you can create as many of those as you want pointing to the same variable. The restriction comes in when you try to create references in addition to the one allowed mutable reference. Even if those additional references are immutable, it creates the potential for problems and the Rust compiler will not allow it.

Dangling references

A dangling reference is a reference to something that no longer exists at the referenced location.

A dangling reference can occur when a function returns a reference to a value that is owned by a variable whose scope is limited to that function.

It’s easy to erroneously create a dangling pointer—a pointer that references a location in memory that may have been given to someone else—by freeing some memory while preserving a pointer to that memory. In Rust, by contrast, the compiler guarantees that references will never be dangling references: if you have a reference to some data, the compiler will ensure that the data will not go out of scope before the reference to the data does.

{
   let reference_to_nothing = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello");

    &s // Error
}

Solution-

Remove borrow operator

{
    let reference_to_nothing = dangle();

    println!("Dangling refernce data is {}", reference_to_nothing);
}

fn dangle() -> String { // Remove borrow operator
    let s = String::from("hello");

    s // Remove borrow operator
}

Output-

Dangling refernce data is hello

Slices

Borrowing a data type with sequence of elements, if we only want subset of elements we use Slice. Commonly used slices is String &str

let  message = String::from("Welcome to Rust");
println!("message is {}", message); 

let last_word = &message[10..];
println!("last word is - {}", last_word);

Here we sliced the string to only get the last word i.e. Rust. Here the message variable is still the owner of the string. and last_word variable points to the address of the slice.

Output

message is Welcome to Rust
last word is Rust

&String != &strString Type is not String Literal

With strings, a borrowed reference to a string is not equivalent to a slice from the string. They’re different data types.

Dref Coercion- The deref gives the compiler the ability to take a value of any type that implements Deref and call the deref method to get an & reference that it knows how to dereference

Summary –

  • Borrow the string object to slice the string &my_string[4..]
  • Once you create one mutable reference to a variable you cannot create any other references to it.
  • A slice only stores a pointer to the heap data, along with length information. The slice doesn’t need to keep track of capacity because the slice will never own anything on the heap.
  • A string reference will point to an actual string on the stack, which in turn points to and owns the string data that lives on the heap. A slice will only store a pointer to the heap data.

Reference-

https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html

Loading

Ownership of resources in Rust

Ownership in Rust where the variables are responsible for creating their own resources.

Every value is owned by one, and only one variable at a time

When the owning variable goes out of scope, the value is dropped

Memory Management in Rust and other languages

Memory ManagementFeatureAdvantageDisadvantage
Manual Allocation and Deallocation Progammer responsible for memory mgmt
C/C++ – malloc() and free()
Programmer has lots of controlMemory leaks
Garbage CollectionAutomatically cleans up memory
Java, Python, C#, Ruby, Go
Its easy as programmer don’t have to worry about memory mgmtCan lead to wasteful memory
Can run at inconvenient times
OwnershipVariables are responsible for freeing their own resources
Rust
Safe, Effecient (when data will be dropped at compile time)Requires understanding of ownership

ExampleEvery value is owned by one, and only one variable at a time

{
    1let inner_variable = String::from("Welcome");
    println!("inner_valriable is {}", inner_variable);        
}2

println!("inner_valriable is {}", inner_variable);3 // Error
  1. The memory is allocated at this line of code in Stack and points to heap where the data is stored. ↩︎
  2. Deallocates/drops the data in heap and stack since the scope is complete ↩︎
  3. Error – Variable is nto available here since its deallocated in prevous line. ↩︎

ExampleWhen the owning variable goes out of scope, the value is dropped

Ownership is moved to another variable (outer_variable)

let outer_variable : String;
{
    let inner_variable = String::from("welcome");
    println!("inner_valriable is {}", inner_variable);   
    outer_variable = inner_variable;
}

println!("outer_variable is {}", outer_variable);

X The above example breaks the rule since 2 variables assigned to same address in heap. Hence this is called as move the inner_varaible is out of scope.

    let outer_variable : String;
    {
        let inner_variable = String::from("welcome");           
        outer_variable = inner_variable;
        println!("inner_valriable is {}", inner_variable); // Error
    }

    println!("outer_variable is {}", outer_variable);

Since the inner_variable scope is moved to outer_variable the memory allocation is dropped.

Copy works only for stack data types, such as integer and floating point

Cloning the data

The clone() method is used to create a deep copy of heap data.

String data is not implicitly copied and must be explicitly cloned using the clone() method.

let outer_variable : String;
{
    let inner_variable = String::from("welcome");           
    outer_variable = inner_variable.clone();
    println!("inner_valriable is {}", inner_variable);
}

println!("uter_valriable is {}", outer_variable);

Clone creates a new heap and copies the data and binds to the stack.

Output

inner_valriable is welcome
outer_valriable is welcome

Copy occurs implicitly; cloning must be done explicitly

Transfering Ownership

When a function takes ownership of a value, it becomes responsible for that value and its associated data. The function can then do whatever it wants with the data, including modify or delete it. If the function doesn’t need the data anymore, it will be dropped.

Hence Rust is Safe i.e. at compile time the program knows when the variables are accessible and efficient since it knows when to drop the data from memory.

Summary-

Shadowing allows you to declare a new variable with the same name as an existing variable.

Copying allows you to duplicate a value that is stored on the stack.

The heap can dynamically add and remove data.

The stack stores values in sequential order. The stack adds and removed data as LIFO.

Copying data on the stack occurs implicitly, whereas cloning data on the heap must be done explicitly using the clone method.

The String data type stores the sequence of characters that make up the String on the heap and it stores a structure containing a pointer to the heap data, the String’s length, and its capacity on the stack.

Rust uses an ownership model to manage memory.

Loading

OrderCloud Headstart Docker Setup Error- package installation must use TLS 1.2 or higher

0 47.60 npm notice Beginning October 4, 2021, all connections to the npm registry – including for package installation – must use TLS 1.2 or higher. You are currently using plaintext http to connect. Please visit the GitHub blog for more information: https://github.blog/2021-08-23-npm-registry-deprecating-tls-1-0-tls-1-1/

0 114.7 npm ERR! Cannot read properties of null (reading ‘pickAlgorithm’)

Update the UI docker file in this location- \headstart\docker\build\UI\Dockerfile

Change this to https

RUN npm config set registry https://registry.npmjs.org/ --global

Also clear cache –

This should resolve the error

Loading

Loops and conditional statement in Rust

Conditional Statement

An if expression is used to control which code gets executed based on conditions the program can evaluate at runtime.

Short hand to use if condition.

let y =true;
let x = if y {1} else {0};
println!("x is {}", x)

Output-

x is 1

Loop

Rust provides a loop keyword to indicate an infinite loop. Unless stopped manually or with a break staement, loop can go infinitely.

Infinite loop example. Manually stop the program

let mut count=0;

loop{
   count += 1;
   println!("count is {}", count)
}

break keyword

Immediately exit the loop and continue execution forward. The break statement can be used to prematurely exit all types of loop constructs.

Here once loop is at 10 it breaks the loop and continues further execution

let mut count=0;

loop{
   if count == 10{
      println!("breaking the loop on count  {}", count);
      break;
    }
    count += 1;
    println!("count is {}", count);
}
println!("Continues execution.");

Output-

count is 1
count is 2
count is 3
count is 4
count is 5
count is 6
count is 7
count is 8
count is 9
count is 10
breaking the loop on count  10
Continues execution.

break with return

In the below code break can return the result or the reason the loop is stopped to the execution program.

let mut count=0;

let result = loop{
    if count == 10{
        println!("breaking the loop on count  {}", count);
        break count * 5
     }
     count += 1;
     println!("count is {}", count);
};

println!("Count result is - {} * 5 = {}", count, result);

Output-

count is 1
count is 2
count is 3
count is 4
count is 5
count is 6
count is 7
count is 8
count is 9
count is 10
breaking the loop on count  10
Count result is - 10 * 5 = 50

Predicate (while) loop

Predicate loop that repeats a block of code while the condition is true. Begins by evaluating a boolean condition and breaks/completes when the condition is false. A while-loop repeats a block of code while a condition is true.

Here break statement can be used to terminate the loop but it doesnot return the value as comapred to loop

let mut count=0;
while count < 5 {
   count += 1;
   println!("count is {}", count);
}

Output

count is 1
count is 2
count is 3
count is 4
count is 5

Iterator for loop

The for in construct can be used to iterate through an Iterator. One of the easiest ways to create an iterator is to use the range notation a..b. This yields values from a (inclusive) to b (exclusive) in steps of one.

For loops are mainly used to iterate over the items in a collection, and execute some code for each one so that you can process each item in the collection individually. If you need to repeat a block of code for a specific number of times that you know before starting the loop, you can use a ‘for’ loop to iterate over the range from zero to that value.

for n in 1..5{
   print!("{}\t", n)
}

loop 1..5, here 1 is inclusive while 5 is exclusive. Print! won;t print in new line.

Output

1       2       3       4

user iter() methodto iterate over the list-

let message= ['r','u','s','t'];

for msg in message.iter(){
    print!("{}\t", msg)
}

Output-

r       u       s       t

Iterator with index

At times apart from the element in the array or list and index is required. This can be achieved by using enumerate() method

let message= ['r','u','s','t'];

for (index,msg) in message.iter().enumerate(){
     println!("item {} is {}", index, msg)
}

Output-

item 0 is r
item 1 is u
item 2 is s
item 3 is t

Nested loops

Example here taking row and columns and printing those values in a nested loop i.e. for loop(rows) wthin for loop(columns)

let matrix = [[1,2,3],[10,20,30], [100,200,300]];

for row in matrix.iter(){
    for col in row.iter(){
        print!("{}\t", col);
     }
     println!();
}

Output-

All the columsn are printed in same line while row in new line.

variable.iter() is used to iterate between rows and columns

1       2       3
10      20      30
100     200     300

Modify the value in for loop using iter_mut()

Sometimes value in the collection needs to be updated. This can be done by iterating and updating the values. To do so use iter_mut() function as follows-

let mut matrix = [[1,2,3],[10,20,30], [100,200,300]];

for row in matrix.iter_mut(){
   for col in row.iter_mut(){
         *col += 5;
         print!("{}\t", col);
    }
    println!();
 }

Reference – https://dhghomon.github.io/easy_rust/Chapter_17.html

Notice here the iter_mut() method is used which give a reference to the mmeory allocated for the collection. In the next line *col is used to deference so that a addition operation can be performed.

Using * is called “dereferencing”.

Output-

6       7       8
15      25      35
105     205     305

Rust has two rules for mutable and immutable references. They are very important, but also easy to remember because they make sense.

  • Rule 1: If you have only immutable references, you can have as many as you want. 1 is fine, 3 is fine, 1000 is fine. No problem.
  • Rule 2: If you have a mutable reference, you can only have one. Also, you can’t have an immutable reference and a mutable reference together.

Loading

Functions in Rust

Functions are use to  efficient programming and eliminates the need to constantly rewrite code.

Rust is a strictly typed language and therefore needs to know the data type for all variables at compilation.

When there is not a specified return value, the function will return the unit data type which is represented as () and indicates there is no other meaningful values that could be returned. A statement does not return a value.

In the below example we can see how a function can be defined and called from the main.

Function without and with parameters. Define the parmeter data type and return data type.

fn main(){
    display_message();
    display_message_with_parameters(10);
    let addition  = add_numbers(10, 20);
    println!("Addition of 2 number is {}", addition);
}

fn display_message(){
    println!("Display message....");
}

fn display_message_with_parameters(value : i8){
    println!("Function paramter value is {}", value);
}

fn add_numbers(param1: i8, param2: i8) -> i8{
    param1 + param2
}

Output-

Display message....
Function paramter value is 10
Addition of 2 number is 30

Diverging Functions

Diverging functions never return. They are marked using !, which is an empty type.

fn foo() -> ! {
    panic!("This call never returns.");
}

Empty return functions

Function return empty (). It doesn’t return anything but returns back to caller

fn some_fn() {
    ()
}

fn main() {
    let _a: () = some_fn();
    println!("This function returns and you can see this line.");
}

Arrays and tuples in Rust

In Rust arrays are stored in contigious section of memory locations and sorted in order. They are fixed length and cannot be dynamically resize, containing elements of same data type.

A fix size array denotes [T;N]. where T denotes element type i.e. i32, u64 etc. and N denoted non-negative compile time constant size. This means compiler needs to know before running the code the size of array.

One way of declaring an array is to have the values comma seperated in square brackets e.g.- [1, 2, 3]

Example-

let fruits = ["apple", "orange"];
let selected_fruit = fruits[0];

// Retrieve first element of an array.
println!("Selected fruit is - {}", selected_fruit);

Output-

Selected fruit is - apple

To modify the value in the array make the array mutable and change the value of an element in array, in this case first element.

let mut fruits = ["apple", "orange"];
fruits[0]= "mango";

let selected_fruit = fruits[0];
println!("Selected fruit is - {}", selected_fruit);

Output-

Selected fruit is - mango

Repeat expression – Declaring arrays with the data type and length [expr, N] where N is the times to repeat expression (expr).

Note that [expr; 0] is allowed, and produces an empty array. This will still evaluate expr, however, and immediately drop the resulting value, so be mindful of side effects.

let numbers : [i32; 5]; // declare array
numbers= [1, 3, 5, 7, 9]; // initalize array
println!("Selected number - {}", numbers[0]); // print first element

Output-

Selected number - 1

Compiler Errors-

If array is not initalised and the element of array is tried to access the compiler thorws an error-

let numbers : [i32; 5];
println!("Selected number - {}", numbers[0]);

Solution-

let numbers : [i32; 5];
numbers = [0;5]; // All elements has value 0
println!("Selected number - {}", numbers[0]);

Output-

Selected number - 0

Compiler Errors-

Index out of bounds – if the element accessed is larger than the array length.

let numbers : [i32; 5];
println!("Selected number - {}", numbers[5]);

The array length is 5 and index starts from 0, so the last index is 4. But in above program the index 5 is trying to access hence error

Solution-

The element accessed here is the last element i.e. index 4.

let numbers : [i32; 5];
numbers = [0;5]; // all elements has value 0
println!("Selected number - {}", numbers[4]);/ Compiles successfully

Count of elements

Get the length of an array using extension function len()

let numbers : [i32; 5];
numbers = [0;5];
println!("Length of numbers is {}", numbers.len());

Output-

Length of numbers is 5

Slice Array

Sleic the arrary by giving an expression the starting and ending index with double dots between. e.g.- [starting_index..ending_index]. Access the memory of the variable and provide the range e.g.- &array_variable[2..4]

Print the array and the slice using {:?}

Omit start index and end index [..4] and [2..] respectively

let numbers : [i32; 5] = [10, 20, 30, 40, 50];
let array_slice = &numbers[2..4];
let omit_start_index = &numbers[..4];
let omit_end_index = &numbers[2..];
println!("array = {:?}", numbers);
println!("slice = {:?}", array_slice);
println!("omit start index = {:?}", omit_start_index );
println!("omit end index = {:?}", omit_end_index );

Output

array = [10, 20, 30, 40, 50]
slice = [30, 40] // [2..4]
omit start index = [10, 20, 30, 40] // [..4]
omit end index = [30, 40, 50] // [2..]

Multi-dimensional array

Can be declared as follows-

let fruits = [["1", "2"],["apple", "orange"]];
println!("Selected fruit is - {}", fruits[1][1]);

Tuples

Tuples are similar to arrays except that tuples can have mixed data types. Similar to arrays tuples are stored in a fiexed-length and the data types must be know at compile time with elements are ordered.

Tuples is declared with comma seperated elements with round braces – (“hello”, 5 , ‘c’)

Here the first element is string then integer and character. See below example how to declare tuples and access elements.

Example-

// declare and inititaize tuple
let tuple = ("Hello", 5 , 's');
// Access elements
println!("First element in tupple is - {}",tuple.0)

Output-

First element in tupple is - Hello

Explict declaration can be done as follows-

Example-

// declare and inititaize tuple
let tuple: (&str, i32, char) = ("Hello", 5 , 's');
// Access elements
println!("First element in tupple is - {}",tuple.0) 
// Prints "First element in tupple is Hello"

Updating element in tuple

First mark the variable as mutable. Access the element of the tuple and update.

// declare and inititaize tuple
let mut tuple: (&str, i32, char)    = ("Hello", 5 , 's');
// Access second element and add 5
tuple.1 += 5;
// Print updated element
println!("Add 5 to second element in tupple - {}",tuple.1)

While updating the element be carefull that the element value does-not exceeds the max value a data type can hold or there will be memory overflow error.

Copy the tuple object

Continuing to the aboce code, decalre the tuple with 3 elements, assign the updated tuple and access the declared variable in tuple.

// declare and inititaize tuple
let mut tuple: (&str, i32, char)    = ("Hello", 5 , 's');
// Access second element and add 5
tuple.1 += 5;
// Print updated element
println!("Add 5 to second element in tupple - {}",tuple.1);

let (first, second , third ) = tuple;
println!("Copied tuple third element is {}", third);

Output-

Add 5 to second element in tupple - 10
Copied tuple third element is s

Warnings-

Above code, first and second element are never used, hence the compiler will give warnings – unused variable:

Although it won’t stop the compiler from running the code. The suggestion here is to use prefix tuple element with _ (underscore) i.e. _first and _second. Lets change this and see if there are no warnings.

    // declare and inititaize tuple
    let mut tuple: (&str, i32, char)    = ("Hello", 5 , 's');
    // Access second element and add 5
    tuple.1 += 5;
    // Print updated element
    println!("Add 5 to second element in tupple - {}",tuple.1);

    let (_first, _second , _third ) = tuple;
    println!("Copied tuple third element is {}", _third);

Now there are no warnings and compiles with results-

Reference

https://doc.rust-lang.org/std/primitive.tuple.html

https://doc.rust-lang.org/rust-by-example/primitives/array.html