简体   繁体   English

Rust 通用局部变量

[英]Rust generic local variable

From the book "Rust in Action" (Manning, currently MEAP), the final example of Chapter 2 shows a way to implement a generic function that can work on BufRead + Sized parameters:从《Rust in Action》(Manning,目前是 MEAP)一书中,第 2 章的最后一个例子展示了一种实现通用函数的方法,该函数可以处理BufRead + Sized参数:

fn process_lines<T: BufRead + Sized>(reader: T, re: Regex) {
  for line_ in reader.lines() {
    let line = line_.unwrap();
    match re.find(&line) {
        Some(_) => println!("{}", line),
        None => (),
    }
  }
}

The code that uses it looks like:使用它的代码如下所示:

let input = args.value_of("input").unwrap_or("-");
if input == "-" {
  let stdin = io::stdin();
  let reader = stdin.lock();
  process_lines(reader, re);
} else {
  let f = File::open(input).unwrap();
  let reader = BufReader::new(f);
  process_lines(reader, re);
}

I'm wondering if it is possible to declare the reader variable earlier with a generic type so the process_lines() call can be factored out?我想知道是否可以使用泛型类型提前声明reader变量,以便可以排除 process_lines() 调用?

let reader: ????;
let input = args.value_of("input").unwrap_or("-");
if input == "-" {
  let stdin = io::stdin();
  reader = stdin.lock();
} else {
  let f = File::open(input).unwrap();
  reader = BufReader::new(f);
}
process_lines(reader, re);

Or by using an (anonymous) method that would return impl BufRead + Sized ?或者通过使用将返回impl BufRead + Sized的(匿名)方法?

Something like:就像是:

fn build_reader(input: &str) -> impl BufRead + Sized {
  if input == "-" {
    let stdin = io::stdin();
    stdin.lock()  
  } else {
    let f = File::open(input).unwrap();
    BufReader::new(f)
  }
}

But compiler is not very happy, see https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0e74d3e96de0bbf61b9f61d44d305e5e但是编译器不是很开心,见https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0e74d3e96de0bbf61b9f61d44d305e5e

Edit编辑

I succeeded building a BufRead builder using the advice from https://stackoverflow.com/a/49964042/258622我使用https://stackoverflow.com/a/49964042/258622的建议成功构建了一个 BufRead 构建器

fn build_reader(input: &str) -> Box<dyn BufRead> {
  if input == "-" { 
    Box::new(BufReader::new(io::stdin()))
  } else {
    let f = File::open(input).unwrap();
    Box::new(BufReader::new(f))
  }
}

Compiler is happy, I wonder what are the consequences of not using stdin.lock() ...编译器开心,不知道不使用stdin.lock()会有什么后果...

Main now looks like:主要现在看起来像:

fn main() {
 ...
 let input = args.value_of("input").unwrap_or("-");
 let reader = build_reader(input);
 process_lines(reader, re)
}

Code that compiles: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c5218ef4a266597da6a36c21e060bcda编译代码: https : //play.rust-lang.org/? version = stable & mode = debug & edition = 2018 & gist =c5218ef4a266597da6a36c21e060bcda

Edit 2 : A more idiomatic version taking advantage of the fact that clap::ArgMatches::value_of() returns an Option<&str> :编辑 2 :利用clap::ArgMatches::value_of()返回Option<&str>的事实的更惯用的版本:

fn build_reader(input: Option<&str>) -> Box<dyn BufRead> {
  match input {
    None => Box::new(BufReader::new(io::stdin())),
    Some(filename) => {
      let f = File::open(filename).unwrap();
      Box::new(BufReader::new(f))
    }
  }
}

fn main() {
  ...
  let input = args.value_of("input");
  let reader = build_reader(input);
  process_lines(reader, re)
}

As @sshashank124 has stated, returning impl SomeTrait only means the function is to return one concrete type that implements SomeTrait .正如@sshashank124 所说,返回impl SomeTrait仅意味着该函数将返回一种实现SomeTrait具体类型 It doesn't give the function license to return heterogeneous types.它不提供返回异构类型的功能许可。

The fact that Stdin::lock takes a reference also doesn't help here. Stdin::lock需要引用的事实在这里也无济于事。 It will go out of scope as soon as the function returns or the local scope which it is in ends.一旦函数返回或它所在的本地范围结束,它就会超出范围。

Ignore the issue of Stdin::lock for a moment, you can do: Stdin::lock忽略Stdin::lock的问题,你可以这样做:

let stdin = io::stdin();
let reader: Box<dyn BufRead> = if input == "-" {
    Box::new(stdin.lock())
} else {
    let f = File::open(input).unwrap();
    Box::new(BufReader::new(f))
};
process_lines(reader, re);

Or with third party crate such as either :或与第三方箱如either

use either::Either;

let stdin = io::stdin();
let reader = if input == "-" {
    Either::Left(stdin.lock())
} else {
    let f = File::open(input).unwrap();
    Either::Right(BufReader::new(f))
};
process_lines(reader, re);

It seems trivial in this case.在这种情况下,这似乎微不足道。 But it may help you in the future.但它可能在未来对你有所帮助。

Firstly, the method approach (with impl BufRead ) nor the if/else approach will work since the types of the two BufRead implementers are different (namely StdinLock and BufReader ).首先,方法方法(使用impl BufRead )和if/else方法都行不通,因为两个BufRead实现者的类型不同(即StdinLockBufReader )。 The issue with both of these methods is that they expect a single concrete type.这两种方法的问题在于它们期望单一的具体类型。 I wouldn't worry too much about trying to factor out something as trivial as this.我不会太担心试图找出像这样微不足道的东西。

In general however, if you wish to have polymorphism at runtime, you can use the Box<dyn Trait> pattern to create a trait object.然而,一般来说,如果您希望在运行时具有多态性,您可以使用Box<dyn Trait>模式来创建一个 trait 对象。 More information on this can be found in the rust book here: https://doc.rust-lang.org/1.30.0/book/2018-edition/ch17-02-trait-objects.html关于这方面的更多信息可以在 Rust 书中找到: https : //doc.rust-lang.org/1.30.0/book/2018-edition/ch17-02-trait-objects.html

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

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