使用迭代器优化minigrep项目

前言

学习了迭代器,我们现在来优化minigrep项目

1. 使用迭代器优化Config结构体new方法

回顾之前的一个代码片段,在Config的new函数中包含如下代码

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

        let query = args[1].clone();
        let filename = args[2].clone();
        // CASE_SENSITIVE 环境变量出现则代表区分大小写,而并不关心起具体值
        let case_sensitive = env::var("CASE_SENSITIVE").is_err();

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

因为query、filename对于new函数没有拥有其所有权,所以使用clone方法。传进来仅仅只字符串切片,而返回的Config需要拥有字符串的所有权。针对queryfilename两个字段,必须克隆出一份,这样Config才拥有它俩的所有权。

而迭代器有用数据的所有权,这样就不需要使用切片了,我们还可以使用迭代器自带的功能进行长度检查和索引。在main函数中,env::args()产生了个迭代器,我们可以直接将迭代器传给new函数。

改造如下:

main.rs

Rust 复制代码
// env::args()就是迭代器
let config = Config::new(env::args()).unwrap_or_else(|err| {
    eprintln!("Problem parsing arguments:{}", err);
    process::exit(1);
});

lib.rs

Rust 复制代码
impl Config {
    pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enought arguments");
        }

        args.next(); // 放出第一个元素

        // 取第二个元素
        let query = match args.next() {
            Some(arg) => arg,
            None => return Err("Didn't get a query string"),
        };

        // 取第三个元素
        let filename = match args.next() {
            Some(arg) => arg,
            None => return Err("Didn't get a query string"),
        };

        // CASE_SENSITIVE 环境变量出现则代表区分大小写,而并不关心起具体值
        let case_sensitive = env::var("CASE_SENSITIVE").is_err();

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

这里的args的第一个参数不需要,手动调用。而next方法返回的是Options枚举中,使用match进行取值。

2. 使用迭代器优化搜索函数

我们还可以优化search方法,使用迭代器替换原来的逻辑,如下示例代码

Rust 复制代码
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    // let mut results = Vec::new();

    // for line in contents.lines() {
    //     if line.contains(query) {
    //         results.push(line);
    //     }
    // }

    // results

    contents
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}

使用迭代器的好处是让开发者能专注于高层的业务逻辑,而不必陷入写循环、维护临时变量的细节工作里。

search_case_insensitive函数也可以优化成如下示例代码

Rust 复制代码
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let query = query.to_lowercase();
    // let mut results = Vec::new();

    // for line in contents.lines() {
    //     if line.to_lowercase().contains(&query) {
    //         results.push(line);
    //     }
    // }
    // results

     contents
        .lines()
        .filter(|line| line.to_lowercase().contains(&query.to_lowercase()))
        .collect()
}

3. 循环vs迭代器

在上面的"2"中,如果我们把一本小说的内容放到一个String里面,搜索"the"。

rs 复制代码
test bench_search_for  ...bench: 19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ...bench: 19,234,900 ns/iter (+/- 657,200)

实际上测试的结果是,使用迭代器比手写循环还要快。

零开销抽象(Zero-Cost Abstraction)

不过重点不在这,而我们要知道的是:迭代器在rust里是一种高层次的抽象,但是它在编译后,生成的逻辑和我们手写底层代码几乎一样的。 这套机制在rust里叫做零开销抽象(Zero-Cost Abstraction),意味着

  • 使用抽象时不会引入额外的运行时开销。所以我们可以尽情使用类似迭代器这样高层次的抽象。

实际上,rust使用了很多优化,使得产出的代码非常地高效,我们可以无所畏惧地使用闭包或者迭代器,它们既能让代码在感观上保持高层次的抽象,又不会因此带来任何的运行时性能的损失。

相关推荐
爱学习的程序媛23 分钟前
【Web前端】深入解析JavaScript异步编程
开发语言·前端·javascript·ecmascript·web
梧桐16824 分钟前
马克沁机枪上阵(二):前线开辟—Claude Code 如何用一天打通前端
前端
是上好佳佳佳呀28 分钟前
【前端(一)】HTML 知识梳理:从结构到常用标签
前端·html
楚轩努力变强31 分钟前
2026 年前端进阶:端侧大模型 + WebGPU,从零打造高性能 AI 原生前端应用
前端·typescript·大模型·react·webgpu·ai原生·高性能前端
放下华子我只抽RuiKe535 分钟前
深度学习 - 01 - NLP自然语言处理基础
前端·人工智能·深度学习·神经网络·自然语言处理·矩阵·easyui
酉鬼女又兒1 小时前
零基础入门前端 第十三届蓝桥杯省赛 :水果拼盘 Flex一篇过(可用于备赛蓝桥杯Web应用开发)
前端·css·职场和发展·蓝桥杯·css3
weixin199701080161 小时前
《苏宁商品详情页前端性能优化实战》
前端·性能优化
天若有情6731 小时前
前端HTML精讲02:表单高阶用法+原生校验,告别冗余JS,提升开发效率
前端·javascript·html
蜡台1 小时前
Vue 组件通信的 12 种解决方案
前端·javascript·vue.js·props