This beginner's guide to Rust programming introduces core concepts including the Rust toolchain (rustc compiler, Cargo build system), command-line argument handling using std::env::args(), iterator methods like nth(), and error handling with Option and Result types. The course demonstrates practical applications through two projects: a CLI calculator that performs arithmetic operations and an image combiner that merges two images by alternating their pixels. Key topics include struct and module organization, pattern matching with match expressions, and proper error propagation using the question mark operator. The curriculum emphasizes Rust's unique approach to memory safety and systems programming, helping beginners understand how to write reliable, efficient code while managing low-level details safely.
Rust Programming Tutorial for Beginners: A Comprehensive Guide
Added:Welcome to the rusting Replit course. This course will introduce you to the rust programming language. With it in the description. There are links to various materials used and mentioned in this course. Specifically, there is a one click link to the interactive course powered by rapid which allows you to learn rust completely within your browser. There is also a link to the slides mentioned, which can be used as a cheat sheet for us. Finally, there is a link to an article written by yours truly, which walks through rust and the interactive projects. This course assumes intermediate knowledge in at least one other programming language. If you are new to programming, why not try the interactive Free Code Camp curriculum. This is an interactive course going over the basics of the rust programming language within your browser. First, you will get an overview of rust. The course covers the basics of rust, but you can also find some notes of this in the linked slides or article. Then you can follow the interactive content to create your own CLI calculator. Where you can perform basic arithmetic operations through the command line. Then you will create an image combiner this project takes two images and combines their pixels into one new image. First an overview of rust. A quote from the official rust book rusts deals with low level details of memory management, data representation and concurrency. The language is designed to guide you naturally towards reliable code that is efficient in terms of speed and memory usage. The main tooling within the rust ecosystem are rusty, which is the compiler that takes your rust code and compiles it into a binary which is machine readable code rust up which is the command line utility to install and update rust and cargo, the rust build system and package manager which you will get a lot of experience using throughout this course. And now moving on to the first project the CLI calculator. To begin, I have a real built using the next language in Replit. This allows me to define the necessary tooling for the project. The link to this template is available in the description of this video. If you want to go too long. You can use cargo to create a new Ross project in the current working directory by typing in cargo new followed by the project name, which we will call calculator. This creates a new directory named calculator initializes it as a git repository and add useful boilerplate for your project. The cargo Tamil file is the manifest used by cargo to manage your project metadata. This is similar to a package dot json file if you are familiar with those. The main.rs file is the default entry point for your application. You can see it contains a function declaration using the FM keyword with the handle main as well as a call to the print ln macro which prints to the console in rust. A macro is denoted by the exclamation mark character, which is also known as the being symbol. To run the project, navigate into the calculator directory then you can either use the cargo Run command which will compile your code and then run the compiled binary. As you can see hello world is printed out. Or you can use rust C which is the compiler where you pass in an argument as the location to your entry point file. In our case, this is the source folder and the file is main.rs. This will compile your code into a binary and it will be placed within your working directory then you can run the binary throughout this project, I will just be using cargo to run the project. So let's remove that file the rust standard library comes with an EMV module, which allows access to the command line arguments passed when calling the program. The necessary exports from the EMV module for this project are the OGS function and the OG struct. The OGS function returns an instance of the OG struct and is imported into the file scope with the following syntax use keyword followed by standard the module name in V, the name of the function OGS, and the name of the struct To get an idea of what the abstract looks like, you can declare a new variable named OGS using the let keyword and assign the value of the return of calling the function OGs. Then, use the print line macro to print the value of the OGS variable. To do this, place curly brackets within the string as the first argument and pass the variable you want to go within the curly brackets. Now, if you run this code, you will see the following error Augs cannot be formatted with the default format. Because the trait display is not implemented for OGs. That's a lot to take in. But for now, this error comes about because the print line macro does not know how to format the abstract. However, you can just follow the compilers advice here to extend the format using the code on question marks syntax. Now, running this code, you should be able to see the output. As you can see, the abstract is printed to the console. And it contains an inner field, which consists of the location of the compiled binary. You can confirm this by running the application and passing it an argument Free Code Camp. Now we can see Free Code Camp is in the inner Ray. When we ran our code, you might have noticed that the compiler threw a warning saying we have an unused import. We don't strictly have to import hogs. But if you want to explicitly type the OGS variable, you can do so using the OG struct that we imported like so. Now running the code, that warning should no longer be there. To access the arguments within the OG struct within the OGS variable, you can use the Enter method which exists on the OG struct. First, let's declare a new variable named first. And assign it the value of calling the Enter method on OGs. Hovering over the method, you can see the function definition where it looks like it takes in two arguments. However, being a method on a struct, the first argument is implicit. And is usually a reference to self where self is the struct. In this case, this is a mutable reference to self. argument that needs to be passed in is in which is a use side a number and it returns the nth element of the iterator which is wrapped in an option. Let's unpack what all of that means. So accessing the first argument by passing zero is printed to the console. You'll notice you will still get an error if you run the code. This error is saying that the OGS variable cannot be borrowed as mutable. We noticed that in the definition, the nth method took a mutable reference to in this case OGs. So, in order to use the method, we need to declare OGS as immutable variable. After doing that, we should be able to run the code. And this is what the first variable is value is it is some wrapping the value we are interested in. Some is a variant on the option enough. This will become more clear as we define enums later on. For now. Just know that you can unwrap options to get the value inside. rerunning this code, we should see that the value is no longer wrapped in sum. However, in this case, if we pass in an index where there is no argument, running the code, you will see that the application panic. Panicking is rust 12 erroring out during runtime. You can see three main panics That called optional unwrap on a non value. This is because when there is no value to access using the end method, none is returned, as opposed to some with a value. So instead of unwrapping, let's just print that value. And first the variable is none. And and none has no value, therefore, it cannot be unwrapped. To fix this, let's get the first argument out. And once again, unwrap now running our code, you will see that we get Free Code Camp. We know we want our application to take in three arguments, first being the first number, as well as an operator being plus times divide, or minus. And this will be the next argument. Let's unwrap that as well as a third argument being the second number. To get an idea of all of these, let's print them out to the console. First operator and second. Running this, let's add some arguments. We would expect to use this application by going one plus two for example. However running this, our application panics apparently, we are still unwrapping on a non value. Well, which of these is the non value, it's keep adding arguments to find out. Application still panics for the application and still panics. Now we are passing six arguments in its application and it is still cranking that add more finally, the application doesn't panic. And this is because the nth method returns an element of an iterator and we know it needs to be the iterates over the OGS variable. Internally what this means is the nth method will look something like this when we have a mutable reference to self as well as n, which is the use size or the index. And we know this returns an option where it is either a string of the value we want or not. Now South Quino is our OG struct. But what nth does is for each n we can assume N in this case as an example, assume it equals zero. So we are accessing the first element. There is some sort of iterator over self. Accessing let's see that in array and it is calling next on that array. As you can see next advances the iterator and returns the next value. So, in accessing the first element when the method is called a second time the iterator has passed the first element. So that second call calling next again will result in the second element being passed. Let us look at it from this perspective. If at this point in her is equal to an array of these two arguments. Calling next brings up the first argument. Cooling next again, would bring up the second argument So, as we are unwrapping here, and let's get rid of this as we are unwrapping the variable into first, which in the case of our call would be one, calling the method again on to this time starts at the plus argument, and two becomes the first element after that. And then three is the second index of the iterator. Practice Practically, this means if we want to access the elements we are interested in what we actually need to do when we are calling just this code. And we don't want it to panic. We want the first indexed element. Running this should yield what we want. Now, unlike some other languages, you cannot perform basic arithmetic operations on strings. And all of these variables that we have here are strings. So we need to parse them into numbers, or inoculate in our case floats. So we can declare a new variable first number, and set that equal to first which is a string, use the past method on the string, which we can see here returns a result. And a result is similar to the option enum in that it can be unwrapped to get the value out. And we can do the same for the second number to be second, dot pass dot unwrap. Now, we should get a warning that rust doesn't know what type we want the number to be, because there are many kinds of numbers in Rust, we need to give it a type annotation. And we can do this in many ways. One is to annotate the variable this way in which case the parse method knows to take second as a string and parceled into NF 32. All my preferred method for doing this is to use something called turbo fish syntax, which is fun to use and fun to say. This can be done because on the parse method you can see it accepts F which is the type and it returns a result of that type. So, let's change second number to that as well. Now, let us declare a new function called operate and this will take in the operator and we can have the operator of your car which is the type character we will need the first number which we know is in F 32. And the same with the second number now we can use a bunch of if statements to compare if the operator is equal to the character we expect for example, in this case, the addition character and what we can do is return first number plus second number. Now, first thing that we will come across is an error that there are mismatched types which expected an empty tuple but found an F 32. And this is because we have not added a return type annotation to the function we can do that with this syntax. Another thing to note is the type car in Rust This is a character literal made using single quotes. Whereas if we had used double quotes that would be of the type string let's continue with this logic. Adam an else if statement. Comparing if operator is equal to a minus. Then we can return first number minus second number else if operator is equal to divide by then return first number divided by second number. And finally, the last operation we'll need is if operator is equal to an asterix, or the multiplication operator we will get a warning that we don't have all of our clauses, where the if statement is missing an else clause. This is because in Rust if the operator did not match any of those, then the function will return an empty tuple. And as we have typed the function to return an F 32, it cannot return an empty tuple. So we add the base case otherwise, return for now we can just return zero. But you'll notice you cannot just type zero because it expects an F 32. And floats require the decimal place. Something else to note about rust is it allows implicit returns. What this means is you can get rid of the return keyword as well as the semicolon at the end. And this is exactly the same as what we had before. But this is the more recitation way of writing the code. And we can do this throughout. Now, to use the function operate, you can declare a new variable called result. Call the function passing in the operator first number and second number. However, you'll notice that we get a warning because operators have type string, but the function expects the type car. So we need to convert the operator into a character. And we can do this by using the cost method on the string, which returns an iterator over the characters of a string slice. And with the iterator we can call the next method to access the first element of that slice. And the next method returns an option which we know we can unwrap if the value exists. Now, we can print our result let's run the code to see if this all works as expected. And we can see one plus two, the result is equal to three. However, this is not the final output we want. And what we can do is declare a new function called output which will accept the first number which we know to be an F 32 The operator which is a car the second number which is also an F 32. And then the result which is that operation on the two numbers which is an F 32. We will want this to return a string and in order to get the output the way you want, we can use the format macro, which is very similar to the print line macro in that the first argument is a reference to a string size which we can pass in additional arguments which will be placed within the curly brackets. So we want first number operator, second number is equal to result and format returns a string. Now to use this what we can do is call output and pass in first number. The operator the second number and the result Hopefully running the code gets us what we expect. And that's correct, one plus two is equal to three. Let's try something else. Let's try one minus two, negative one. However, there's something much better we can do to improve the readability of the code. And that is instead of a bunch of elsif statements, we can use rusts pattern matching expression, which starts with match fall followed by the expression to be evaluated, which in our case is the operator and then this is called a match arm. And what this is, is if cooperating matches the plus character then evaluate the following first number plus second number. Now, because we are not including a semicolon at the end, this is implicitly returned and we can continue with this logic. Now, we will get an error, saying that we have a non exhaustive pattern. Because similarly to the if statements, they need, where we needed an else clause, the match expression needs a base case, which can be represented by the underscore and we can still return the 0.0. And you will see that this code runs exactly the way it did before. However, returning zero if the operator is invalid, does not make too much sense. So it might make more sense to panic. And you can do that using the panic macro and pass it a message such as invalid operator used. Now what this will do is if an invalid operator is used, the code will panic. You can see it has panicked at invalid operating used. But now, what if we wanted to extend our application to not just accept more multiplication? What's the server say? This is an interesting case where we need to escape the Asterix. In order to run the code. To get around that, you can extend the operator function to not just work with an asterix, or not just match with an asterix, which needs to be escaped each time. But it is common to multiply with an x. And we can extend it further using bitwise OR for capital X. What this allows, is instead of escaping the Asterix, we can just use x to represent multiplication. And this will work just fine. And that is our application finished. All that's left to do is build it and to do so you can use cargo build to compile the binary and you can also pass the release flag. What this does is it provides some optimizations for the compiler and removes all debugging information running this will compile our code. The code is found in the target directory in the release directory and it is called the binaries name. We can run this code now with arguments add the period at the beginning and that we get the same result. So that is the CLI calculator finished. For the image combiner project, start by creating a new rust project using the Cago new command. And let's call this combiner then cd into the combiner directory and run the project to ensure it is set up correctly. You should see hello world printed to the console. This project is bigger and more complex than the calculator project. So to prevent this The main.rs file from becoming too overwhelming, create a new file within the source directory called arcs.rs. This file will be a new module named arcs, which will house the logic used to handle the command line arguments. Within arcs.rs create a function named get enter arg which takes a U size in and returns a string. Then, we know from the previous project that we can access the EMV module inside the standard library and call the arcs function chaining the nth method to get the nth argument. And let's unwrap the value. Now, to group the related command line argument data, you can create a struct named OGs. You've already encountered structs in the previous project. This was the string struct and the struct from the EMV module. Instead of using the NV OG struct, you will be defining a custom arc struct specifically for use in this application. The string struct looks something like this where we can declare the struct using the keyword struct that contains the Vic field, which is a veck of uate Vic, in this case is a dynamically sized array. So following this syntax create a struct named Augs. The three fields image one which is a string, Image two, which is also a string and output to also be a string, these will be the paths to the images passed as arguments within main.rs. The OG struct can be imported once the OGS file is declared as a module using the mod keyword. So mod OGS declares the file as a module and then we can import it using the use keyword. You can use the arc struct by declaring a new variable named arcs in this case and assigning it the value of the arcs followed by the field name and values for those fields. You will notice the error saying we cannot use OGS here because it is not public. It is a private field in a private struct it is a private struct to make it public use the pub keyword before the struct definition now you will notice that each of the fields is also private. So similarly before each definition, the public key word can be used to declare the field as public and therefore accessible outside of the current module. As you can see here to create a new string the new function has been used. The reason this function can be used is because it has specifically been implemented for the string struct the implementation would look something like this. Where function new has been declared To be implemented on the string struct, where it returns self being string. And as you can see a string is created within it and implicitly returned. And the same goes for the new function within the veck struct. Now, if you're creating many OG structs, it would be beneficial to be able to do the same thing that is implement a new function on the OG struct within the OGS module. So you can use the imple keyword to implement new on the struct OGs. We new does not take in any arguments, no to return self. Where we can take that code we had previously here. This time, returning it from the new function, then we can call Augs. New to instantiate a new org, you'll notice that there is again an error that new is private. So before the function declaration, use the public keyword to make the new function public. If we were to print out the value of ocsea You'll notice two things. First being that there is an error, which we've encountered before saying that the treat display is not implemented for OGs. Now, you may think that you could just extend this as we have done previously. However, we still get error, where the debug trait is not implemented for arcs. In order to implement the debunker trade, we can actually follow the compilers advice here to add this syntax to the struct definition, what this does, this is an attribute that is deriving the debug trait for the OG struct. And after doing this, we should be able to print out the OG struct. However, you'll notice that currently, the fields are all empty strings. This is not what is needed for the OGS fields. So, we can replace the string new call with the get end org function passing in one as argument to the first chord because the zeroeth argument is the path to the binary continuing this logic we can pass in to to get the second argument and similarly three to get the third argument now if we run our code should get an error because we are not passing in any any arguments. So calling unwrap within the get nth OG function should cause a panic. What we can do is test this by calling our application with some arguments. And as we can see, image one has the value first image to second and output third. For this application, an external crate called image is required to decode and encode the images. Much the same way other languages have libraries or packages. Rust has crates you can add the image creator to application by creating a new dependency in the Kaga dot Tamil file this is the name of the crate followed by its version number in our case 0.2 3.1 For now, running cargo run will fetch and install the image crate whilst the image creators installing a new function can be created within main.rs named find image From path we'll take an argument path, which is a string. And we will set the return later. The image crate comes with an IO module, including a reader struct, we can create a new variable Image Reader set that equal to the reader struct. And the struct implements an open function which takes a path to an image file and returns a result containing the reader. So, we can pass in the path as the argument and we can unwrap the result this reader can be formatted and decoded to yield the image format for example, a PNG or a JPEG as well as the image data and new variable image format will be assigned the unwrapped value of the dot format method on the reader the imagery to being the variable Image Reader dot format and we can unwrap the value from within the format and variable image can be assigned and unwrapped value of the decode method on the reader. Now, we are interested in both the image and the image format variables. So the two values can be returned in a tuple. The image as well as the image format can be returned in a tuple. To get an idea of the types we need to return, we can explicitly type our variables we know this to be a reader, but the reader as a type of a BufferedReader from a file the image format is also a type from the image create which is image format. And the image another type from the image create which is a dynamic image. So, we know we need to return a tuple from this function and in the order to be a dynamic image and an image format then, the necessary imports can be included at the top of the file that is importing from the image create the reader is in the IOE module and from the image create the dynamic image and the image format you'll most likely get these errors until crater has been installed. Now within the main function the return tuple can be D structured into two new variables which we will call image one and image format one from find image from path parsing in the image one field from the OG struct and the same can be done for image two. You will notice the error about missing imports for the explicit types within find image from path. The BufferedReader and file structs need to be imported from the standard library. BufferedReader is within the IO module. And file is within the FS module. There is an error here in the print line statement. But for now, we can get rid of that line because we won't be using it. It is important to handle errors which are likely to arise such as the case where three images of different formats are given as arguments to combine. For example, we will need some logic where if image one format is not equal to Image two format, then only to return an error as well way of handling such an error is to return a result we have encountered some results already. But a result can consist of either an okay, or an error variant. What that practically looks like is if we find the format's are not equal to each other, we return an error normally with a value within what this means is our function needs to be typed to return a result and that result needs to contain some values. But we also need the base case where if there is no error of function returns okay, it is common to see results return an empty tuple when there is no error, what this means is our result the type returned on okay is an empty tuple. Otherwise, it will be some error, the error we will return is an enum and this is because enums are useful for acting as both types. And as well for now we can call our error as an image data error. And we will need to define this enum. To do so, we use the enum keyword followed by the name of our enum as well as the different variants on Ina. We have seen this for example, with the option email which is defined similarly to this where it takes some with a value or it just returns none. In our case, we will define one of the variants to be different image formats. As such, we can turn in within our main function an error of image dot errors. The variant different image formats, we just need to fix this year image format one image format to to make combining the images easier, the largest image is resized to match the smallest image first in to find the smallest image which we can do using a function called Get smallest image or get smallest dimensions I should say. Because the image will consist of two dimensions, each image will have in this case, it will be a tuple containing the width as a U 32. And the height as a U 32. And the second image we will pass in its dimensions which will be the same to use 30 twos in a tuple what we will want to do is return the smallest dimension as a tuple to compare the sizes, we can declare one variable variable being the number of pixels in the first dimension, which will be the first dimension access the first element in that tuple multiplying by the second element in the same tuple and we can do the same with the second tuple. Now what we are comparing is the number of pixels in the image. To do this we will return if the number of pixels in the first image is less than the number of pixels in the second image, then we will return the first image is dimensions else the second image is dimensions we will create a function called standardize size which will take in the first image which we know to be a dynamic image as well as the second image which again is a dynamic image and it will return both images. We can again return them in a tuple however, these images will be resized. And what we want is Using our get smaller dimension function, we want to return the smallest with the smallest height. So, we're creating two new variables by destructuring the return of get smallest dimension passing in image one getting its dimensions by using the dimensions method available on the dynamic image struct and the same with Image two. Except this is good smallest dimensions there you go, I think it is helpful if we print out the values of the width and the height which we can do like so and we can also add a new line at the end now, what we want to do is in order to standardize the size, we will first find out if for example, Image two steam engines are equal to the width and the height that we got from calling get smallest dimensions on the image, then we will know that image two is the smallest image and therefore, what we want to do is we will want to resize image one and we can use the Resize exact method which is implemented on the dynamic image struct. immutably borrows the image and using the width, height and phototype arguments resizes the image so, we can pass in the width that we want the height that we want. And now we need to pass in a filter type. This type also comes from the image crate and the type we will be wanting to use is triangle this is just used for its simplicity as well as it generally does a very good job of resizing the image without distorting it too much. Now, we will need to import triangle it exists in the image drops filter type number and it is the type of triangle now, we are wanting to return a tuple with the resized images. So, what we can do is after resizing the first image, we will then also just return Image two along with it in a tuple and we will need an else clause that is if image one is the smaller image then return a tuple with image one unchanged, resizing Image two passing in the new width we want the new height and the filter type. You can see there are errors about using the dimensions method on the dynamic image and this is because the trade has not been brought into scope. So, we can follow the compilers advice to get the generic image view treat from Image generic image view and that should be enough to get rid of the errors for now, fixing the typo in the standardize size functioning we can then have to claim the format declare image one image two again this time destructured from the standardize size function having resized whichever image is the largest. Now it is all in fine that we have we are handling resizing the images. However, we know that the end goal is to create a new image Using the pixels from the two input images. So to handle the output, a temporary struct can be created to hold the metadata for the output image. And we can call this struct floating image. And the metadata we will want is the width as a U 32. The height as a U 32. It will contain some data being the pixel values as a veck of uate. Because the pixel values will exist between zero and 255, AQa to perfectly fits that number type. We also want the name of the image as a string. In order to create this floating image, we can do as we did previously with the old struct and we can implement the new function which will take in a width and a height as well as the name and it will return a new floating image. And what this will look like is floating image with a value of the input width and height. We know that there will be some sort of data as well as the name for the data. When we create the new floating, we won't have the pixel values yet, but we will need to reserve the space for them in memory. In order to do this, we can declare a buffer and that buffer could be a fixed value, such as et Cie buffer capacity, set this to some number, for example 3,655,744. And then the buffer itself will be a vector with the capacity of that buffer capacity and then we can assign the buffer to the Stata. An interesting thing about rust is that you can use this number format to make your numbers easier to read, where you separate three digits of your number using an underscore. However, what we have here assumes that any image we input will not be larger than essentially this number of bytes that we can more dynamically size this, based on the fact that we know that our image will consist of a height, you can multiply that by the width. And to get the number of pixels, we multiply this by four. The reason for this will be clearer as we go through this next we will be using RGB a values. So we are multiplying by four because there we are using the red to green, the blue and the alpha values for each pixel. You will see we have a warning here that there are mismatched types with capacity function expected to use size but found a u 32. The reason but capacities of type U 32. It's because we use the height and the width to create it which are you 30 twos. Normally, freshmen automatically convert or trying to convert the number type into the correct type. However, in this case, we cannot be certain that this is possible. So we can follow the advice the compilers giving to try to convert the value. Otherwise, if that's not possible, fail or in this case panic. So we can try into and then unwrap. However, in order to use the train method, we need to bring it into scope and it exists in the standard library and the Convert module of the tray into implementation. So we can just bring that into scope. Now within main we can create an output variable containing the new floating image. We will use image ones with and image one's height as the inputs for the new function. We could use Image two as well. But considering image one and Image two have been resized to be the same size the width and height will be identical anyway. And we will want to pass in the output field from the OG struct. As this will be the name of the function and the location which we will later use. At this point, we can deal with the error that we have occurred, which is saying that the trait debug is not implemented for image data areas. We can resolve this by following compilers advice to use the attribute and derive the debug trait for our image dot two areas a number. The reason this is necessary is because main returns the standard output the result of running the code normally either being a status of one or zero. And in order to return to the standard output, the debug trait is to be derived. From here, we can run our code to see if everything is working as expected. What we will need are two images as inputs to get these images, I will just be importing them into our combine a project. Within the images folder we have three images here. First being my profile picture. The other being the Free Code Camp blogger on the other end inverted Free Code Camp logo that we might use later. Then in order to test our code, we can go Cago run passing in the arguments which we know to be images folder, we can pass in my profile image as well as the image to the Free Code Camp. But these images are PNGs we also need to specify an output location which we can say is within images. And we can set it to test a PNG we have not currently implemented anything for the output image to go into However, we still need to provide the argument get a few warnings code panic because I called the wrong folder nigga. Running again we can see that the width and the height output. So everything is working as expected. As I said earlier, in order to process images, they are converted into a vector of RGB pixels. And pixels are stored as you eight because the values are between zero and 255. So in order to process our images, we will be creating a new function called combine images. And this will take an image one, which we know to be a dynamic image image to the same and this will return their cookie weights. The dynamic instruct implements the to RGBA method, which returns an image buffer containing a veck of UAT. And the image buffer implements the into vac method which returns the back itself. So to visualize that a bit better, let's declare a new variable called veck one which will be image one we will call the to RGBA eight method on the dynamic image you'll notice that it returns an image buffer containing an RGB you eight as well as the back of your eight. So in order to get the backs out, we can call the into BEC method on the image buffer. Likewise, we can do for vector in image to in processing our images, we will want to alternate the pixels of the two images. So what we will do is we will create a function called alternate pixels. And what this will do taking the two vectors and it will return a vector of us for us. So let's define that function we know that it takes in too thick of UAT. It also returns a vector view ID. First, we need to declare a variable Java can combine data that will hold the data of the two images after they have been alternated, in essence, where we are pushing the pixels from one vector and then putting the pixels from the next vector. And in order to size, the combined data, we will match it using a VAC macro, we want you weights. And we want the number of your weights to be the same length as any of the vectors passed. In this case, they are the same, but we will just set them to be the size of the first vector, what this reg macro does, it will create for example, if Vic one dot length is equal to 100, or let's say five, then the veck macro will create a vector of have you rates of that same length. Now, what else we will do is we will use a while loop to go over each pixel. In this case, we will refer to the pixels as indices within the length of the vector. And first, we will need to declare this pi variable. And we will know that we'll want to mutate our variable so we'll declare it as a mutable variable starting at zero and within the while loop, we will increment by four this is done because we are using our GPA sets within the vector and being four values we need to increment i by four as we axing accessing the indices within the vector using the modulo operator, we can see that if I modular eight is equal to zero, we will splice into combined data the RGBA set from FX one, in order to do this, we need to set the range in our case the range will be from I until I plus three and we will need to get the RGBA set from the first vector. To do this, we will create a little function set GBA what this will do, it will take in the first vector, it will take in a starting index as well as an ending index and it will return the RGBA Tet from those indices. Otherwise, we will want to do similarly this time passing in vector so, let's define that set RGBA function we know that it takes in a vector this vector will be evaluates to have a starting index, which is you size as well as an end index also a new size, we will want to return the vacuolated. In order to do this, we will be declaring a mutable vector RGBA. It's just a new vector. And for every index in start until the end inclusive, you will declare any variable Val we will set this equal to Feck dot get and pass in the index that we are interested in. Then, after doing that, we will push that index to the RGBA vector. We will push that value and then we will want to return that vector we have initially here we get returns an option. What we could do, it's just unwrapped to get the value. However, there is a chance that that value doesn't exist. So what would actually be better is for us to match on get cool. And if there is some value. It's called bad value d. What we want to do is return the value If there is none, what we want to do is potentially panic and say that the index is out of bounds. Now, you will notice that we get an error, telling that the function expected to return a vector of the rates but instead is returning an empty tuple. To resolve that, we can return the combined data. You'll notice we have an issue here where combined data is being spliced. So what we actually need to do is declare it as mutable. And we get an error down here saying that it expected function is expected to return evaluates however, it is actually returning a veck of UAT. But the UAT are referenced. What this means is we are pushing reference values. In this case, currently, Val is equal to a reference of UAT. What we want is for it to be just a UAT. In order to do that, we can use the Asterix before the variable what this does is dereference the value that resolves the error, however, now we have a new error, saying that the borrow of a moved value back one where the value borrowed after being moved. Here, we can see that vac one has been moved. Why this is is because within the while loop, we are passing Vic one into the set RGBA function, we don't actually want to pass in the whole value, what we want to do is we want to pass in a reference to a value pointer. And the same needs to be done. With back to now we need to adjust the function definition as it's not accepting back you at accepting reference to evaluate. To clarify. The syntax here is rusts range syntax, which allows the range to be inclusive of the end value. So far, we haven't called these functions yet. However, what we need to do first is to set the data of bind data into the output image which we actually need to call output. A method on floating image needs to be defined to set the data field of the output to the value of that combined data because currently what we have for the data field is just a buffer not the actual pixel values we are interested in. So far, you have only defined functions on struct. However, now we are wanting to define a method methods are defined in a similar way, but they take an instance of the struct as their first argument. You saw this with the next method. So we are going to define a method called set data on the floating image. What this will do is with we'll take a mutable reference to self as well as the data we are wanting to set which is effectively weights. What this will do is return a result either on okay to return an empty tuple or to return one of our image data error. Variance what we can do is first make sure that data dot length is greater than the current floating images capacity. If the length of the data is more than the capacity of the current floating image, what this means is we cannot store we did not reserve enough buffer in order to store that data. So here we will return an error. And this type of error will be image dot errors. And we'll need to declare a new variant and this will be buffered to small. So we need to do to find that variant to buffer to small. Otherwise, we can just set the data to the data that is passed into the method. In that case, the result is okay. Now within main, we can call that method on output. Passing in the combined data. You'll notice that again only one argument is passed into set data because implicitly the first argument of a method is self. You need to define combined data. Let's do that first and say that combined data equal to we have the combine images function. And it takes in image one and Image two. And what we've done is we've made that set dotta returns a result. And the interesting thing you can do with the rest is propagate that result fairly, by adding a question mark at the end of the expression. What this will do is essentially unwrap the value but it's propagated into current function and as main already returns a result of either Okay, with an empty tuple, or an error with image data errors, which is the same function signature as set data, we can use error propagation operator. Finally, the new image needs to be saved to a file, the image create has a safe buffer with format function, which works like this. We can use safe buffer. With format, we need to pass in the path of where we want to save the image, which we have stored in the Name field of output, we need to pass in the data from output. However, this needs to be a reference to the data and not passing the value itself. resizing the width of the new image. And the height of the image needs to define the color type of the image, we can get the color type from the image create. And we know that the type is RGBA. And they are you eight. And finally the format of the image, which will be the same as image one format. This function also returns a result so we can unwrap that. And I need to remember to correctly name my variables, you'll see we still get an error about output, where we can offer it as mutable because there's not declared it mutable. This is because inside data as a method, it takes a mutable reference to self, which in this case is output. So we need to declare our output variable has been beautiful. To test out our image combiner we can once again cover run and pass in arguments, which we know to be images to my profile PNG images to the FCC glyph. And we output this image into the images folder. And we will call it output dot png. Running this will compile the code. And once it has finished, we should be able to navigate into that images folder and find the output. And there it is the profile image. And if we go can bluff combined into one image. Although the application is finished, we have a lot of calls to unwrap within our code. What this means is if at any point there is an error during runtime, our code will panic. And it may not provide the best user experience all the best error to help develop and debug as they're using the application. So, one thing we can do to extend this as we already have some basic error handling within our main function, where we are returning result where if there are any errors, we are returning image data areas, which is enough we can extend this further with some more common types of error. So, for example, fixing up the find image from path function instead of unwrapping on the reader open returns a result. So, what we can do is we can match on the result and if the expression is okay, we will get the Image Reader otherwise, if there is an error, we can get this error out in a variable named E and then we can return an image data error and we will add a new type of unable to read image from path as a variant on image dot areas you know and we can pass an E as a value to the INA. So let us define this New variant we named it unable to read image from path and we know it takes in some sort of error. Now, this error exists in the standard library as standard IO error. Next, we need to move our logic within this match on that we can continue with the next unwrap statement. However, format returns an option. And the general rule of thumb I follow with how to handle options versus results is as we have already done with a result we match on the result and we handle the okay variant versus the error variant. However, if we are dealing with an option what I like to do is use an if let statement and that looks something like this. If let there is a value that is the option returns some then we can declare a new variable called image format. And if this is the case, we can perform some logic else we can return an error in this case, you'll need to explicitly return error image data errors and we will declare a new variant called unable to format image and something we can do here is pass in the path that way our error has more information with it as to which image that failed to read. Now, we cannot pass in part as is because we are moving at into this open call. So what we need to do is passing a reference to the path to the first open call and then we can define that variant and we call that variable unable to format image and it took in a string then handling the image logic as decode returns a result we can match on the result match on the result and in the arms. If okay, we can get the value in a variable called image we can return okay with the tuple that we expect to be an image as well as the image format. Otherwise, if there is an error, we can get the error in a variable called E and return that error in again another variant of image data errors and this variant we will call unable to decode image and we will pass in that error as is. So, to declare this new variant, which we named unable to decode image, it takes in a specific error from the image crate which is called Image error. As such, we need to add it to our dependency then we can remove some of this logic that we are no longer using and means to fix up some things. So, we need to define our function to return a result and on this result, it is either returning a tuple which we originally had otherwise that you know it is returning one of the image data areas variance then as we learned earlier earlier, we can propagate the errors into the main function as we are calling it using the error propagation operator. And finally, we have one more unwrap to deal with and this is on the safe buffer with format function. Now, what we can do with this is as it is returning a result however, we are only actually interested in the case where there is an error, we will again use an if let expression de structuring the error from the call. This way, we can just return the error if there is one in a new variant called unable to save image and passing that error. Otherwise we can return okay To define that variant, which we named unable to save image, and we are passing in, again, an image error from the image creator, we have also reject this to no longer be using these imports. So what we can do is just remove that. And just like that they are handling our areas in a much better way to inform both the developer and all the final user of what is happening when any of these areas arise.
Up Next

Building a 256-Byte RAM in a Digital Logic Simulation
@SebastianLague
419.5K views•2025-04-12

Introduction to Secure Multiparty Computation with Yehuda Lindell
@fhe_org
7.7K views•2021-02-04

Python for Data Analysis: Numpy, Pandas & Visualization
@freecodecamp
3.2M views•2021-02-18

Enigma Machine Mechanics: WWII Encryption Explained
@JaredOwen
13.2M views•2021-12-11
Related Study Plans & Knowledge Roadmaps
Structured learning paths in Computer Science















![Introduction to Memory [AP Psychology Unit 5 Topic 1] (5.1)](https://i.ytimg.com/vi_webp/WXbOd_nL1Jw/maxresdefault.webp)













![Пишем приложение «Погода» для iOS за 1 час [GeekBrains]](https://i.ytimg.com/vi_webp/Ykyia6eMQk8/maxresdefault.webp)














