Info
Defining and implementing a trait:
trait MyTrait {
fn print_static();
fn print_member(&self);
fn set(&mut self, x: u32);
fn consume(self);
}
struct S {
x: u32,
}
impl MyTrait for S {
fn print_static() {
println!("wow! 🎈");
}
fn print_member(&self) {
println!("{}", self.x);
}
fn set(&mut self, x: u32) {
self.x = x;
}
fn consume(self) {
println!("and we're done");
}
}
fn main() {
S::print_static();
// static fn
S { x: 5 }.print_member();
// const self ref
let mut x = S { x: 10 };
x.set(345);
x.print_member();
// mutable self ref
x.consume();
// self
// x.print_member();
// variable was moved
}
Vectors:
fn main() {
let mut v = Vec::new();
v.push(("abc", 5));
v.push(("def", 6));
// iterate immutably
for (s, n) in &v {
println!("{} {}", s, n);
}
// iterate mutably
for (_, n) in &mut v {
*n += 1;
}
// iterate and destroys the vec
for (s, n) in v {
println!("{} {}", s, n);
}
}
Problems
P1
For this problem you are supposed to write an application that emulates a terminal that implements several commands. To emulate a command, make a trait with the following functions:
- get_name: returns a string with the name of the command
- exec: takes a slice of strings with the command arguments
Make a struct for each command and implement the above trait for the following commands:
- ping: prints "pong!"
- count: prints the number of arguments it received;
count a b c-> will printcounted 3 args - times: prints how many times this command (only this command!) has been called
- a new command of your choosing
Add a special stop command (this doesn't have to be implemented through the trait) that stops the execution. Suggest the correct command if the uses misspells it (eg: if it says TIMES).
Example input: file.
The application will print the error if anything bad happens. The application must not panic.
Make a struct that keeps the commands in a collection, reads the commands from a text file, and executes them. This struct needs to have the following functions:
- new: creates a new instance
- register: adds the new command to the collection
- run: reads lines a file, splits them based on spaces, uses the first word as the command and searches it in the collection, and executes the command with the remaining arguments. This function should treat the case where the command is not valid or if the line is empty. It will continue to run even on error cases.
Example main:
fn main() {
let mut terminal = Terminal::new();
terminal.register(Box::new(PingCommand {}));
terminal.register(Box::new(CountCommand {}));
terminal.register(Box::new(TimesCommand { count: 0 }));
terminal.run();
}
Bonus
SQLite is the most used database engine on the planet, being used in desktop, laptops, phones, aircrafts, TVs, browsers, antiviruses, etc. The difference between SQLite and other databases is that SQLite is designed to not have the need of a server, and is routinely used by applications to store data (eg: your phone stores the contact list in a SQLite database).
rusqlite is the crate that is used the most to interact with SQLite databases. Add the following under dependencies:
rusqlite = { version = "0.30.0", features = ["bundled"] }
Opening and creating tables if they not exists (because we'll probably run this multiple times):
let conn = Connection::open("persons.db")?;
let create = r"
create table if not exists persons (
name text not null,
age integer not null
);
";
conn.execute(create, ())?;
Note: you need execute_batch if you want to create more than a table.
Inserting data in a table:
let name = "sergiu";
let age = 22;
conn.execute("insert into persons (name, age) values (?1, ?2);", (name, age))?;
Notice that the SQL query inserts the values ?1 and ?2. This gets replaced by the arguments that follow.
Selecting data from the database:
struct Person {
name: String,
age: u8
}
let mut stmt = conn.prepare("select * from persons")?;
let person_iter = stmt.query_map([], |row| {
Ok(Person {
name: row.get("name")?,
age: row.get("age")?
})
})?;
for i in person_iter {
let i = i?;
println!("name={}, age={}", i.name, i.age);
}
The query_map function accepts as first argument the possible added arguments with ?1, ?2, etc., just like execute does. In this case, this happens to be empty.
P2
Add a bookmark "bk" command that uses a SQLite database to store bookmarks. Each bookmark can have a name and an url.
Based on the first argument, the command will do the following:
add <name> <url>: adds an url to the databasesearch <name>: prints all the names and the urls that containsnamein their name
Usage example:
bk add foxes https://www.reddit.com/r/foxes/
bk add rust_book https://doc.rust-lang.org/book/
bk add foxes_pics https://www.boredpanda.com/beautiful-fox-pictures/
bk search fox
bk search rust