Using struct to structure data
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Lesson Transcript
[00:00 - 00:08] All right, so in this lesson, we're going to implement another basic Unix utility. This time we're going to do LS, which is mostly just a shell command for looking cool in front of non-technical people.
[00:09 - 00:16] But occasionally it's also used for listing the contents of a directory. So in this lesson, we'll implement a simple version of LS, which we'll call mini LS.
[00:17 - 00:28] And so mini LS will be executable from the command line, just like mini WC was. It will also take a directory as its argument, but default to the current directory, if no argument is passed in.
[00:29 - 00:40] And finally, it'll print a list of files and directories that exist in the given target directory. So in order to tackle these requirements, we'll introduce a couple of new REST concepts.
[00:41 - 00:48] We'll take a look at structs, which can be used to create structured data and Rust. And then in the next lesson, we'll figure out how to add functionality to our structs with implementations.
[00:49 - 01:01] All right, to get started, setting up mini LS will be very similar to our mini WC program. So we'll go ahead and create a new mini LS project here with cargo new mini LS.
[01:02 - 01:11] And then we'll cd into that directory and open up our project in VS code. So the first part of mini LS is going to look very similar to mini WC.
[01:12 - 01:29] We're going to go ahead and use the standard environment or standard ENV module so that we have that ARGS method available to us for pulling in ARGS from the command line. And then we'll just go ahead and pretty much copy that same logic.
[01:30 - 01:41] So we'll go ahead and do what dir name, because that'll be the argument that's passed in by the user. So we'll go ahead and match on the ENV ARGS function and chain and on there.
[01:42 - 01:53] And remember, we need to take the argument at index one, index zero is the name of the program. And then we'll go ahead and open up our body here.
[01:54 - 02:15] And so in the some case, we'll want to take the input like we did before and just return that out. But in the none case, this time instead of just panicking on the none value, if the user doesn't pass in an argument, we'll actually want to use this none case to default to the current directory.
[02:16 - 02:31] So to do that, we'll need to return something like this dot forward slash to signify the current directory. So actually going to use this string helper method here and create a new string from that string slice.
[02:32 - 02:37] So this might seem a little weird. And it's important to note that strings and rust aren't as straightforward as strings and JavaScript.
[02:38 - 02:46] And that's mostly because there's no runtime or garbage collector and rust. So we have to do a little bit of extra work when dealing with things like strings that live on the heap.
[02:47 - 03:02] So we don't normally think about this in JavaScript since everything is a reference and lives on the heap. But for now string and this other string type, which is the reference to a string slice, which looks like that ampersand STR, will be the two string types that we use the most.
[03:03 - 03:14] And we'll dive deeper into the distinction between them later. But for now, you can think of capital S string, like what we have right here as roughly the equivalent to the string type in JavaScript.
[03:15 - 03:27] So let's go ahead and also abstract this logic out of the main function and into its own function. To create a new function in rust, we can use the FN keyword followed by the function name.
[03:28 - 03:35] And so we'll call this parse_der_name because that's what we're doing here. And then you would take a list of arguments between the parentheses.
[03:36 - 03:48] In this case, we're not going to take any arguments because we're going to use the output of the ENV_args function to get our list of arguments. And we also need to go ahead and specify a return type.
[03:49 - 03:56] And this function will return a string when we call it. So we have to use the return syntax here followed by a string.
[03:57 - 04:08] So what this is doing is telling the rust compiler that this function is going to return some value that is of the type string. And if it doesn't, it will be an error at compile time.
[04:09 - 04:13] So our program will never even run. And so you can see that we have a red squiggly here.
[04:14 - 04:24] And that's because we're not returning a string from this function yet. So if we go ahead and take our match logic here, we'll go ahead and just paste this in for now.
[04:25 - 04:32] And remember that match can be used as an expression. So we can just return and we can make this a little more explicit with the return statement here.
[04:33 - 04:37] We just return the output from this match. And that satisfies our string return type.
[04:38 - 04:44] And you can see that our errors is now gone. And Rust, it's a little bit more idiomatic to leave off return.
[04:45 - 04:58] And so by default, the last expression in a Rust function will be returned from that function. And so typically, you only use the explicit return syntax when you're doing something like an early return from a function.
[04:59 - 05:15] So with that parse_dername function in place, we can go ahead and set the output of that to our surname variable. And so the next step is we need to have some way of representing the entries in the given directory that the user has passed in here.
[05:16 - 05:24] And so for that, we will need to create a structured data type. So to create a structured data type in Rust, we'll need to use a struct.
[05:25 - 05:32] And so structs in Rust are kind of sort of like objects in JavaScript. They let you structure data in a way that makes sense for your program.
[05:33 - 05:40] And so we'll go ahead and do that with the struct. And then we'll call our struct entry because that's what we'll be defining here .
[05:41 - 05:50] And so then you define your properties inside of curly braces. And so we'll go ahead and just have one property on our struct here.
[05:51 - 05:54] And we'll call that property path. We're defining the struct here.
[05:55 - 06:00] This is not struct literal syntax. And so we need to give path a explicit type.
[06:01 - 06:09] And so that's going to be a string in this case. And so note that this is not, we're not saying that path has the value string.
[06:10 - 06:15] What we're saying here is that path is of type string. So in JavaScript, this syntax would mean something a little bit different.
[06:16 - 06:23] It would mean that we're trying to set path to whatever the string variable is. In Rust, what we're saying is that path is of type string.
[06:24 - 06:36] And so this syntax will look familiar if you've done any TypeScript programming . But if not, just keep in mind that this colon syntax followed by a type is how we do literal type annotation in Rust.
[06:37 - 06:45] And you'll see this syntax pretty often as we move forward. So you can kind of think of structs as the equivalent to one half of an ES6 class.
[06:46 - 06:52] But note that structs can't include functionality like methods or associated functions on their own. So we'll get to that in a bit when we talk about implementations.
[06:53 - 07:00] But for now, we'll just stick with this path property on its own. So if we wanted to use our new struct, now that we've defined it with this struct syntax.
[07:01 - 07:22] And so if we wanted to use our entry struct at this point, we would use what's called the struct literal syntax. And so we can go ahead and take a look what that looks like by creating just a dummy entry here, and setting that equal to the name of our struct entry, entry, followed by the defined values for each of the properties.
[07:23 - 07:31] In this case, we just need to define string. So we can go ahead and do that same string from and pass it a string slice.
[07:32 - 07:38] And that will give us a variable that's bound to a new instance of our entry. Cool.
[07:39 - 07:48] And we can go ahead and do just a sanity check here and run cargo run. And cool, it doesn't do anything yet, but it does compile successfully.
[07:49 - 07:55] So in this lesson, we implemented a reusable function to parse the argument passed in by our users. And we also saw how to create structured data with structs.
[07:56 - 08:00] In the next lesson, we'll solve the next piece of the puzzle and figure out how to add some functionality to our new entry struct.
This lesson preview is part of the Rust For JavaScript Developers course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Rust For JavaScript Developers, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
![Thumbnail for the \newline course Rust For JavaScript Developers](/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fassets.fullstack.io%2Fn%2F20201030012350278_hero-rust-for-javascript.png&w=3840&q=75)
All right, so in this lesson, we're going to implement another basic Unix utility. This time we're going to do LS, which is mostly just a shell command for looking cool in front of non-technical people. But occasionally it's also used for listing the contents of a directory. So in this lesson, we'll implement a simple version of LS, which we'll call mini LS. And so mini LS will be executable from the command line, just like mini WC was. It will also take a directory as its argument, but default to the current directory, if no argument is passed in. And finally, it'll print a list of files and directories that exist in the given target directory. So in order to tackle these requirements, we'll introduce a couple of new REST concepts. We'll take a look at structs, which can be used to create structured data and Rust. And then in the next lesson, we'll figure out how to add functionality to our structs with implementations. All right, to get started, setting up mini LS will be very similar to our mini WC program. So we'll go ahead and create a new mini LS project here with cargo new mini LS. And then we'll cd into that directory and open up our project in VS code. So the first part of mini LS is going to look very similar to mini WC. We're going to go ahead and use the standard environment or standard ENV module so that we have that ARGS method available to us for pulling in ARGS from the command line. And then we'll just go ahead and pretty much copy that same logic. So we'll go ahead and do what dir name, because that'll be the argument that's passed in by the user. So we'll go ahead and match on the ENV ARGS function and chain and on there. And remember, we need to take the argument at index one, index zero is the name of the program. And then we'll go ahead and open up our body here. And so in the some case, we'll want to take the input like we did before and just return that out. But in the none case, this time instead of just panicking on the none value, if the user doesn't pass in an argument, we'll actually want to use this none case to default to the current directory. So to do that, we'll need to return something like this dot forward slash to signify the current directory. So actually going to use this string helper method here and create a new string from that string slice. So this might seem a little weird. And it's important to note that strings and rust aren't as straightforward as strings and JavaScript. And that's mostly because there's no runtime or garbage collector and rust. So we have to do a little bit of extra work when dealing with things like strings that live on the heap. So we don't normally think about this in JavaScript since everything is a reference and lives on the heap. But for now string and this other string type, which is the reference to a string slice, which looks like that ampersand STR, will be the two string types that we use the most. And we'll dive deeper into the distinction between them later. But for now, you can think of capital S string, like what we have right here as roughly the equivalent to the string type in JavaScript. So let's go ahead and also abstract this logic out of the main function and into its own function. To create a new function in rust, we can use the FN keyword followed by the function name. And so we'll call this parse_der_name because that's what we're doing here. And then you would take a list of arguments between the parentheses. In this case, we're not going to take any arguments because we're going to use the output of the ENV_args function to get our list of arguments. And we also need to go ahead and specify a return type. And this function will return a string when we call it. So we have to use the return syntax here followed by a string. So what this is doing is telling the rust compiler that this function is going to return some value that is of the type string. And if it doesn't, it will be an error at compile time. So our program will never even run. And so you can see that we have a red squiggly here. And that's because we're not returning a string from this function yet. So if we go ahead and take our match logic here, we'll go ahead and just paste this in for now. And remember that match can be used as an expression. So we can just return and we can make this a little more explicit with the return statement here. We just return the output from this match. And that satisfies our string return type. And you can see that our errors is now gone. And Rust, it's a little bit more idiomatic to leave off return. And so by default, the last expression in a Rust function will be returned from that function. And so typically, you only use the explicit return syntax when you're doing something like an early return from a function. So with that parse_dername function in place, we can go ahead and set the output of that to our surname variable. And so the next step is we need to have some way of representing the entries in the given directory that the user has passed in here. And so for that, we will need to create a structured data type. So to create a structured data type in Rust, we'll need to use a struct. And so structs in Rust are kind of sort of like objects in JavaScript. They let you structure data in a way that makes sense for your program. And so we'll go ahead and do that with the struct. And then we'll call our struct entry because that's what we'll be defining here . And so then you define your properties inside of curly braces. And so we'll go ahead and just have one property on our struct here. And we'll call that property path. We're defining the struct here. This is not struct literal syntax. And so we need to give path a explicit type. And so that's going to be a string in this case. And so note that this is not, we're not saying that path has the value string. What we're saying here is that path is of type string. So in JavaScript, this syntax would mean something a little bit different. It would mean that we're trying to set path to whatever the string variable is. In Rust, what we're saying is that path is of type string. And so this syntax will look familiar if you've done any TypeScript programming . But if not, just keep in mind that this colon syntax followed by a type is how we do literal type annotation in Rust. And you'll see this syntax pretty often as we move forward. So you can kind of think of structs as the equivalent to one half of an ES6 class. But note that structs can't include functionality like methods or associated functions on their own. So we'll get to that in a bit when we talk about implementations. But for now, we'll just stick with this path property on its own. So if we wanted to use our new struct, now that we've defined it with this struct syntax. And so if we wanted to use our entry struct at this point, we would use what's called the struct literal syntax. And so we can go ahead and take a look what that looks like by creating just a dummy entry here, and setting that equal to the name of our struct entry, entry, followed by the defined values for each of the properties. In this case, we just need to define string. So we can go ahead and do that same string from and pass it a string slice. And that will give us a variable that's bound to a new instance of our entry. Cool. And we can go ahead and do just a sanity check here and run cargo run. And cool, it doesn't do anything yet, but it does compile successfully. So in this lesson, we implemented a reusable function to parse the argument passed in by our users. And we also saw how to create structured data with structs. In the next lesson, we'll solve the next piece of the puzzle and figure out how to add some functionality to our new entry struct. [BLANK_AUDIO]