CodeBlanks Logo Icon CodeBlanks

Result and commands

Split commands into functions that return Result.

Commands with Result

Let the command handler return each command's result as Result<String, String>: a message for the user on success, a description on error. Slice patterns let you match the arguments directly:

fn calistir(d: &mut Defter, args: &[&str]) -> Result<String, String> {
    match args {
        ["ekle", metin @ ..] if !metin.is_empty() => {
            d.ekle(metin.join(" "));
            Ok(String::from("eklendi"))
        }
        ["sil", n] => { /* parse n */ }
        [k, ..] => Err(format!("bilinmeyen komut: {k}")),
        [] => Err(String::from("komut yok")),
    }
}

Deleting and validation

The delete command must parse the number and check it's in range. Instead of a bare ?, clear messages are produced with map_err:

["sil", n] => {
    let i: usize = n.parse().map_err(|_| format!("geçersiz numara: {n}"))?;
    if i == 0 || i > d.notlar.len() {
        return Err(format!("böyle bir not yok: {i}"));
    }
    let silinen = d.notlar.remove(i - 1);
    Ok(format!("silindi: {silinen}"))
}

Practice Command handler

Write fn calistir(d: &mut Defter, args: &[&str]) -> Result<String, String>: handle ekle, sil and unknown commands as above. Print the results of ["ekle", "süt", "al"], ["sil", "x"] and ["uç"] with {:?} on separate lines.

Starter code

main.rs
struct Defter {
    notlar: Vec<String>,
}

impl Defter {
    fn new() -> Self {
        Self { notlar: Vec::new() }
    }
    fn ekle(&mut self, metin: String) {
        self.notlar.push(metin);
    }
}

fn main() {
    // buraya yaz
}

Sample solution

main.rs
struct Defter {
    notlar: Vec<String>,
}

impl Defter {
    fn new() -> Self {
        Self { notlar: Vec::new() }
    }
    fn ekle(&mut self, metin: String) {
        self.notlar.push(metin);
    }
}

fn calistir(d: &mut Defter, args: &[&str]) -> Result<String, String> {
    match args {
        ["ekle", metin @ ..] if !metin.is_empty() => {
            d.ekle(metin.join(" "));
            Ok(String::from("eklendi"))
        }
        ["sil", n] => {
            let i: usize = n.parse().map_err(|_| format!("geçersiz numara: {n}"))?;
            if i == 0 || i > d.notlar.len() {
                return Err(format!("böyle bir not yok: {i}"));
            }
            let silinen = d.notlar.remove(i - 1);
            Ok(format!("silindi: {silinen}"))
        }
        [k, ..] => Err(format!("bilinmeyen komut: {k}")),
        [] => Err(String::from("komut yok")),
    }
}

fn main() {
    let mut d = Defter::new();
    println!("{:?}", calistir(&mut d, &["ekle", "süt", "al"]));
    println!("{:?}", calistir(&mut d, &["sil", "x"]));
    println!("{:?}", calistir(&mut d, &["uç"]));
}

Expected output

Ok("eklendi")
Err("geçersiz numara: x")
Err("bilinmeyen komut: uç")

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 In the pattern <code>["ekle", metin @ ..]</code>, what is <code>metin</code>?

  • The number of elements
  • The slice of all elements after "ekle" Correct answer
  • Only the second element
  • The last element

Explanation: .. matches the remaining elements; with metin @, that part is bound as a slice.

2 Bind the rest

Question
match args {
    ["ekle", metin @ ____] => Ok(metin.join(" ")),
    _ => Err(String::from("?")),
}
Answer
match args {
    ["ekle", metin @ ..] => Ok(metin.join(" ")),
    _ => Err(String::from("?")),
}

Explanation: In a slice pattern, .. matches all the remaining elements.

3 Arguments and result

Term Meaning
["ekle", "süt", "al"] Ok("eklendi")
["sil", "x"] Err("geçersiz numara: x")
["uç"] Err("bilinmeyen komut: uç")
[] Err("komut yok")

All lessons in Intermediate