在Rust中使用stdin数据实例化struct

时间:2013-12-31 17:41:22

标签: closures rust

我对Rust非常非常新,并尝试实现一些简单的东西来感受语言。现在,我绊倒了实现类似类的结构的最佳方法,该结构涉及将字符串转换为int。我正在使用一个全局命名空间的函数,我的Ruby大脑感觉不对。

这样做的乡土方式是什么?

use std::io;

struct Person {
  name: ~str,
  age: int
}

impl Person {  
  fn new(input_name: ~str) -> Person {
    Person { 
      name: input_name, 
      age: get_int_from_input(~"Please enter a number for age.")
    }
  }

  fn print_info(&self) {
    println(fmt!("%s is %i years old.", self.name, self.age));
  }

}

fn get_int_from_input(prompt_message: ~str) -> int {
  println(prompt_message);
  let my_input = io::stdin().read_line();
  let my_val =
    match from_str::<int>(my_input) {
      Some(number_string)   => number_string,
      _                     => fail!("got to put in a number.")
  };

  return my_val;
}


fn main() {
  let first_person = Person::new(~"Ohai");
  first_person.print_info();
}

这会编译并具有所需的行为,但我不知道该怎么做 - 显然我不了解最佳实践或如何实现它们。

编辑:这是0.8

3 个答案:

答案 0 :(得分:8)

以下是我的代码版本,我更加惯用了这个代码:

use std::io;

struct Person {
  name: ~str,
  age: int
}

impl Person {
  fn print_info(&self) {
    println!("{} is {} years old.", self.name, self.age);
  }

}

fn get_int_from_input(prompt_message: &str) -> int {
  println(prompt_message);
  let my_input = io::stdin().read_line();
  from_str::<int>(my_input).expect("got to put in a number.")
}


fn main() {
  let first_person = Person {
    name: ~"Ohai",
    age: get_int_from_input("Please enter a number for age.")
  };
  first_person.print_info();
}

fmt! / format!

首先,Rust使用基于fmt!的语法弃用printf宏,而使用format!,它使用类似于Python format strings的语法。新版本的Rust 0.9会抱怨使用fmt!。因此,您应该将fmt!("%s is %i years old.", self.name, self.age)替换为format!("{} is {} years old.", self.name, self.age)。但是,我们有一个方便的宏println!(...),这意味着与println(format!(...))完全相同,所以在Rust中编写代码的最惯用方法是

println!("{} is {} years old.", self.name, self.age);

初始化结构

对于像Person这样的简单类型,在Rust中使用struct literal语法创建该类型的实例是惯用的:

let first_person = Person {
    name: ~"Ohai",
    age:  get_int_from_input("Please enter a number for age.")
};

如果您确实需要构造函数,Person::new是类型Person的“默认”构造函数(我指的是最常用的构造函数)的惯用名。但是,默认构造函数要求从用户输入初始化似乎很奇怪。通常,我认为您将拥有person模块(例如,模块导出person::Person)。在这种情况下,我认为使用模块级函数fn person::prompt_for_age(name: ~str) -> person::Person是最惯用的。或者,您可以在Person - Person::prompt_for_age(name: ~str)上使用静态方法。

函数参数中的

&str~str

我已将get_int_from_input的签名更改为&str而不是~str~str表示在交换堆上分配的字符串 - 换句话说,C中的malloc / free或C ++中的new / delete的堆操作。但是,与C / C ++不同,Rust强制要求交换堆上的值一次只能由一个变量拥有。因此,将~str作为函数参数意味着函数的调用者不能重用它传入的~str参数 - 它必须复制{{1}使用~str方法。

另一方面,.clone是字符串的切片,它只是对字符串中一系列字符的引用,因此它不需要在分配字符串的新副本时调用具有&str参数的函数。

&str中对&str使用~str而不是prompt_message的原因是该函数不需要保留超过函数末尾的消息。它仅使用提示消息进行打印(而println需要get_int_from_input,而不是&str。更改函数以~str后,您可以将其称为&str而不是get_int_from_input("Prompt"),这样可以避免在堆上不必要地分配get_int_from_input(~"Prompt")(同样,您也可以可以避免在下面的代码中克隆"Prompt"

s

let s: ~str = ~"Prompt"; let i = get_int_from_input(s.clone()); println(s); // Would complain that `s` is no longer valid without cloning it above // if `get_int_from_input` takes `~str`, but not if it takes `&str`.

Option<T>::expect方法是您拥有的匹配语句的惯用快捷方式,如果您获得Option<T>::expect,则要返回x,如果获得Some(x),则返回失败1}}。

在没有None

的情况下返回

在Rust中,它是惯用的(遵循Haskell和OCaml等函数式语言的示例)返回值而不显式编写return语句。实际上,函数的返回值是函数中最后一个表达式的结果,除非表达式后跟一个分号(在这种情况下它返回return,也就是单位,这实际上是一个空的占位符值 - ()也是没有显式返回类型的函数返回的内容,例如()main)。

结论

我无论如何都不是Rust的伟大专家。如果您需要有关Rust的任何帮助,除了Stack Overflow之外,您还可以尝试使用irc.mozilla.org上的#rust IRC频道或Rust subreddit

答案 1 :(得分:0)

这并不是特定的生锈特性,但尝试将功能拆分为离散单元。不要混淆在终端上放置字符串的低级任务,以及从终端获取字符串以及更直接相关(并且在很大程度上依赖于实现)的请求值的任务,并验证它。当你这样做时,你应该开始做出的设计决定。

例如,你可以写下这样的东西(我还没有编译它,而且我是自己生锈的新手,所以他们可能至少有一件事是错的:))

fn validated_input_prompt<T>(prompt: ~str) {
    println(prompt);
    let res = io::stdin().read_line();

    loop {
        match res.len() {
            s if s == 0 => { continue; }

            s if s > 0 {
                match T::from_str(res) {
                    Some(t) -> {
                        return t
                    },

                    None -> {
                        println("ERROR.  Please try again.");
                        println(prompt);
                    }
                }
            }
        }
    }

}

然后将其用作:

validated_input_prompt<int>("Enter a number:")

或:

validated_input_prompt<char>("Enter a Character:")

但是,为了使后者工作,你需要为字符实现FromStr,因为(遗憾的是)生锈似乎没有默认情况下这样做。像这样的东西,但是,我再也不确定这种生锈的语法。

 use std::from_str::*;

 impl FromStr for char {

     fn from_str(s: &str) -> Option<Self> {
         match len(s) {
             x if x >= 1 => {
                 Option<char>.None
             },
             x if x == 0 => {
                 None,
             },
         }

         return s[0];
     }

 }

答案 2 :(得分:0)

telotortium的输入读取功能的变体,在输入错误时不会失败。 loop { ... }关键字比写while true { ... }更受欢迎。在这种情况下,使用return很好,因为函数提前返回。

fn int_from_input(prompt: &str) -> int {
    println(prompt);
    loop {
        match from_str::<int>(io::stdin().read_line()) {
            Some(x) => return x,
            None    => println("Oops, that was invalid input. Try again.")
        };
    }
}