【问题标题】:How do I pass modified string parameters?如何传递修改后的字符串参数?
【发布时间】:2018-07-04 06:10:31
【问题描述】:

我在chapter 12 of The Rust Programming Language,实现了不区分大小写的行搜索。两次实现相同的逻辑对我来说没有意义,所以我想如果我只是调用区分大小写的搜索函数并将参数转换为小写,那可能会起作用。它没。

这是我的非工作代码:

fn main() {
    let a = search("Waldo", "where in\nthe world\nis Waldo?");
    let b = search("waldo", "where in\nthe world\nis Waldo?");
    let c = search_case_insensitive("waldo", "where in\nthe world\nis Waldo?");

    println!("{:?}", a);
    println!("{:?}", b);
    println!("{:?}", c);
}

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
}

pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let query = query.to_lowercase();
    let contents2: &str = &contents.to_lowercase();

    search(&query, contents2)
}

我提出的大多数版本中的错误不可避免地非常类似于:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:25:28
   |
25 |     let contents2: &str = &contents.to_lowercase();
   |                            ^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
...
28 | }
   | - temporary value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 23:1...
  --> src/main.rs:23:1
   |
23 | pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

【问题讨论】:

  • 嗨!您能否解释一下您不理解错误消息的哪一部分?你对此有什么困惑?谢谢!
  • @LukasKalbertodt 并不是我不理解这个错误。显然 to_lowercase() 返回一个新字符串,因为具有翻转大小写的 unicode 字符串可能具有与原始字符串不同的长度。这个新字符串必须在函数结束时释放。然而,返回值使用了这个新字符串的一部分,这就是导致错误的原因。我不明白的是如何解决它。
  • 为什么&amp;'a str 换成contents2
  • @ljedrz 同样的原因我什至有一个内容2,我或多或少地随机尝试了一些东西,希望让错误消失。添加生命周期参数就是其中之一。删除它并不会真正改变错误。仅将其更改为 'b 会更改错误。但我需要做的是想办法让字符串比函数更有效(我不知道强制借用)或重构,所以这不再是问题了,但我有点迷茫
  • @Shepmaster 我已编辑问题以添加最少、完整的代码。您可以验证它是否会产生相同的错误。如果将函数的返回类型替换为 Vec 并将 String::from(line) 推送到向量中,它也可以按预期编译和工作,而不仅仅是 line。我想这是我一直在寻找的解决方案,尽管也许有更好的方法。在这个例子中,有人可能会争辩说,Simon Whitehead 的功能不一样,两个函数都应该有一个循环,但我希望能找到一种灵丹妙药。可能是我太乐观了

标签: string rust borrow-checker


【解决方案1】:

编辑 2:

由于您已使用 MCVE 更新了问题,并且您已表示您不在乎偏离书籍示例...这是另一个依赖于通过使用 String 进行额外分配的版本:

fn main() {
    let a = search("Waldo", "where in\nthe world\nis Waldo?");
    let b = search("waldo", "where in\nthe world\nis Waldo?");
    let c = search_case_insensitive("waldo", "where in\nthe world\nis Waldo?");

    println!("{:?}", a);
    println!("{:?}", b);
    println!("{:?}", c);
}

pub fn search<S>(query: S, contents: S) -> Vec<String> where S: Into<String> {
    let query = query.into();
    let mut results = Vec::new();

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

    results

}

pub fn search_case_insensitive<S>(query: S, contents: S) -> Vec<String> where S: Into<String> {
    let query = query.into().to_lowercase();
    let contents = contents.into().to_lowercase();

    search(query, contents)
}

这里是running in the Playground

编辑:

我意识到我从来没有真正给过你一个选择。以下是我可能会做的事情:

pub enum SearchOptions {
    CaseSensitive,
    CaseInsensitive
}

pub fn search<'a>(query: &str, contents: &'a str, options: SearchOptions) -> Vec<&'a str> {
    let mut results = Vec::new();

    for line in contents.lines() {
        let check = match options {
            SearchOptions::CaseSensitive => line.contains(query),   
            SearchOptions::CaseInsensitive => line.to_lowercase().contains(&query.to_lowercase()),   
        };
    
        if check {
            results.push(line);
        }
    }

    results
}

这大约是你可以“去重复”的程度。

原答案:

实际的问题是,当 contents 绑定到生命周期 'a 时,您正试图传递它……但您真正想要“不区分大小写”的是 query

这并不以完全相同的方式绑定到生命周期'a,因此......有效:

pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let query = query.to_lowercase();
    search(&query, contents)
}

Here it is on the playground

您仍然需要复制逻辑...因为您需要将小写查询与小写行匹配...这在本书的示例中进行了演示:

if line.to_lowercase().contains(&query) {
//      ^^^^^^^^^^^^^^ each LINE is converted to lowercase here in the insensitive search
    results.push(line);
}

“我如何停止重复逻辑?” - 好吧,它们一开始并不完全相同。我认为您的尝试并不完全是您最初所追求的(尽管很高兴得到纠正)。

【讨论】:

  • 小写查询会产生一个非常不同的答案;我看不出它是如何解决 OP 问题的。
  • @MatthieuM。我检查了书籍示例 - 这本书实际上将查询小写,因此 OP 无论如何都朝着错误的方向前进。他们引入了一个书中没有出现的错误,因为他们做了一些完全不同的事情。我没有帮助解决错误,而是尝试将它们指向正确的方向,因为如果正确遵循示例,他们正在做的事情不是问题。不管怎样,我认为“逻辑重复数据删除”不是一个可以实现的目标,因为逻辑的两条核心线需要不同。这就是我最后要指出的。
  • 我已经添加了一个示例,说明我在这种情况下可能会做什么……但它偏离了您在书中所遵循的示例。我的答案的上半部分是基于您在阅读本书时做错了什么。
  • 我知道该函数的行为与书中的不同之处在于返回的字符串都是小写的。我对重写 grep 或完全复制书中的功能并不感兴趣。但是我经常编写代码,其中具有相同签名的两个函数之一在一个简短的前奏后调用另一个函数,我认为其他人可能在函数返回字符串引用时遇到类似的问题。我希望得到的答案类似于“将两个函数的返回值更改为 Vec”或者我无法想象的更好
  • @Kaan 我只是想让它尽可能接近源材料。即使老实说我认为我的解决方案是可以接受的,我也会在这里到处使用String 实例,并在以后适当的地方将其重构为引用。 Rust 程序员通常会尽可能避免分配,因此您会看到引用被大量使用 - 但我个人会直接访问 String 实例并稍后进行重构。无论如何,我希望我的解决方案对您有所帮助。
【解决方案2】:

您对变量contents2 的生命周期引入了一个不可能的约束;通过编写&amp;'a,您试图为其分配与contents 参数相同的生命周期,但它是在search_case_insensitive 的范围内创建和销毁的,因此比contents 寿命更长。

为了使contents2search_case_insensitive 的主体寿命更长,您需要将其作为String 返回并分配给它之外的某个变量,或者通过引用将其传递给search_case_insensitive,只要它已在其他地方以 String 的形式存在。

引用The Book:

了解生命周期注释是很重要的 描述性的,而不是规定性的。这意味着参考有多长 有效是由代码决定的,而不是由注解决定的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-26
    • 2018-11-17
    • 1970-01-01
    • 2011-06-29
    • 2018-08-05
    • 1970-01-01
    相关资源
    最近更新 更多