简体   繁体   English

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

[英]Instantiating a struct with stdin data in Rust

I am very, very new to Rust and trying to implement some simple things to get the feel for the language. 我对Rust非常非常新,并试图实现一些简单的东西来感受语言。 Right now, I'm stumbling over the best way to implement a class-like struct that involves casting a string to an int. 现在,我绊倒了实现类似类的结构的最佳方法,该结构涉及将字符串转换为int。 I'm using a global-namespaced function and it feels wrong to my Ruby-addled brain. 我正在使用全局命名空间的函数,我觉得我的红宝石大脑感觉不对。

What's the Rustic way of doing this? 这样做的乡村方式是什么?

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();
}

This compiles and has the desired behaviour, but I am at a loss for what to do here--it's obvious I don't understand the best practices or how to implement them. 这会编译并具有所需的行为,但我不知道该做什么 - 显然我不了解最佳实践或如何实现它们。

Edit: this is 0.8 编辑:这是0.8

Here is my version of the code, which I have made more idiomatic: 这是我的代码版本,我更加惯用:

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! / format!

First, Rust is deprecating the fmt! 首先,Rust正在弃用fmt! macro, with printf -based syntax, in favor of format! 宏,基于printf的语法,支持format! , which uses syntax similar to Python format strings . ,它使用类似于Python格式字符串的语法。 The new version, Rust 0.9, will complain about the use of fmt! 新版本的Rust 0.9将抱怨使用fmt! . Therefore, you should replace fmt!("%s is %i years old.", self.name, self.age) with format!("{} is {} years old.", self.name, self.age) . 因此,您应该用format!("{} is {} years old.", self.name, self.age)替换fmt!("%s is %i years old.", self.name, self.age) format!("{} is {} years old.", self.name, self.age) However, we have a convenience macro println!(...) that means exactly the same thing as println(format!(...)) , so the most idiomatic way to write your code in Rust would be 但是,我们有一个方便的宏println!(...) ,这意味着与println(format!(...))完全相同,所以在Rust中编写代码的最惯用方法是

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

Initializing structs 初始化结构

For a simple type like Person , it is idiomatic in Rust to create instances of the type by using the struct literal syntax: 对于像Person这样的简单类型,在Rust中使用struct literal语法创建该类型的实例是惯用的:

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

In cases where you do want a constructor, Person::new is the idiomatic name for a 'default' constructor (by which I mean the most commonly used constructor) for a type Person . 如果你想要一个构造函数, Person::new是一个类型为Person的'default'构造函数(我指的是最常用的构造函数)的惯用名。 However, it would seem strange for the default constructor to require initialization from user input. 但是,默认构造函数要求从用户输入初始化似乎很奇怪。 Usually, I think you would have a person module, for example (with person::Person exported by the module). 通常,我认为你会有一个person模块,例如(由模块导出的person::Person )。 In this case, I think it would be most idiomatic to use a module-level function fn person::prompt_for_age(name: ~str) -> person::Person . 在这种情况下,我认为使用模块级函数fn person::prompt_for_age(name: ~str) -> person::Person是最惯用的。 Alternatively, you could use a static method on Person -- Person::prompt_for_age(name: ~str) . 或者,您可以在Person - Person::prompt_for_age(name: ~str)上使用静态方法。

&str vs. ~str in function parameters &str与函数参数中的~str

I've changed the signature of get_int_from_input to take a &str instead of ~str . 我已经改变了get_int_from_input的签名来取一个&str而不是~str ~str denotes a string allocated on the exchange heap -- in other words, the heap that malloc / free in C, or new / delete in C++ operate on. ~str表示在交换堆上分配的字符串 - 换句话说,C中的malloc / free或C ++中的new / delete操作的堆。 Unlike in C/C++, however, Rust enforces the requirement that values on the exchange heap can only be owned by one variable at a time. 但是,与C / C ++不同,Rust强制要求交换堆上的值一次只能由一个变量拥有。 Therefore, taking a ~str as a function parameter means that the caller of the function can't reuse the ~str argument that it passed in -- it would have to make a copy of the ~str using the .clone method. 因此,将~str作为函数参数意味着函数的调用者不能重用它传入的~str参数 - 它必须使用.clone方法复制~str

