These are chat archives for rust-lang/rust

18th
Jul 2017
isaacg1
@isaacg1
Jul 18 2017 05:44

I can't figure out why this doesn't work. I'm trying to make a static HashMap mapping &str to a certain type of function:

static FUNCTIONS: HashMap<&'static str, &fn(Vec<Expression>) -> Expression> =
    vec![("+", &plus)].into_iter().collect();

fn plus(args: Vec<Expression>) -> Expression {
    args.iter().sum()
}

The error message is:

error[E0277]: the trait bound `std::collections::HashMap<&str, &fn(std::vec::Vec<Expression>) -> Expression>: std::iter::FromIterator<(&str, &fn(std::vec::Vec<Expression>) -> Expression {plus})>` is not satisfied
  --> src/main.rs:20:36
   |
20 |     vec![("+", &plus)].into_iter().collect();
   |                                    ^^^^^^^ a collection of type `std::collections::HashMap<&str, &fn(std::vec::Vec<Expression>) -> Expression>` cannot be built from an iterator over elements of type `(&str, &fn(std::vec::Vec<Expression>) -> Expression {plus})`
   |
   = help: the trait `std::iter::FromIterator<(&str, &fn(std::vec::Vec<Expression>) -> Expression {plus})>` is not implemented for `std::collections::HashMap<&str, &fn(std::vec::Vec<Expression>) -> Expression>

The relevant documentation link is: https://doc.rust-lang.org/std/collections/struct.HashMap.html at the bottom.

isaacg1
@isaacg1
Jul 18 2017 05:57
In contrast, the following both compile without errors:
static TEST_VEC: HashMap<&'static str, &'static i32> =
    vec![("+", &1)].into_iter().collect();

static TEST_FUNC: &'static Fn(Vec<Expression>) -> Expression =
    &plus;
To be clear, this is thing that doesn't work:
static FUNCTIONS: HashMap<&'static str, &'static Fn(Vec<Expression>) -> Expression> =
    vec![("+", &plus)].into_iter().collect();
isaacg1
@isaacg1
Jul 18 2017 06:08
OK, the whole idea is silly, just ignore this.
Sherab Giovannini
@Shaddy
Jul 18 2017 09:59
hi guys, I'm following the "tutorial" here => https://doc.rust-lang.org/book/second-edition/ch12-03-improving-error-handling-and-modularity.html at some point he says to split the code into /src/lib.rs (which I understand belongs to the same project), and "use it" with extern crate greprs, but I don't find the connection between "greprs" and "lib.rs", the code doesn't contain any "mod greprs" or so
am I missing something?
Denis Lisov
@tanriol
Jul 18 2017 10:02
greprs is the name of the Cargo project used in that chapter.
Sherab Giovannini
@Shaddy
Jul 18 2017 10:03
damn :facepalm:
thanks @tanriol :)
Sherab Giovannini
@Shaddy
Jul 18 2017 10:16
awesome chapter btw
Sherab Giovannini
@Shaddy
Jul 18 2017 10:49
is this channel appropiate to post code in relation to book tutorial? meaning, I would like to get some suggestions of "canonical" ways to improve my code
Max Frai
@max-frai
Jul 18 2017 10:51
Just ask the questions :)
Sherab Giovannini
@Shaddy
Jul 18 2017 10:52

I'm following the greprs tutorial, at the end of enviroment variable example, it suggests to check both environment variable and command line, and priorize one of them. Original code (from tutorial) is:

impl Config {
    pub fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }

        let query = args[1].clone();
        let filename = args[2].clone();

        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();

        Ok(Config {
            query: query,
            filename: filename,
            case_sensitive: case_sensitive,
        })
    }
}

I ran into something like this, but not sure if there is another "cleaner" way to approach this.

impl Config {
    pub fn new(arguments: &Vec<String>) -> Result<Config, &'static str> {

        match arguments.len() {
            0 | 1 | 2 => return Err("not enough arguments"),
            3 => {
                let query = arguments[1].to_string();
                let filename = arguments[2].to_string();

                return Ok(Config {
                    query: query,
                    filename: filename,
                    case_sensitive: false })
            },
            4 => {
                let query = arguments[1].to_string();
                let filename = arguments[2].to_string();
                let option = arguments[3].to_string();

                let case_sensitive = if option == "--case-insensitive" { 
                    false 
                } else { 
                    env::var("CASE_INSENSITIVE").is_err()
                };

                return Ok(Config {
                    query: query,
                    filename: filename,
                    case_sensitive: case_sensitive })        
            },
            _ => return Err("more arguments than expected")

        }
    }

}
the thing is that I'm not confortable by having 2 different scopes for each len of argument, then coding repetitively behaviours
I could declare first query, filename and option, for example, and "fill" it by matching len, but I've read that it's not a good practice
any suggestion?
Denis Lisov
@tanriol
Jul 18 2017 11:06
@Shaddy Point number one: in real life the best suggestion will be to use an existing command-line argument processing library such as clap or docopt
And even if not, I'd probably first make a loop over arguments to filter out options and apply the case-insensitive flag, and then match on the positional arguments.
At the moment I actually prefer structopt
Sherab Giovannini
@Shaddy
Jul 18 2017 11:15
all right, I figured out that there should be some useful commandline parsing library, just following the tutorial
let me refactor to something better
Sherab Giovannini
@Shaddy
Jul 18 2017 11:22
Well, not the best way but I think it's more close on what you suggested
impl Config {
    pub fn new(arguments: &Vec<String>) -> Result<Config, &'static str> {

        if arguments.len() < 3 {
            return Err("not enough arguments")
        }

        let query = arguments[1].to_string();
        let filename = arguments[2].to_string();

        let mut config = Config {
            query: query,
            filename: filename,
            case_sensitive: true };

        for argument in arguments[2..].iter() {
            match argument.as_ref() {
                "--case-insensitive" => config.case_sensitive = false,
                "--whatever-option" => (),
                _ => (),
            }
        }

        config.case_sensitive = if config.case_sensitive { env::var("CASE_INSENSITIVE").is_err() } else { false };

        Ok(config)
    }

}
first I get the fixed arguments, and create a mutable config in order to update it with next arguments, then iterate over left arguments and match interesting cases
the last if is not really good, I'm aware that could be better
for example this config.case_sensitive |= env::var("CASE_INSENSITIVE").is_err();
Denis Lisov
@tanriol
Jul 18 2017 12:25
Note that normal command-line conventions usually allow options to be specified even before or between arguments: greprs --case-insensitive query --some-other-option filename
Sherab Giovannini
@Shaddy
Jul 18 2017 12:56
yes, my intention wasn't to have a perfect parser but trying to express myself in a clean way, best approach would be to apply a filter of those arguments that starts with "--", and create a iterator over them by matching occurrences
I guess
its really great rust
Nikolay Denev
@ndenev
Jul 18 2017 13:23
Hi, I'm having some trouble figuring out how to do something so I'm hoping someone can have idea.
Basically I'm trying to implement Iterator over a file that reads data in chunks using BufReader's fill_buff() and consume(),
and then parse the variably sized records using NOM. The thing is when I reach the end of the first 8k buffer I have some data left
in it but it's not enough to parse next record so NOM returns IResult::Incomplete and then I call fill_buff() again but I'm getting the same buffer back.
Do I have to consume the whole buffer so that it gets refilled? This means probably that I have to store this remainder somewhere?
Sherab Giovannini
@Shaddy
Jul 18 2017 14:39

Surprisingly this code

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
    }

    handle.join().unwrap();
}

executes everything secuentially...

hi number 1 from the main thread!
hi number 2 from the main thread!
hi number 3 from the main thread!
hi number 4 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the spawned thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!
Jonas Platte
@jplatte
Jul 18 2017 14:41
@Shaddy Not really, actually
Sherab Giovannini
@Shaddy
Jul 18 2017 14:41
what happened?
Jonas Platte
@jplatte
Jul 18 2017 14:41
Spawning a thread is an expensive operation and a tiny loop from 1 to 5 is way faster.
Sherab Giovannini
@Shaddy
Jul 18 2017 14:42
so, if I go for 100 for each loop should be intercalated?
Jonas Platte
@jplatte
Jul 18 2017 14:42
I doubt it ^^
sleep for 1-10 milliseconds for every iteration in both loops should do it though
Sherab Giovannini
@Shaddy
Jul 18 2017 14:42
ok, lets see
yep, you're right
Jonas Platte
@jplatte
Jul 18 2017 14:44
:)
Sherab Giovannini
@Shaddy
Jul 18 2017 14:44
for a while I thought maybe writting to stdout could be synced or something xD
or just windows things
Jonas Platte
@jplatte
Jul 18 2017 14:45
Well it is synced insofar as that you won't get output like "hi hinumber number 11"
Sherab Giovannini
@Shaddy
Jul 18 2017 14:46
oh, thats true
Jonas Platte
@jplatte
Jul 18 2017 14:46
every time you run println! it locks stdout until it has written the full line.
Sherab Giovannini
@Shaddy
Jul 18 2017 14:47
sounds logic