Rust Tutorial For PHP and JavaScript Developers
Chapters9
Setting up rustup and the core tools rustc and cargo to manage Rust projects.
A friendly, hands-on Rust intro for PHP/JS developers, walking through cargo, rustup, a tiny to-do CLI, and basic Rust concepts.
Summary
Nuño Maduro demonstrates how to get Rust up and running on a developer machine, explaining cargo, rustc, and rustup as the Rust equivalents to Composer and PHP’s binaries. He builds a small CLI app (to-do) to illustrate a binary versus a library, showing how to create a project with cargo new and how the cargo.lock and target folders fit into compilation. Throughout, Nuño compares Rust concepts to PHP: immutable variables, the standard library, and the edition system versus PHP versions. He refactors the main file into command modules, introduces a command trait, and sketches a crude persistence layer using a local storage.txt file to store to-dos. The talk also covers basic CLI argument parsing with vectors, match statements for command dispatch, and a Naive implementation of add/list commands with simple error handling. Finally, Nuño touches testing with cargo test and shares tips on learning Rust (the Rust Book) and using GitHub Copilot for productivity. He ends with personal notes, a quick tour of production Rust projects, and encouragement to experiment beyond this minimal example.
Key Takeaways
- Install Rust and set up a project with rustup and cargo; you’ll get rustc and cargo immediately after running the installer.
- Cargo provides analogues to PHP Composer: you create, manage dependencies, and scaffold new projects with cargo new; cargo.lock tracks dependencies, similar to composer.lock.
- Rust variables are immutable by default; you can opt into mutability with the mut keyword when you need to change a value.
- You can organize code in Rust using modules and structs; interfaces are traits, and you can implement a trait for multiple structs to share a common handle method.
- Rust’s standard library and modules (e.g., std::env, std::fs, std::process) are where you handle CLI args, I/O, and process control, without third-party dependencies for a basic app.
- A minimal CLI can persist data by writing to a local file (storage.txt) and read it back for display, illustrating a simple to-do storage workflow.
- Tests in Rust live alongside code files (mod tests or #[cfg(test)]) and cargo test runs all tests, outputting concise results without a separate test runner.
Who Is This For?
Essential viewing for PHP and JavaScript developers curious about Rust, especially those who want a practical, code-first introduction to building a CLI and comparing core concepts to what they know in PHP.
Notable Quotes
""The first thing we are going to do is install Rust on my machine.""
—Intro to the setup process and Rust toolchain.
""Variables on rust are not actually variables meaning that they don't change the value. They are immutable by default.""
—Explains Rust’s immutability concept for beginners.
""Rust edition is the equivalent of the PHP versions. We have editions and this is the edition required to run my Rust project.""
—Clarifies editions vs. PHP versioning.
""If you have some description coming from the description option, I want to execute this code right here.""
—Shows handling optional CLI arguments safely.
""Cargo test -Q" hides compilation steps and runs unit tests, a Rust-native way to ensure code reliability."
—Demonstrates Rust’s built-in testing workflow.
Questions This Video Answers
- How do I install Rust and set up my first project with cargo and rustup?
- How does Rust’s immutability differ from PHP variable behavior in practice?
- What is the Rust edition system and how does it compare to PHP versioning?
- How do you structure a small Rust CLI with modules and traits similar to PHP classes?
- How does Rust’s testing with cargo test work and how do I write basic tests for CLI commands?
Full Transcript
So the first thing we are going to do is install Rust on my machine. Just be mindful that installing Rust on my machine works in the same way that installing Rust on Ubuntu or Windows operating system. And what we have to do is really type this command, this script called Rust up. Okay. And I can download it with the curl for example and run it. And what rust up does it's mainly installing two binaries on my machine. One is rust C and the other one is cargo. Okay. So we are going to get those two tools rust C and cargo.
Once I run this script, once it's run, we get access to cargo and cargo to give you the equivalent on PHP is pretty much the composer of Rust. With this, we can add dependencies, remove dependencies, and even start a new project. So, this is the first tool we get. After cargo, we also get something called Rust C. And Rust C is pretty much the equivalent of the PHP binary in in the sense that with Rust C, you actually compile code that you can run afterwards. And with PHP you interpret and run code. So they're equivalent but in different languages.
Again with cargo you can add dependencies, remove dependencies but also start a new project. You can do the similar thing with composer create project. So let's do that. I'm going to type cargo in it to-do. Now one thing to keep in mind here is that if I just do it like this, I'm going to create a new binary application. However, if I pass the option d-le, I'm going to create a new package like a library basically to others can use. In this case, I'm going to remove the d-l lib and I'm going to simply type cargo in need to-do.
In this, as the feedback on the terminal is telling me, we are going to we are creating a binary application. Now, let's take a look about the file structure here. So, I'm going to open this to-do on my left and I'm going to open this file with a name cargo.l file. The cargo file is the equivalent of composer.json, meaning that we have all the meta information about our uh application. In this case, I obviously have my application name which is to-do the version of my application which is 0.1. On the equivalent side on PHP, we actually tag use g tag to actually know the the version of the package.
However, on rust use this flag version. Then we have the rust edition. The rust edition is the equivalent of the PHP versions. We have PHP 5, PHP 7, PHP 8. However, with Rust, we work with editions. Okay? And this is the edition required to run my Rust project. Then on the bottom I have this list of dependencies similar to composer required dependencies where I can specify which community dependencies my Rust project is using. I could come here and do something like Krono equals to 4.0.0. And this would simply mean that my project uses the community datetime library of Rust which is Chrono.
For my CLA app I actually don't need any dependencies. So I'm going to keep this list empty for now. Now I want to show you the cargo.lock file which is another file and for that I'm going to compile my project u for the first time and to do that I'm going to simply type cargo run to see u I need to enter on the folder first and then I'm going to type cargo run. When I type cargo run the first thing I see on top is the compilation steps of my project. So I can see that my project got compiled and then finished compiling and after is kind of executing the result of the compilation and I can see hello world on my console which is exactly what my source code is telling me to do.
Once I run cargo for the first time I can actually run carun-q which is my favorite way of running cargo which basically hides all the compilation steps I see here on top and just prints exactly what my uh program is printing out. So once we run cargo for the first time, I get this file which is cargo.lock and this is pretty much the equivalent of the composer.lock. It's basically a manifest of the dependencies used with my my project. Then I also have this target uh folder which contains all the temporary files result of the compilation.
I kind of want you to ignore this folder for now. Just be mindful that this target folder represents some of the temporary files used on the process of compilation. And then we have our source directory. So pre equivalent to PHP. However, within this source directory, we have our main rs file. The main rs is the equivalent of the public index.php which is the entry point of your application. And I can see as well that within this main rs I have a function with the name main which is equally the entry point of my app. And we can immediately start understanding some stuff here about Russ.
The first thing is the declaration of functions. I can declare functions with the fn keyword then giving the function name and then the list of parameters. Now as you can see here the type is not specified. This is one cool thing about rust is that the type inference is very strong. So here if I want it to be explicit I can say that the return type of this function is void. So this is the equivalent of the PHP thing like this basically. So here this function is returning void meaning nothing. And then I have a macro print ln which is like a function that displays hello world.
And of course if I run cargo here I get to see hello world on my console. So again we are doing a CLA application. So probably the first thing we need to do here is explain the usage to the user. That will be something like usage to-do which is the way I want to use my application and then to-do and then I pass the command and finally a list of these arguments. So obviously if I run cargo I should see this on the console which I do. So what I can do just below is type now the list of commands and the list of commands will be basically something like this commands equals to two commands by the way we are working with two commands today.
One of them is to basically people want to run cargo run add and then the to-do description like to-do one for example and the other one we are going to build together is the list to basically display everything we have added to the to-do list. So let's explain that to the user. This will be the add command which receives a description as an argument and this command adds a to-do. And then just below we have the list command which basically displays all to-dos. If I run this uh with cargo hopefully things are still working as expected which they are.
Something I can do here to make this a little bit more sexy is give some space here. Okay. So I'm going to do that. Oops. Cargo run-q. There we go. And I get to see the usage uh right on my terminal. Now, now that we know a little bit about functions, you're looking at this function main. Something we can do is extract this usage to a new function with a name display help. So, let's do that. Display help returns void. And then the function body would be pretty much a copy paste of the content of my previous main function.
And here I'm going to call display help to get to see the usage in the same way, which I do. Now once we have the usage the the user actually will interact with this with this CLI tool doing something like cargo run and then passing the command which can be add or list. So we need to actually capture the arguments of uh this CLA application to catch it to catch the arguments we can actually use something on rust call it um the ins from the standard library. I want to talk a little bit about that.
So in Rust actually I want to talk about variables before that. I'm sorry. So to catch the the we need to catch the arguments and then the command but we need to store those values somewhere and we usually store that on variables. And let's talk a little bit about variables on rust uh before we actually talk about the standard library. Variables on rust are not actually variables meaning that they don't change the value. They are immutable by default. Uh this is a little bit different from PHP as variables can change. They are actually variables. So on rust when I type something like uh let command equals to add and then I just type below command equals to list on PHP this would work meaning that the value of the command will change however on rust once I try to debug what is within command you will see that rust will start to complain.
So a couple of things here we need to take a look of. The first one is that the rest compiler on the console is telling me that you cannot assign twice to an immutable variable. Meaning that I have assigned the value add here but then after I have tried to assign a different value to the variable command and that's will not work in rust. So the variables are immutable by default. Now thanks to this great developer experience on the console we can we get to see here we can see that Russ is telling me that consider to making this binding mutable which is uh which can be done by the mute keyword meaning that if I come here and I just type let mute key uh command I now get to see on the terminal the value um of my command which is list right there on the bottom.
However, Rust is very helpful again on the console. is really just telling me, oh, you're just assigning like command to add and then you are overwriting just below. Doesn't make any sense. Therefore, I'm going to tell you a warning here. However, my my program compiles and that's thanks to the moot keyword which allows me to mutate variables. So again, variables are immutable by default on Rust. And now that we know how to actually create variables, we can start to collect the arguments coming from the CLI and be explicit about this um this variable. I'm going to say that the arcs is a vector of strings.
This is the equivalent the vector in Rust is the equivalent of the array in PHP. However, we can say what what is within the vector which is um uh values which are strings within the arcs are a vector of strings and this vector of strings will be something from the standard library. Call it standard library in give me the arcs from the M standard library. So I want to talk a little bit about this. The standard library in Rust is pretty much some common helpers that are commonly used within Rust projects. Even on every language actually we have this is pretty much helpers for people building applications.
On PHP we have like the global env and the global uh server variable. We have a bunch of array functions. We have a datetime library as well which uh pretty much allows you to build PHP applications with those helpers. Okay. In Russ we have this standard library which gives me access to specific modules. One of them is the in module which we can grab the arguments from this. So obviously if I try to de debug this. So I'm going to do that. Hopefully I have all the arguments on my console. Um I have a mistake here.
I can import this. This is like this. Uh did what? Uh vector of strings. What I had to do with this? What the What the hell is happening? Give me a sec. That's why I have created tdu. Huh? Oh, I need to call the collect. Of course. I'm sorry about this, guys. That's why you have the backup. Huh? Just in case. All right. Now, it's compiling. And on the bottom, I have on the line eight my uh vector of strings which is displaying by binary. However, I can add the command and now I have uh displayed on my terminal, the add command on bottom.
I can pass a description for for example, and I get to see that description on my vector of strings. As you can see, we can use this function on Rust DBG, which is pretty much the equivalent of a via var dump in PHP. And we can see the values that are compiled and runtime. And we can see these values on the terminal. So once we get the arguments, remember we are trying to capture the command. Uh so when I type cargo run, we need to capture this value to then do something with the command. As you can imagine, uh if we have if we have the add command, we need to do some code.
If we have the list, we need to do something else which is displaying all the to-dos. And after if we have something that doesn't make any sense, we need to tell to the user that the command is unknown. So let's do that. Let's capture the command by doing something like let command equals to args get the index number one because remember the command is within the index number one. And one thing cool in rust which you probably would expect to see here is that in PHP we have this thing like is set if the array key exists within the array then we capture the value.
Well on rust there is no nables. Okay. So here I need to tell to Russ the following. Okay unwrap the string within the key number one or else which is otherwise we need to execute this default call back. So I'm going to do that. I'm going to say okay if there is anything on the position one which is the case right here however it's not the case when I don't run any command I want you to get me back that string however otherwise you execute this default call back and when the user doesn't provide the command what we need to do is display the help just like that and then we need to exit the program with a feedback with the exit code I'm going to do that using the process module from Rust again an helper and I'm going to exit with a static code with a status code zero.
And I need to import this module right here on top. So standard library process. Okay. So let's see how this is going. Let's debug here as well the command to see if we can capture the add or the list. So I'm going to cargo run-q and I get to see the help meaning that we are entering on this default call back. The key number one does not exist. However, if I run cargo run-q, I get to see the ad on the console and I get to see a list. Now, one cool thing, and you probably get this same feeling with PHP standard level max, is that if the program is compiling, meaning it's kind of working as expected.
And that's a very cool feeling with working with compiler languages such as Rust is the feeling that if it's compiling, it's kind of working unless you make a typo somewhere. Um, which is a very good feeling when working with Rust. Again at this point on the command variable we have the add value. We have the list value. However, we also have something that if the user types like uh which is nothing at all. We have the command with this value which doesn't actually represents anything. We don't know what to do with it. So we need some control flow to do something when is at to do something when is list and to do something when the value doesn't make any sense.
And for that we can use the match expression. The rest have pretty much the same stuff as PHP. So we have if conditions, else conditions, we have the match statement which is exactly what we are going to use. And the match can be done with something like this. Okay, let the exit code be the result of the following match expression. If the command a string is equal to the value add, I want you to do something within this this block of code. If the command is list, I want you to do something else. However, if the command is something that I don't know, meaning I want to enter on this default scope, I want to do something else.
And finally, now that we have an integer of 32 bits on the exit code, what we can do here is simply exit with that exit code. And of course, we need to get an exit code from this mass match expression. So, we need to type some code here actually. So I'm going to type print new line with adding a to-do and return back the integer of 32 bits uh zero. Now one very cool thing on Rust is that if you have something like this meaning that you have a return plus a semicolon there is the synatic sugar which is you remove the semicolon and then you can remove the keyword which is pretty cool.
So some synthetic sugar. So if you have this this is the equivalent of having this. So we just hide the semicolon, hide the return statement, which is in my opinion pretty cool. I used to see more of these things in PHP to be honest. Uh then we can do the same stuff on the list command instead of adding a to-do, we are obviously uh displaying all to-dos and returning zero at the same time. And finally, of course, if we enter on a default statement, meaning that we don't have add or list, we need to say to the user unknown command and you need to back off with the exit status uh one, which is an error.
So, let's see how my program is doing. I'm going to type car-q displays the help makes sense. dash q add which heading a to-do and then list to display all to-dos and then and I get to see unknown command. So uh so far it's working as expected except doing the proper job of storing to-dos and displaying all the to-dos but uh I think it's working right. Before we actually talk a little bit about the concrete code that will store those to-dos in the persistent layer, let's actually refactor this a little bit because at this point my main file have 42 lines and for the same reason you don't store your code on the index.php.
You probably organize your code on PHP and within folders within classes in Rust is the same stuff. We basically uh refactor all our code when we have to. So looking at this um at this application on PHP, how would I would tackle this problem? I would probably use Symfony console for this and I would probably have a separate class with a name add command to actually handle the process of uh uh storing a to-do and then I would have a list command to actually display all the to-dos uh on my terminal. So let's do that on Rust or at least try to do the same thing.
So what I'm going to do here is that on my source directory I'm going to create a new file. That file will have the name commands rs function. On this file I'm going to store the code of my add command and I'm going to store just below the code of my list command. Again we don't have classes on Rust but we have something that they say it's not similar but it's kind of it is. So we can do kind of the same thing with something called it a strruct. So here I'm going to I'm going to create something called it strruct f command and within this strct I can have properties similar to classes on PHP.
So here I could have something like name equals to a string and then I could have for example a age equals to an integer of 32 bits. There's a lot of data types on rust like integer of 32 bits unsigned integers which are positive um integers. However today we are going not not to talk about that anyh too much. So I want to my add command did not have any properties for now. However, I need to have at least one method to handle the process of storing the to-do on the persistent layer. So let's add actually some methods to this strruct.
The way we can do that on uh rust is not within the strct definition. It's just on the scope below. So what I can do here is something like implement f command and then I'm going to put all the methods within this implement f command scope and I'm going to do is create first of all my constructor. The constructor convention on rust is using this new uh function name. So new function returns a self which is the structure basically pretty much the self on PHP as well. And then within this self uh within this function uh new scope I'm going to return the f command without any properties.
However, remember remember this when you have a semicolon we can remove the semicolon remove the return keyword and make this code a little bit more simpler. So this is the equivalent of a construct in PHP. And just below we need a method for the strct which will actually handle the process of uh adding a to-do uh to a persistent layer. So let's do that. I'm going to type function handle and then I don't want to actually create a static method. I want to this method to be uh an instance method of my strct. So I'm going to pass here self.
Okay, this is the equivalent of this in PHP. Okay, so the handle will return back an integer of 32 bits. Remember we need this back to our match statement to the exit code. So this endle we return an integer of 32 bits and within this um scope we are basically uh printing out what we had before within our match uh statement. So I'm going to copy this had to within my um handle method and just save this. So to re to let it very clear if we pass the self within the arguments meaning that this function is non-static is a instance method of the strct.
If I don't if I don't pass self this method becomes static similar to uh PHP. So in this case I want this to be an instance method. So I'm going to pass self. And finally let's actually use this strct aka class in PHP within our uh main match uh statement. So I can remove all of this thing and simply type fth command new. So I can call this method statically because I'm not passing any uh self argument here. However, the handle method is non-static and that's because I'm passing the self here. So add command new handle and that should pretty much return back an exit code.
Let's try to compile our application to see how this is uh doing. So I'm going to clear my terminal, run cargo, and let's use uh the terminal feedback to actually proceed with this. So cargo is telling me on the right that the f command was not found within the scope of the main function. So meaning that the strruct was not imported. So let's do that in the similar way we do in PHP. We just go to the top. One difference on R is that I need to declare the module commands. Okay, so in my main rs, I need to declare every single file that was is within my source directory and then I can import the strct by doing use commands add command.
So I've imported. Let's run cargo once more and just follow the feedback. Now cargo is telling me that the add command that you just have imported uh within our main rs file is a private strruct. Another very cool thing about Rust is that strcts are private by default. Meaning that when you create this strct at command, it's private to the commands file and you cannot use it elsewhere unless you use this keyword pub and you make this strct public. I'm going to clear uh my terminal run cargo once again and let's see how it goes.
Next, the terminal is telling me that you are calling the new and the handle committed. However, both are private. Okay, so functions implemented on strcts are also private by default. Meaning that I need to come to this function new and actually make the function public to be able to use it outside of my file. Cargo run, we get to see the display help, meaning that I'm pretty sure that my program is working as expected. So if I target car run add I get to see adding a to-do which is the result of my handle uh function people are everyone is enjoying or not huh cool okay that's cool let's move forward here uh so we need to do the same thing to the to the list command let's do that because right now we don't have a list command yet meaning that the code get getting executed is uh within this uh match statement so let's do the same behavior we have done with the f command so I have this code right here.
I'm going to simply copy paste it like a good old days. Bam. Put here on the bottom and then rename some stuff here obviously. So this will be the list command which returns a new uh list command. And finally the handle method instead of adding a to-do will display or displaying all to-dos. Okay. So hopefully uh let's run cargo. Actually let's use this strct within my uh match statement. So this will be list command. Give me a new instance of that and then handle the job. So if I run cargo, let's see what it tells us.
It's telling me of course that the list command is undeclared. Meaning we need to import this strct within the main RS. Now one cool thing and I wish to see that in PHP as well is that when when you see that you are importing everything from the same file, something you can do is this thing right here. Bam. and just imports everything from that file. Uh it's kind of a wild card that basically imports and make sure that everything on that file commands is exposable within the main rs function. Nice synthetic sugar again. So this is pretty cool.
I'm going to clear my terminal run cargo. I get to see adding a to-do. So I'm kind of sure that the list is also working as expected again when rust compiles unless you have made like to-do in English which is difficult with go copilot these days. um meaning that your program is kind of working as expected. All right, cool. Let's talk about interfaces because I mean looking at these two commands, we have the f command and we have the list command. Something that is kind of similar here is this handle method meaning that both these strcts implement the same handle method in the same way meaning without arguments and returning back the exit code.
So something we can do is just say okay both the list command and the f command and maybe other command in the future needs to at least implement the command interface to make it all of them look similar. So let's talk about interfaces on on rust and this actually will be fun. So I'm going to go to my commands rs module and I'm going to declare here on top my um command interface. Now one difference though on Rust is that you actually declare interface with a word trait. Okay. All right. Calm down. Huh. So on on PHP you have this thing called it trait which allows you to actually share methods between between classes.
People are not actually sure about it like why people are still using it. However on Rust in order to declare an interface use the keyword trait. Okay. So in this this is exactly the same stuff as typing here like in PHP interface command. Okay. The difference is that similar to PHP we can specify what this interface is actually uh forcing the definition of which is a handle method which receives self meaning non-static and then returns back an integer of 32 bits. This is my interface definition. However, on Rust you can have default implementations meaning that you can provide okay the strct who implements this trait will have this method that works this way.
So you can provide a default concrete implementation for the strict implementing this interface. For now I'm going to do similar to PHP meaning that I don't want to provide a default implementation meaning that is up to the strct to uh define the concrete implementation of the handle method. So once we have the interface traits and rust, we can copy the name and you probably would expect to do something like implement command. It doesn't work that way. Okay, so I'm going to remove it here and I'm going to go just on the bottom of the implement f command.
I'm going to say the following. The implementation of command for f command is the following just below. So it's not within the strct definition is not within this first implement f command is just below implementation of command for f command is the one within this uh this uh two brackets and then we need to put all the methods defined by that interface within that scope definition. So I'm going to copy this function handle all of it going to remove it here from the methods without any interface and I'm going to copy just below right here.
Okay. Now one cool thing which I also wish to see in PHP is that usually when you have an interface you are kind of defining the public API of a specific class. Nobody creates an interface with private methods. Right? So here what in Rust if you are defining an an interface or uh the concrete methods of an interface to a strct all the methods are just public by default. So meaning that you can remove this pub keyword. Okay, which is pretty cool. So hopefully things are still working as expected or not. Let's see. Cargo run.
Oh, it's complaining. And now it's complaining about the strct f command does not have a handle method. And why? because uh interfaces or traits in Rust are actually uh private by default, meaning I need to make this interface public to be used outside of the scope of the commands. Let's run cargo once more and hopefully it's working as expected. It's not working as expected. That's because I need to save it, I guess. Let's make let's see. Of course, I need to save it. There we go. It's now uh running and compiling. Uh which is pretty cool.
We can do the same thing for the list command. Let's do that. So here on the list command, I can just do right here implementation of command for list command. It's the one that we had on the method handle. Remove it from here on the top. Put it within the interface definition. Removing the pub keyword again doesn't make any sense. It's public API. Let's run cargo. And of course is working as expected, which is cool. Okay, we got to see variables, functions, interfaces, strrus, data types, all this craziness. Let's actually start coding the code responsible to persist the to-do and then uh display all the to-dos.
And I wanted to be mindful of you really just need to to do Rust, you really just need to know this concepts of cargo, how to run cargo, how to install it, and then how to create strct. This is not the only thing that you actually need to do, okay? When it needs come to really concrete and complicated code, go copilot will basically do everything for you. Okay? So, do not memorize everything I'm about to show you. Okay? So, the hath command uh which is right here, we want to actually start to persisting this to-dos.
Okay? So, to persist a to-do, the first thing we need to do is grab the description of that to-do. Right? People are going to run this command on the following way. uh cargo run add my to-do description one for example. So I need to provide this description of the to-do. So the first thing we need to do is grab the arguments and I can do that typing let arcs actually no I need to provide those arguments on the arguments because remember we have already the arguments in our main function right here. So we need to provide this thing to the add command strruct and the way I'm going to do that is actually uh by declaring a new property of my strct.
This strct will have a property with a name arcs which is a vector of strings and then on my constructor which is the new I'm going to actually give uh to my strct those arguments. So this will be equally a vector of strings. And finally I need to pass this argument into the property of my strct. So I'm going to do something like this. Now again some synthetic sugar of rust is that if the property name is equal to the given argument you can simply do this ben and it's exactly the same thing. Okay. So having this or this is equivalent.
So I can just remove this and give uh to my uh add command the arguments and let's debug those uh self arcs. So self again is the equivalent of this. Let's debug those arcs to see if we have something on our handle method. Okay. Uh rust is complaining is telling me the following. When you create a new instance of f command and that's done on the main rs function which is right here. you are not providing a vector of strings meaning that we are missing the arguments um uh argument. So let's do that provide here there we go and run cargo and we now have the the list of arguments within our uh command handle method meaning that we we can grab the description of the to-do.
So the description I can see that if I type my to-do one I get to see that on the index number two of my vector. So we need to type something like this. Let description equals to uh self arcs get the index number two. Now remember a description may or may not be there because if the user provides cargo run ad without the description we may not actually have uh an index number two. Meaning that at this point we don't have null. we have instead a description option which we could unwrap or else as we have seen on the main function.
However, some this time I'm going to do something different. What I'm going to do is just say this if you have some description and that's may come from the description option. I'm going to make this a little bit bigger so everyone can follow. There you go. If you have some description coming from the description option, I want to execute this code right here. And for that I'm going to simply debug the description itself and then return back probably zero because we are going to add the to-do. Otherwise I want to say to the user that description is required and return back the exit status code number one.
Okay. So let's recap. We are grabbing the description which is an option may or may not be there on the index number two. Then if we have a description, we are going to output that description. Or else, of course, same to the user, description is required. So I'm going to run I'm going to clear my terminal here. Make this a little bit bigger. And I'm going to type cargo run at without any description at all. And it's telling me description is required. And I get to see this red thing, meaning that the exit code was one.
And if I type cargo run add to-do 1, I get to see exactly that on my terminal, meaning that I'm entering on this if statement right here. Now, at this point, we have a description, which is a string, and we can store it uh somewhere on my uh computer. So, what I'm going to do to follow a very naive implementation is that within my root directory, I'm going to create a file with the name storage.txt. Okay. So within this file we are going to put all the to-dos people are are using our CLI tool. So then what I can do is use a module from Rust called it file system.
So here on the top I'm going to scroll here. I'm going to import the module from Rust from the standard library. Again very common all languages have something like this. And I'm going to import the file system uh module from Rust. Then just below what I'm going to do is and again you need no need to memorize all of this is type something like this. Okay, let the mutable file to be equals to uh in um file system open options new and I want you to open that file for writing writing on append mode and finally the file name is the one with the name storage.txt.
If something goes off rails, which it shouldn't go off rails, but if something goes off rails, I want you to type file not found. Okay, so at this point we are opening this file for writing. And what we can do is simply type now. Okay, write on that file the contents of my description. And this will basically increment every description of the to-dos on bottom. So let's run cargo to see how it goes. I need the exclamation point here. There we go. Cargo run add to-do number one. I get to see um an error saying that um method not found uh write in the new line.
Okay. Uh description. H give me a sec. No, print will print out. Huh? In this case, I just really want to uh write on the terminal. You know what? I'm going to do something different. Okay, this should work. Otherwise, I have the backup. Huh? Okay, I'm going to see the backup real quick. Huh? Yeah, of course. Okay, so what we what we're going to do again? Yeah, of course. On the write ln function, I actually need to pass the file as a first argument. So, again, on this case, what I'm going to do is say, okay, write me on this file the contents of this description.
And here, I can simplify this a little bit by doing this. Write the contents of this description. However, if something goes off rails, we can expect the file to not be writable. Okay, once I do this, I can give feedback to my user. So, printing and a new line. So, now to the console to-do added. Let's run this on cargo to see how it goes. So, again, without anything, it will complain. Oh, it's actually still not complaining uh compiling. And this is because in order to use this function with the name write ln, I actually need to import this on top.
So, let's do that. clear my terminal, run cargo again without a description. I'm supposed to enter on the else condition just below and I am. So description is required. However, if I type cargo to-do um add and then to-do number one, for example, I get to see to-do added and that it's pretty much sure on my um not this one. There we go. To-do number one. And if I do this with for example uh to-do number two, I get to see that as well on my file which I think is pretty cool. Okay. Uh so now that we are we are able to add to-dos to this file.
The last step is really displaying all the to-dos when we are typing cargo run list. So the way we can do that is simply by modifying the code of the handle method within the list command. And that can be done very simple by doing something like this. Let the contents to be equals to file system read to string the contents within the However, if something goes off rails, I want you to type file not found. And once we have the contents, we can use the very old print new line. This is the equivalent of um print in in PHP.
And I can do print out the contents of that file to simply have a nave approach of uh displaying all the to-dos. So again, cargo run add to-do number three for example to-do added. Pretty cool. And I can now run cargo run list to get to see all the to-dos on my terminal. Again, a very naive implementation, but get the job done. And we just have saw how to create a CLI app on Rust, which I think is pretty cool. All right, I hope everyone is excited about Rust at least in the way I am.
Uh I want to show you one more thing before we close this off. I want to show show you testing. Okay, now testing on Rust works a little bit different from what you would expect. In PHP, we have to import or require a PHP unit or past and then we need to create a folder with the name tests and then a separate file for each test case. So it's a lot of writing. Uh and Rust is a little bit different. So in order to have tests for example for the commands module you actually write those tests on the bottom of the file.
That's correct. So on the same file that you have your source code just below you will have your tests. So what we are going to do here is just have a separate section for our tests and then I'm going to type this test module code. So basically telling to rest that okay all the code below is related to tests. So within my module test, I want to import everything on this file that can be done with a super and then the wild card um word and then we have this annotation to specify that within this thing is a test.
Now we can have two tests just to showcase you how testing works on rust. So what I want to do here is simply do um add command for example to have a test and remember for the add command we need to provide create a new strct with a name uh command for example and then we need to pass the arguments to that strruct f command. So I'm going to first of all create an array of arguments that will be a vector that contains within the binary on the first position remember. So this will be a to-do to string.
Then we have the command name which is add to string as well. And then we have the to-do description which is something like my to-do number four. Okay. So once we have the arguments we are kind of preparing our test. We can create a new strct of the add command that will be command equals to uh add command and call the method static method new which is the construct within this new I can pass my arguments and I have successfully prepared my test. I can now act just on bottom that can be done by simply storing the exit code as a result of the command endle.
So kind of acting to then assert right. So on bottom what I can do is just call the section assert and just say now this is kind of similar to PHP though I can type assert equal and then provide what I'm trying to assert which is the exit code to be zero. Okay, so let's see. Let's run this thing to see how it goes. And also different from PHP is that you would expect to run like vendor bin PHP unit or vendor bin past. And in this case, you just type cargo test, which is a kind of built-in within cargo.
So if I run cargo test, you see a lot of stuff on the terminal. Again, to avoid this, you can simply clear the terminal. Cargo test- Q just hides all the compilation steps because you don't need to see them. And I get to see that my test result is okay. Meaning that I get an exit code zero from my handle method. And if I would for example pass one here, obviously my test would fail with the exit code number one. So let's run that caron-q. And I get this feedback on my console that the left value is one.
However, the right one is zero. Meaning that my uh test is failing. So I can put zero to make this test pass. And then just below I can do the same stuff for the list command which I'm going to do. Okay, I'm going just copy paste and then call this list command to prepare my list command. I don't need any arguments at all. So I'm going to just create an argument without uh those arguments. I'm just listing the stuff. Then I'm going to call the handle method get back my exit code and insert the exit code as zero.
Let's run cargo test- Q. And I get to see that both of my tests are passing and the test result is okay. And this is pretty much what I had to show you. Um, a couple of small notes. Uh, today we just have saw a very naive implementation of Rust as a CLA application. Of course, Rusty's use cases are way more uh diverse diverse than this. Uh, here for example, I have my website, my blog actually written on Rust. So if you want to see a little bit what the Rust code looks like in production, just go to my GitHub.
nunodu.com is the name of the project and you get to see some of the interesting and spicy stuff on Rust such as a synchronous multi-threading. So very cool and safe stuff. Uh on my GitHub you will find a couple of Rust projects including a CLI framework actually. So this project is hold on github.com um nunumaduro/nunaduro. Um this was rust for PHP developers. I hope everyone have fun watching me coding and making mistakes which is pretty cool. Feel free to follow me on my socials. I have Twitter, Mastadon, LinkedIn, YouTube and just uh if you have taken pictures don't hesitate on putting them in social media.
I like to see them. Okay. Thank you so much. Yeah. Thank you Nu. This this was really fascinating. Anyone any questions? So, uh I was wondering like do you have any tips or um examples of where we can go to learn once we've mastered what you've already shown. Um yes. Um actually I'm going to tell you the way I've learned Rust which might uh it might work for you. I hope so. So the way it worked for me is that I started to learn by the Rust book which is the name of the the official documentation of Rust.
to be really honest they write very simple words even for a non-native English like me and I felt that I really learned it just from from the Rust book and then also that something helped me a lot is with GitHub copilot you get so much auto completion when you are like uh um when you are facing problems and you don't know how to solve them you just actually ask GitHub copilot will just write the code for you even if it's not like the very correct one you at least you are uh you are able to learn from there so yeah rust book is pretty much my way to go when you when running Rust.
However, play with it and play with go GitHub copilot as well. It will basically uh get you on the rails. I I feel like you're welcome. Um very quick one is I noticed you had self in upper capital S and lower case. Yeah. Is it case insensitive as a language? Uh no. Meaning that you would well actually I need to double check that. But in the case of the self, so when you are returning back, I'm going to actually go there real quick. Uh you were talking obviously about this self right here. So when you are return here, I could do two two things.
I could do for example list command just basically specifying the strct I'm returning on. But you can basically use this syntactic sugar and say that you want to return self and this needs to be uppercase. However, when you're using the self argument which is the this on PHP, you need to be lower case here. Okay. and and then sec what what sort of is the the infrastructure behind it frameworks and all that sort of thing how does it compare to PHP um that's a good question so I can tell you from experience again I'm not an expert on rust but from experience uh in order to serve in order to serve a PHP application on the digital ocean droplet for example I would need to put it behind engin all these different tools that basically I kind of you the default configurations because I don't know how to configure them.
However, on Rust, I simply had to proxy and jinx to my server on Rust and that was basically working. So, I had that on Ubuntu, a very simple server serving my Rust application um web application and then I just had Ninkx proxying all the requests to that server and that's it. So, no PHP FPM equivalent for us. All right. Anyone else? Um, I think I saw you tweet once that you were looking and experimenting with other languages to find some new IDs for PHP and for your packages and stuff. I was wondering if there are any things some things you found in Rust that you want to implement in.
Yeah, actually yeah, excellent question by the way. So something I have actually ported to Lavel itself. So in Laravel we have this this PHP code where you can actually bar dump values. So you can use something like DD value and then have the console outputting the the string value. So something we didn't had and you can find it on Rust. I don't know if you have noticed but when I I was executing my Rust code you get to see this um uh where is it? Well, you get to see next to the debug value the origin of the dd basically, you know.
So when you're using on rust something like uh this um uh this function like dbd and then you put actually the command inside on rust you see the value of the command and also the origin of the of the value the file where it's coming from. On PHP we didn't have that at least on DD. So now we actually have ported this thing and when you dive our dump some value with the DD function on PHP you get to see also the file on the console. So that's something I actually was able to port to PHP which is pretty cool.
All right the last question before was cake time. See thank you that was very good demo. Um, I have a question about the add command where you use if else statement and I was wondering could you use unwrap or else like you used in main. RS. A good question. Yes, I could use the unwrap or else. However, on that case I want to showcase a little bit about the the the if synthetic sugar here which is okay. If you have something create a variable with it that's why I've kind of showcased this way. So you always get back an option when you are using something that may or may not be null.
So in that case you can do something like this which is creating a variable out of an if condition. However you could also do as you have mentioned unwrap or else which is more like okay unwrap the string otherwise execute me this default call back. Uh so I wanted to showcase both that's why I've done it differently. Good question. Thank you Noo. And thank you. Let's have another round of applause.
More from nunomaduro
Get daily recaps from
nunomaduro
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.









