首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Rust实现UNIX‘Rust’

用Rust实现UNIX‘Rust’
EN

Code Review用户
提问于 2016-10-19 09:12:26
回答 1查看 399关注 0票数 7

我目前正在阅读Michael的书“Linux编程接口”。这是第四章末的练习之一(我在铁锈工作,而不是C)。

tee程序在UNIX系统上的锈蚀实现。根据它的手册页

tee实用程序将标准输入复制到标准输出,在零或多个文件中复制。输出没有缓冲。

因此,运行echo "foo" | tee将打印"foo“到终端。运行echo "foo" | tee myfile会将"foo“打印到终端,并创建包含"foo”内容的文件myfile

这是一个很小的例子,但我很想知道这是否是惯用的锈菌代码,以及是否可以提高效率。

代码语言:javascript
复制
use std::env;
use std::io::{self, Read};
use std::io::prelude::Write;
use std::error::Error;
use std::fs::File;

fn main() {
    let fname = match env::args().nth(1) {
        Some(name) => name,
        None => String::from("/dev/null"),
    };

    let mut outfile = match File::create(&fname) {
        Ok(file) => file,
        Err(why) => panic!("{}", why.description()),
    };

    loop {
        let mut line = String::new();
        match io::stdin().read_to_string(&mut line) {
            Ok(_) => {}
            Err(_) => std::process::exit(0),
        }

        if line.is_empty() {
            std::process::exit(0);
        }

        println!("{}", &line);

        let _ = outfile.write(line.as_bytes());
    }
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2016-10-19 12:58:13

高级别问题:

  1. 在零个或多个文件中复制,代码只处理零个或一个文件。
  2. 输出没有缓冲。代码正在很大程度上缓冲输入。查看read_to_string:在此源中读取所有字节直到EOF,并将它们放入buf中。这意味着程序必须等待整个输入字符串存在才能写入单个字节。输入可能是千兆字节!
  3. 代码假定所有输入都是一个UTF-8编码字符串.此cat不能与图像或压缩文件一起使用任何类型的二进制数据。

更多战术问题:

  1. 不要从前奏曲中use单个项目。序曲的意义是对所有你想要的东西进行全面的导入。
  2. 检查OptionResult上存在的所有方法。例如,unwrap_or_else封装默认值大小写。
  3. 硬编码/dev/null将实现与特定的操作系统联系在一起(也许可以,因为您正在制作cat),但也与文件系统布局有关。当cat不可用时,不能使用此/dev/,这很可能是在系统紧急情况下发生的。最好是程序不写任何东西,而不是写到/dev/null
  4. 打印出错误description显示的信息比打印错误少:其他os错误Is是一个目录(os错误21)
  5. expect可能比matchunwrap_or_else更紧凑,这取决于所需的错误格式。
  6. 我不相信循环会循环,因为正如上面所讨论的,整个输入都被读取到字符串中。
  7. 如果循环执行了循环,那么为每次迭代重新分配整个字符串是效率低下的。最好清除它并重用内存。
  8. 使用println!时不需要传递引用;该宏自动引用所有参数。
  9. 不要忽视错误!let _是一个非常大的危险信号。cat几乎没有什么责任,但是正确地编写输出是相当关键的!当写入失败时,程序应该退出,例如当管道关闭时。
  10. 退出代码并非在所有情况下都有很好的定义。恐慌的时候是什么?
  11. 标准的输入和输出将在每次迭代中被反复锁定。不如一把锁有效。
代码语言:javascript
复制
use std::env;
use std::io::{self, Read, Write};
use std::fs::File;

fn main() {
    let fname = env::args().nth(1).unwrap_or_else(|| String::from("/dev/null"));

    let mut outfile = File::create(&fname).expect("Unable to create file");
    let mut line = String::new();

    loop {
        line.clear();

        io::stdin().read_to_string(&mut line).unwrap_or_else(|_| {
            std::process::exit(0)
        });

        if line.is_empty() {
            std::process::exit(0);
        }

        println!("{}", line);

        outfile.write(line.as_bytes()).expect("Unable to write!");
    }
}

很抱歉有点离题了,但问题很有趣。我可能会写得像这样:

代码语言:javascript
复制
use std::env;
use std::io::{self, Read, Write};
use std::fs::File;

const BUFFER_SIZE: usize = 8 * 1024;

fn main() {
    let mut buf = vec![0; BUFFER_SIZE];

    let stdin = io::stdin();
    let mut stdin = stdin.lock();

    let stdout = io::stdout();
    let stdout = stdout.lock();

    let mut outputs: Vec<_> = env::args().skip(1).map(|fname| {
        let f = File::create(&fname).unwrap_or_else(|e| {
            panic!("Unable to create file {}: {}", fname, e);
        });

        Box::new(f) as Box<Write>
    }).collect();

    outputs.push(Box::new(stdout));

    loop {
        let bytes = stdin.read(&mut buf).expect("Unable to read input");

        if bytes == 0 { break }

        // **Very important**, otherwise you can end up with
        // Heartbleed-esque bugs! I'm chosing to shadow `buf` to
        // deliberately prevent using it again in this loop.
        let buf = &buf[..bytes];

        for output in &mut outputs {
            output.write_all(buf).expect("Unable to write output");
        }
    }
}
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/144628

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档