Rust Questions from Beginners, Including Me

Have Rust beginner questions? Discord has Rust channels for the help you’re looking for!

Beginners are encouraged to ask questions
We are stocked full of answers. But… some are “no”.

Information overload and I’m still trying to find a bigger project I can work on that interests me. I have the Interpreter to work on still, and that will take some serious work! But I’m also thinking of going back to small systems of my programming past and playing with writing them in Rust. In the meantime, I thought I’d sneak around the Rust #beginners channel on Discord and give everyone a peak at some questions (and hopefully some answers) that beginners are asking about their early Rust code.


I have a [u8; 10] and I have tried to do a .map() on it but I get:

note: the method 'map' exists but the following trait bounds were not satisfied:
   '&mut [u8; 10] : std::iter::Iterator'
   '&mut [u8] : std::iter::Iterator'

Try .iter().map(...) (Or iter_mut based on what you need).

So, I’m assuming this is an example of that (though, with u16 so my sum doesn’t get too big):

fn main() {
    let list: Vec<u16> = [1,1,2,3,5,8,13,21,34,55].to_vec();
    list.iter().for_each(|n| println!("The next is: {}", n));
    let sum: u16 = list.iter().map(|n| { n }).sum();
    println!("\nSum of list: {}", sum);
}

Note a few things here. We couldn’t do:
list.iter().map(|n| println!("The next is: {}", n));
and the compiler nicely reminds us why:
note: iterators are lazy and do nothing unless consumed

So, iter().for_each() closure works nicely … or we could have used an old-school for loop over the iter() instead.


What would be the Rust equivalent for this in C:
#define TEST 1

A constant is appropriate for this case, for example:
const TEST: i32 = 1;




Student looking in book for answers to his questions
I. Don’t. See. My. Question. Anywhere!

I went ahead and asked my beginner question from the previous blog post: when implementing a trait for several “related” structs, I can require other traits that I depend on… but I can’t make a field (that all the structs share) a requirement… which prevents me from being able to write a default implementation of a trait – I have to write it individually for each struct… is that “just how it is”?

And I got back a good answer:

struct fields are not part of a trait's interface; traits can be implemented by things other than structs e.g. enums, primitive types, closures, …

you can add a health(&self) -> u8 method on your trait that each struct is required to implement, and then use that in the trait's implementation of cur_health

if you want the trait to advertise that its implementors have a concept of "health" then that's one way to go about it

All of that makes sense… and leads me to slightly change the code, to see how it looks with that idea…

struct Player {
    player_name: String,
    real_name: String,
    level: u8,
    health: u8,
}

struct Monster {
    name: String,
    subtype: String,
    hit_dice: u8,
    health: u8,
}

trait Creature {
    fn health(&self) -> u8; 
    fn cur_health(&self) -> String {
        format!("Health: {}", self.health())
    }
}

impl Creature for Player {
    fn health(&self) -> u8 { self.health }
}
impl Creature for Monster {
    fn health(&self) -> u8 { self.health }
}

fn main() {
    let player_1 = Player {
        player_name: "Aragorn".to_string(),
        real_name: "Viggo Mortensen".to_string(),
        level: 20,
        health: 120,
    };
    let monster_1 = Monster {
        name: "Orc".to_string(),
        subtype: "Captain".to_string(),
        hit_dice: 7,
        health: 21,
    };

    println!(" Player 1: {}", player_1.cur_health());
    println!("Monster 1: {}", monster_1.cur_health());
}

Now, health() is a required trait, and not default implemented (since I can’t), but cur_health() to print the string now CAN be defaulted.

So I can implement the simple health() for each struct and let the “fancy” cur_health() default for each struct type. I do think this is an improvement. I’m not sure I’d even implement any of this in this way, but this example is showing me what is possible.

Config Solutions

Embossed compass legend

So, because match gave me some problems (hrm, what Rust troubles are yet to come!?), this took me a minute to get right. These changes allow us to configure the encoding characters to be defined in .json config files. Plus, a platform level (development, stage, production) can be set and configured values can be overridden via environment variables.

Code Dump Coming…

extern crate config;
extern crate serde;

#[macro_use]
extern crate serde_derive;

mod settings;

use settings::Settings;

fn main() {
    for arg in std::env::args().skip(1) {
      let encoded = encode(&arg);
      let decoded = decode(&encoded);
      assert_eq!(arg, decoded);

      println!("'{}' encodes as '{}' and decode works",
          &arg, &encoded);
    }
 }

fn encode(orig: &str) -> String {
    let settings = Settings::new();

    let mut advance = '1';
    let mut print = '0';
    match settings {
        Ok(s) => {
            advance = s.advance;
            print = s.print;
        },
        Err(_) => {}
    }

    let mut str = String::new();
    let mut cur: u8 = 0;

    for c in orig.chars() {
      while c != cur as char {
        cur = cur.wrapping_add(1);
        str.push(advance);
      }
      str.push(print);
    }
    str
}

fn decode(code: &str) -> String {
    let settings = Settings::new();

    let mut advance = '1';
    let mut print = '0';
    match settings {
        Ok(s) => {
            advance = s.advance;
            print = s.print;
        },
        Err(_) => {}
    }
    let mut str = String::new();
    let mut cur: u8 = 0;

    for c in code.chars() {
        match c {
            a if a == advance => cur = cur.wrapping_add(1),
            b if b == print => str.push(cur as char),
            _ => {},
        }
    }
    str
}



And now I have settings.rs as well:

use std::env;
use config::{ConfigError, Config, File, Environment};

#[derive(Debug, Deserialize)]
pub struct Settings {
    pub debug: bool,
    pub advance: char,
    pub print: char,
}

impl Settings {
    pub fn new() -> Result<Self, ConfigError> {
        let mut s = Config::new();

        // Start off by merging in the "default" configuration file
        s.merge(File::with_name("conf/default"))?;

        // Add in the current environment file
        // Default to 'development' env
        // Note that this file is _optional_
        let env = env::var("RUN_MODE").unwrap_or("development".into());
        s.merge(File::with_name(&format!("conf/{}", env)).required(false))?;

        // Add in a local configuration file
        // This file shouldn't be checked in to git
        s.merge(File::with_name("conf/local").required(false))?;

        // Add in settings from the environment (with a prefix of APP)
        // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key
        s.merge(Environment::with_prefix("app"))?;

        // You may also programmatically change settings
        //s.set("database.url", "postgres://")?;

        // Now that we're done, let's access our configuration
        //println!("debug: {:?}", s.get_bool("debug"));
        //println!("database: {:?}", s.get::<String>("database.url"));

        // You can deserialize (and thus freeze) the entire configuration as
        s.try_into()
    }
}

and some config files too!

conf/default.json:

{
    "debug": 1
}

conf/development.json:

 {
    "advance": "#",
    "print": "@"
}

Can I Talk My Way Through That?

Feel free to comment about what I did wrong, because at the very least, my settings processing seems wrong and repetitive! As bad as globals are, is that possible in Rust and better than passing it around or certainly better than having to go assign settings in every method that needs it?

Also, I understand making the settings struct be pub but why did I have to make each of its properties pub as well – that doesn’t seem right.

It took me a while to fully realize why match was called match and not switch! Because it is based on patterns and not straight conditionals! But these aren’t perl regex patterns, so don’t get excited! Even though they aren’t actual regex’s, they ARE “patterns”, so matching one variable against another isn’t straightforward like with switch/case.

    match c {
        a if a == advance => cur = cur.wrapping_add(1),
        b if b == print => str.push(cur as char),
        _ => {},
    }

So, to check if character c matches variables advance or print, we need (I think) to do it this way: a and b in each of those match arms serve as temporary variables to make the comparison and are lost as soon as the match statement ends. I’ll come back when I figure out the correct way, but hopefully someone will let me know!