On the other hand, &str is a slice into the string, which is just a reference to a range of characters in the string, so it doesn't require a new copy of the string to be allocated when a function with a &str parameter is called. 另一方面, &str&str中的一个切片,它只是对字符串中一系列字符的引用,因此当带有&str参数的函数是一个时,它不需要分配新的字符串副本。调用。

The reason to use &str rather than ~str for prompt_message in get_int_from_input is that the function doesn't need to hold onto the message past the end of the function. get_int_from_inputprompt_message使用&str而不是~strprompt_message是该函数不需要保留超过函数末尾的消息。 It only uses the prompt message in order to print it (and println takes a &str , not a ~str ). 它只使用提示消息来打印它(而println采用&str ,而不是~str )。 Once you change the function to take &str , you can call it like get_int_from_input("Prompt") instead of get_int_from_input(~"Prompt") , which avoids the unnecessary allocation of "Prompt" on the heap (and similarly, you can avoid having to clone s in the code below): 一旦你将函数改为take &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

The Option<T>::expect method is the idiomatic shortcut for the match statement you have, where you want to either return x if you get Some(x) or fail with a message if you get None . Option<T>::expect方法是您拥有的匹配语句的惯用快捷方式,如果您获得Some(x) ,则要返回x如果获得None则返回失败。

Returning without return 回来没有return

In Rust, it is idiomatic (following the example of functional languages like Haskell and OCaml) to return a value without explicitly writing a return statement. 在Rust中,它是惯用的(遵循Haskell和OCaml等函数式语言的示例)返回值而不显式写入return语句。 In fact, the return value of a function is the result of the last expression in the function, unless the expression is followed by a semicolon (in which case it returns () , aka unit, which is essentially an empty placeholder value -- () is also what is returned by functions without an explicit return type, such as main or print_info ). 实际上,函数的返回值是函数中最后一个表达式的结果,除非表达式后跟一个分号(在这种情况下它返回() ,也就是unit,它本质上是一个空的占位符值 - ()也是没有显式返回类型的函数返回的内容,例如mainprint_info

Conclusion 结论

I'm not a great expert on Rust by any means. 无论如何,我不是Rust的伟大专家。 If you want help on anything related to Rust, you can try, in addition to Stack Overflow, the #rust IRC channel on irc.mozilla.org or the Rust subreddit . 如果您需要有关Rust的任何帮助,除了Stack Overflow之外,您还可以尝试irc.mozilla.org上的#rust IRC频道或Rust subreddit

This isn't really rust-specifc, but try to split functionality into discrete units. 这不是特定的防锈特性,而是尝试将功能分解为离散单元。 Don't mix the low-level tasks of putting strings on the terminal and getting strings from the terminal with the more directly relevant (and largely implementation dependent) tasks of requesting a value, and verify it. 不要混淆在终端上放置字符串的低级任务,并从终端获取字符串,使其具有更直接相关(并且主要依赖于实现)的请求值的任务,并验证它。 When you do that, the design decisions you should make start to arise on their own. 当你这样做时,你应该做出的设计决定就会自己出现。

For instance, you could write something like this (I haven't compiled it, and I'm new to rust myself, so they're probably at LEAST one thing wrong with this :) ). 例如,你可以写这样的东西(我没有编译它,我是新生锈的,所以他们可能至少有一件事错了:))。

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);
                    }
                }
            }
        }
    }

} }

And then use it as: 然后将其用作:

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

or: 要么:

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

BUT, to make the latter work, you'd need to implement FromStr for chars, because (sadly) rust doesn't seem to do it by default. 但是,为了使后者工作,你需要实现FromStr for chars,因为(遗憾的是)生锈似乎没有默认情况下这样做。 Something LIKE this, but again, I'm not really sure of the rust syntax for this. 像这样的东西,但同样,我不确定这个的锈蚀语法。

 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];
     }

 }

A variation of telotortium's input reading function that doesn't fail on bad input. telotortium的输入读取功能的变体,在输入错误时不会失败。 The loop { ... } keyword is preferred over writing while true { ... } . loop { ... }关键字优于写作while true { ... } In this case using return is fine since the function is returning early. 在这种情况下,使用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.")
        };
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM