Before we get started, let's make sure of our current code. You should have two files, which are as follows:
Rather than defining a vector of String vectors, let's just create a constant, which is like an immutable variable, and then let the program convert the constant to a vector of String vectors. Let's delete the current vector-based declaration:
pub fn draw_d51() { let d51: Vec<Vec<String>> = get_d51(); for each_inner_vec in d51 { for each_line in &each_inner_vec { println!("{}", each_line); } } } // end of draw_d51()fn get_d51() -> Vec<Vec<String>> { let d51: Vec<Vec<String>> = vec![ vec![ /* Frame-set #0 */ // The next ten lines form a "Vector of Strings". " ==== ________ ___________ ".to_string(), // Each of these ten lines is a String. " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), // Think of each character in a String as a single Pringle's Potato Chip. " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), // Each of these lines is about 48 "Pringle's chips" long. " / | | H | | | | ||_| |_|| ".to_string(), // Now imagine those 48 chips in a Pringle's can. The can is the "String". " | | | H |__--------------------| [___] | ".to_string(), // Now imagine ten Pringle's cans in a cardboard carton. The carton is the "Vector". " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ".to_string(), " |/-=|___|= || || || |_____/~\\___/ ".to_string(), " \\_/ \\O=====O=====O=====O_/ \\_/ ".to_string(), ], vec![ /* Frame-set #1 */ // Now imagine six of those cartons in a shipping container. " ==== ________ ___________ ".to_string(), // This second vector is the second carton in that shipping container. " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ".to_string(), " |/-=|___|=O=====O=====O=====O |_____/~\\___/ ".to_string(), " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ".to_string(), ], vec![ /* Frame-set #2 */ " ==== ________ ___________ ".to_string(), // The shipping container holding all six cartons is the "Vector of Vector of Strings". " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), // This shipping container is labeled "d51" by the "let d51" line above. " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), " / | | H | | | | ||_| |_|| ".to_string(), // The "pub fn get_d51()" line above "returns" (that's the "->" symbol) this vector of " | | | H |__--------------------| [___] | ".to_string(), // vector of Strings, but unlabeled; this function only uses the label "d51" internally. " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), // The portion of the program that calls this function puts its own label on the shipping " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), // container it gets back from this function. It is free to use the same label, or a "__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__ ".to_string(), // different one. " |/-=|___|= || || || |_____/~\\___/ ".to_string(), " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ".to_string(), ], vec![ /* Frame-set #3 */ // This is the fourth ('cause we started counting with zero) vector of Strings in the " ==== ________ ___________ ".to_string(), // shipping container. It can be accessed as "d51[3]", as in, "Put the d51[3] carton " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), // of Pringle's on this shelf, and put the other five cartons, d51[0], d51[1], d51[2], " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), // d51[4], and d51[5] in the storage closet." " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), // This fifth line of the fourth carton can be accessed as"d51[3][4]" (4 because we " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), // start counting with zero). " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~O=====O=====O=====O\\ ____Y___________|__ ".to_string(), " |/-=|___|= || || || |_____/~\\___/ ".to_string(), " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ".to_string(), ], vec![ /* Frame-set #4 */ " ==== ________ ___________ ".to_string(), " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ".to_string(), " |/-=|___|= O=====O=====O=====O|_____/~\\___/ ".to_string(), " \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ ".to_string(), ], vec![ /* Frame-set #5 */ " ==== ________ ___________ ".to_string(), " _D _| |_______/ \\__I_I_____===__|_________| ".to_string(), " |(_)--- | H\\________/ | | =|___ ___| ".to_string(), " / | | H | | | | ||_| |_|| ".to_string(), " | | | H |__--------------------| [___] | ".to_string(), " | ________|___H__/__|_____/[][]~\\_______| | ".to_string(), " |/ | |-----------I_____I [][] [] D |=======|__ ".to_string(), "__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__ ".to_string(), " |/-=|___|= || || || |_____/~\\___/ ".to_string(), " \\_/ \\_O=====O=====O=====O/ \\_/ ".to_string(), ], ]; return d51; } // end of get_d51()
And replace it with a const declaration:
pub fn draw_d51() {let d51: Vec<Vec<String>> = get_d51();for each_inner_vec in d51 { for each_line in &each_inner_vec { println!("{}", each_line); } } } // end of draw_d51()const D51: &str = r" ==== ________ ___________' _D _| |_______/ \__I_I_____===__|_________|' |(_)--- | H\________/ | | =|___ ___| ' / | | H | | | | ||_| |_|| ' | | | H |__--------------------| [___] | ' | ________|___H__/__|_____/[][]~\_______| | ' |/ | |-----------I_____I [][] [] D |=======|__' __/ =| o |=-~~\ /~~\ /~~\ /~~\ ____Y___________|__' |/-=|___|= || || || |_____/~\___/ ' \_/ \O=====O=====O=====O_/ \_/ ' ==== ________ ___________' _D _| |_______/ \__I_I_____===__|_________|' |(_)--- | H\________/ | | =|___ ___| ' / | | H | | | | ||_| |_|| ' | | | H |__--------------------| [___] | ' | ________|___H__/__|_____/[][]~\_______| | ' |/ | |-----------I_____I [][] [] D |=======|__' __/ =| o |=-~~\ /~~\ /~~\ /~~\ ____Y___________|__' |/-=|___|=O=====O=====O=====O |_____/~\___/ ' \_/ \__/ \__/ \__/ \__/ \_/ ' ==== ________ ___________' _D _| |_______/ \__I_I_____===__|_________|' |(_)--- | H\________/ | | =|___ ___| ' / | | H | | | | ||_| |_|| ' | | | H |__--------------------| [___] | ' | ________|___H__/__|_____/[][]~\_______| | ' |/ | |-----------I_____I [][] [] D |=======|__' __/ =| o |=-O=====O=====O=====O \ ____Y___________|__' |/-=|___|= || || || |_____/~\___/ ' \_/ \__/ \__/ \__/ \__/ \_/ ' ==== ________ ___________' _D _| |_______/ \__I_I_____===__|_________|' |(_)--- | H\________/ | | =|___ ___| ' / | | H | | | | ||_| |_|| ' | | | H |__--------------------| [___] | ' | ________|___H__/__|_____/[][]~\_______| | ' |/ | |-----------I_____I [][] [] D |=======|__' __/ =| o |=-~O=====O=====O=====O\ ____Y___________|__' |/-=|___|= || || || |_____/~\___/ ' \_/ \__/ \__/ \__/ \__/ \_/ ' ==== ________ ___________' _D _| |_______/ \__I_I_____===__|_________|' |(_)--- | H\________/ | | =|___ ___| ' / | | H | | | | ||_| |_|| ' | | | H |__--------------------| [___] | ' | ________|___H__/__|_____/[][]~\_______| | ' |/ | |-----------I_____I [][] [] D |=======|__' __/ =| o |=-~~\ /~~\ /~~\ /~~\ ____Y___________|__' |/-=|___|= O=====O=====O=====O|_____/~\___/ ' \_/ \__/ \__/ \__/ \__/ \_/ ' ==== ________ ___________' _D _| |_______/ \__I_I_____===__|_________|' |(_)--- | H\________/ | | =|___ ___| ' / | | H | | | | ||_| |_|| ' | | | H |__--------------------| [___] | ' | ________|___H__/__|_____/[][]~\_______| | ' |/ | |-----------I_____I [][] [] D |=======|__' __/ =| o |=-~~\ /~~\ /~~\ /~~\ ____Y___________|__' |/-=|___|= || || || |_____/~\___/ ' \_/ \_O=====O=====O=====O/ \_/ ' ";
Note several things:
When the compiler ("cargo") is compiling the program, any place it sees the constant "D51", that constant will be "replaced" with the actual contents of the constant.
And then, we need to convert the constant into a vector of String vectors. We'll create a function to do this, and for now, we'll keep it in the "d51.rs" file:
The program won't compile/work currently. We'll have to massage this constant into a vector of String vectors. Since this is something that will have to be done for all image-sets (the D51, the C51, the Coalcar, etc), it should probably go in "main.rs". We could put it in yet a different file, in order to keep from cluttering up "main.rs", but it seems more reasonable to me, at this point, to put it in "main.rs".
... } // end of draw_d51()fn convert_to_vecvecstring(image_str: &str) -> Vec<Vec<String>> { /* * This is bad coding - we're *assuming* the &str to be ASCII-sized chars; include a non-ASCII character, * like '汉', in your drawing, where a string operation tries to access it, like as the first character * of the image's const declaration ( pub const D51: &str = r" ...pub const D51: &str = r"汉
), and watch your program go BOOM! */ //The first character in the string is a newline; lose it. let image_str = &image_str[1..image_str.len()]; // Create new blank vector of vector of Strings. let mut outer_vec: Vec<Vec<String>> = Vec::new(); // This keeps track of which frame/inner vector we're processing. let mut inner_vec_num: usize = 0; // Create first inner vector in the outer vector. outer_vec.push(Vec::new()); for each_line in image_str.lines() { if each_line != "" { // Remove single-quote mark at end of each line, if it exists.. let each_line = if &each_line[each_line.len() - 1..] == "'" { each_line[0..each_line.len() - 1].to_string() } else { each_line.to_string() }; // Then add the string to the current inner vector. outer_vec[inner_vec_num].push(each_line); } else { outer_vec.push(Vec::new()); inner_vec_num += 1; } } // Either return method below is acceptable. The second one only works as the last expression in the function. return outer_vec; // We're returning the entire vector of String vectors. // outer_vec // We're returning the entire vector of String vectors. } // end of convert_to_vecvecstring()
And then we need to modify our "draw_d51()" function:
pub fn draw_d51() {
d51
The comments should make it fairly self-explanatory what the code is doing.
Earlier, in the "Draw All Frames of Train" page, we pointed out that, "...it is bad practice to rely on indexing [of &str strings], even if technically it would work." But then here we've gone and done just that. Since we can make sure that the ASCII-art images are indeed composed of ASCII characters (of the ASCII subset of the UTF-8 characters, more accurately), we can get away with this. But if we were writing this properly, we'd do something different. But ..., we're lazy....
This program should do as we expect: display each of the six D51 frames. And by copying the bulk of the "d51.rs" file to a different filename with a different set of ASCII-art images, and a few other mods, we can display that different artwork.
Copy the "d51.rs" file to a new file named "jack.rs", and then replace the artwork and make the few mods indicated:
/* d51.rs *//* jack.rs */ pub fn draw_d51jack () { // Convert const into a vector of String vectors. let image_vecvecstring = convert_to_vecvecstring(D51JACK ); for each_inner_vec in image_vecvecstring { for each_line in &each_inner_vec { println!("{}", each_line); } } } // end of draw_d51jack () fn convert_to_vecvecstring(image_str: &str) -> Vec<Vec<String>> { /* * This is bad coding - we're *assuming* the &str to be ASCII-sized chars; include a non-ASCII character, * like '汉', in your drawing, where a string operation tries to access it, like as the first character * of the image's const declaration (pub const D51: &str = r"汉), and watch your program go BOOM! */ //The first character in the string is a newline; lose it. let image_str = &image_str[1..image_str.len()]; // Create new blank vector of vector of Strings. let mut outer_vec: Vec<Vec<String>> = Vec::new(); // This keeps track of which frame/inner vector we're processing. let mut inner_vec_num: usize = 0; // Create first inner vector in the outer vector. outer_vec.push(Vec::new()); for each_line in image_str.lines() { if each_line != "" { // Remove single-quote mark at end of each line, if it exists.. let each_line = if &each_line[each_line.len() - 1..] == "'" { each_line[0..each_line.len() - 1].to_string() } else { each_line.to_string() }; // Then add the string to the current inner vector. outer_vec[inner_vec_num].push(each_line); } else { outer_vec.push(Vec::new()); inner_vec_num += 1; } } // Either return method below is acceptable. The second one only works as the last expression in the function.// return outer_vec; // We're returning the entire vector of String vectors.//outer_vec // We're returning the entire vector of String vectors. } // end of convert_to_vecvecstring()const JACK: &str = r" \ 0 / ' \|/ ' | ' / \ ' _/ \_ ' ' __0__ ' / | \ ' / \ ' _\ /_ ' ' o ' /\ /\ ' |/ \| ' _\ /_ ' ' __0__ ' / | \ ' / \ ' _\ /_ ' ' "; // end of JACK
Add the Jack module to "main.rs", and call the function:
mod d51;mod jack; use d51::*; fn main() { draw_d51();jack::draw_jack(); } // end of main()
And now you're displaying the six frames of the D51 train, followed by the four frames of Jumping Jack.
(You'll notice I did not add a use jack::*;
line, opting instead to just specify the path in the calling statement. I only did it this way to remind you that it could be done this way.)
We can even add a coalcar:
mod d51; mod jack;mod coalcar; use d51::*; fn main() { draw_d51(); jack::draw_jack();coalcar::draw_coalcar(); } // end of main()
/* jack.rs *//* coalcar.rs */ pub fn draw_jackcoalcar () { // Convert const into a vector of String vectors. let image_vecvecstring = convert_to_vecvecstring(jackCOALCAR ); for each_inner_vec in image_vecvecstring { for each_line in &each_inner_vec { println!("{}", each_line); } } } // end of draw_jackcoalcar () fn convert_to_vecvecstring(image_str: &str) -> Vec<Vec<String>> { /* * This is bad coding - we're *assuming* the &str to be ASCII-sized chars; include a non-ASCII character, * like '汉', in your drawing, where a string operation tries to access it, like as the first character * of the image's const declaration (pub const D51: &str = r"汉), and watch your program go BOOM! */ //The first character in the string is a newline; lose it. let image_str = &image_str[1..image_str.len()]; // Create new blank vector of vector of Strings. let mut outer_vec: Vec<Vec<String>> = Vec::new(); // This keeps track of which frame/inner vector we're processing. let mut inner_vec_num: usize = 0; // Create first inner vector in the outer vector. outer_vec.push(Vec::new()); for each_line in image_str.lines() { if each_line != "" { // Remove single-quote mark at end of each line, if it exists.. let each_line = if &each_line[each_line.len() - 1..] == "'" { each_line[0..each_line.len() - 1].to_string() } else { each_line.to_string() }; // Then add the string to the current inner vector. outer_vec[inner_vec_num].push(each_line); } else { outer_vec.push(Vec::new()); inner_vec_num += 1; } } // Either return method below is acceptable. The second one only works as the last expression in the function. // return outer_vec; // We're returning the entire vector of String vectors. outer_vec // We're returning the entire vector of String vectors. } // end of convert_to_vecvecstring()const COALCAR: &str = r" _________________ _| \_____A =| | -| | __|________________________|_ |__________________________|_ |_D__D__D_| |_D__D__D_| \_/ \_/ \_/ \_/ "; // end of COALCAR
And now your program should display the six frames of the D51, the four frames of Jack, and one frame of a coalcar.
But the "convert_to_vecvecstring()" function is exacrly the same in each image file. It might make more sense to move this function back into the "main.rs" file so it only has to exist once, instead of once for every image. So we'll just delete the function out of two of the files, and we'll cut it out of the third file and paste it into the "main.rs" file.
mod d51; mod jack; mod coalcar; use d51::*; fn main() { draw_d51(); jack::draw_jack(); coalcar::draw_coalcar(); } // end of main()fn convert_to_vecvecstring(image_str: &str) -> Vec<Vec<String>> { /* * This is bad coding - we're *assuming* the &str to be ASCII-sized chars; include a non-ASCII character, * like '汉', in your drawing, where a string operation tries to access it, like as the first character * of the image's const declaration (pub const D51: &str = r"汉), and watch your program go BOOM! */ //The first character in the string is a newline; lose it. let image_str = &image_str[1..image_str.len()]; // Create new blank vector of vector of Strings. let mut outer_vec: Vec<Vec<String>> = Vec::new(); // This keeps track of which frame/inner vector we're processing. let mut inner_vec_num: usize = 0; // Create first inner vector in the outer vector. outer_vec.push(Vec::new()); for each_line in image_str.lines() { if each_line != "" { // Remove single-quote mark at end of each line, if it exists.. let each_line = if &each_line[each_line.len() - 1..] == "'" { each_line[0..each_line.len() - 1].to_string() } else { each_line.to_string() }; // Then add the string to the current inner vector. outer_vec[inner_vec_num].push(each_line); } else { outer_vec.push(Vec::new()); inner_vec_num += 1; } } // Either return method below is acceptable. The second one only works as the last expression in the function. //return outer_vec; // We're returning the entire vector of String vectors. // outer_vec // We're returning the entire vector of String vectors.} // end of convert_to_vecvecstring()
However, the image files will no longer be able to find the function, unless we specify the path to the function. We can do that like this:
/* jack.rs */use crate::convert_to_vecvecstring; pub fn draw_jack() { // Convert const into a vector of String vectors. let image_vecvecstring = convert_to_vecvecstring(JACK); for each_inner_vec in image_vecvecstring { for each_line in &each_inner_vec { println!("{}", each_line); } } } // end of draw_jack() ...
... or like this:
/* jack.rs */use crate::*; pub fn draw_jack() { // Convert const into a vector of String vectors. let image_vecvecstring = convert_to_vecvecstring(JACK); for each_inner_vec in image_vecvecstring { for each_line in &each_inner_vec { println!("{}", each_line); } } } // end of draw_jack() ...
... or like this:
/* jack.rs */ pub fn draw_jack() { // Convert const into a vector of String vectors. let image_vecvecstring =crate:: convert_to_vecvecstring(JACK); for each_inner_vec in image_vecvecstring { for each_line in &each_inner_vec { println!("{}", each_line); } } } // end of draw_jack() ...
... for all three image files ("jack.rs", "coalcar.rs", and "d51.rs").
The "crate" is the "sl" project that we're creating; it is essentially synonymous with the "main.rs" file/module. Because the "main.rs" file is the "top level" of the entire "sl" "crate", we don't have to add "mod main;" or "mod crate;" to the image files, and instead of saying "use main::*" we say "use crate::*;".
Now it's time to start using ncurses to draw our images rather than simply using "println!". Accordingly, let's Use ncurses to Draw the Images.