gpt4 book ai didi

rust - 为什么我可以返回一个使用 'char.to_ascii_lowercase()` 而不是 `str.to_lowercase()` 的拥有值

转载 作者:行者123 更新时间:2023-11-29 07:58:29 25 4
gpt4 key购买 nike

在接受 &str 的函数中并返回 impl Iterator<Item = char> ,我正在尝试将输入转换为小写,然后过滤并映射该小写形式的字符。在使用 str.to_lowercase() 时,我被以下错误卡住了一段时间。 :

  --> src/lib.rs                                                                                                                      
|
| cipher
| _____^
| |_____|
| ||
| || .to_lowercase()
| ||_______________________- temporary value created here
| | .chars()
| | .filter(|c| c.is_alphanumeric() && c.is_ascii())
... |
| | }
| | })
| |___________^ returns a value referencing data owned by the current function

函数的原始形式:

pub fn decode_to_iter(cipher: &str) -> impl Iterator<Item = char> {
cipher
.to_lowercase()
.chars()
.filter(|c| c.is_alphanumeric() && c.is_ascii())
.map(|c| {
if c.is_alphabetic() {
(((b'z' - (c as u8)) + b'a') as char)
} else {
c
}
})
}

我在网上遇到了几个非常相似的问题,这些问题是关于如何返回已使用 .to_lowercase() 转换的拥有值的。但是发布的所有解决方案都不适合我。

我试图避免使用 &char并坚持 char在我的返回类型中。

我试过使用类似 .to_owned() 的函数获得引用文献的所有权,但却空手而归。

最终,我能够使用 char.to_ascii_lowercase() 编译并通过我的测试。 .我的函数的工作版本是:

pub fn decode_to_iter<'a>(cipher: &'a str) -> impl Iterator<Item = char> + 'a {
cipher
.chars()
.filter(|c| c.is_alphanumeric() && c.is_ascii())
.map(|c| {
if c.is_alphabetic() {
(((b'z' - (c.to_ascii_lowercase() as u8)) + b'a') as char)
} else {
c.to_ascii_lowercase()
}
})
}

最让我困惑的事情之一是 str.to_lowercase() 之间的区别是什么?和 char.to_ascii_lowercase()是。 .to_ascii_lowercase() 的文档在 Primative Type Char 下显示:

pub fn to_ascii_lowercase(&self) -> char

虽然 .to_lowercase() 的文档在 Primative Type Str 下显示:

pub fn to_lowercase(&self) -> String

除非我误解了,否则这两个函数似乎都返回一个拥有的值,所以我不确定为什么只返回 char.to_ascii_lowercase()有效。

我在想:

  1. 如何正确返回 Impl Iterator使用 .to_lowercase() 的值而不是 .to_ascsii_lowercase() ?

  2. char.to_lowercase() 之间有什么区别?和 str.to_ascii_lowercase() ?

最佳答案

这里的问题是 str::to_lowercase分配一个新的 String 值作为字符串的小写版本,然后 str::chars方法借用了新的 String 值。 (您可以通过查看 std::str::Chars 结构来判断它是从 String 值借用的,该结构有一个生命周期参数,该参数指的是它正在迭代其字符的字符串。)

那么为什么会出现问题呢?好吧,由 to_lowercase 分配的 String 值是作为迭代器链的一部分创建的临时值,它又在函数作用域的末尾被丢弃(编译器的错误消息应该告诉你这个)。因此,编译器会阻止您遇到释放后使用错误。如果它允许您返回迭代器,那么它将允许调用者读取已被释放的 String,这违反了内存安全。

您使用 char::to_ascii_lowercase 的变体之所以有效,是因为您从不分配该中间 String 值。因此,您最终会返回一个从输入借用到函数的迭代器,这是有效的,也是您需要添加生命周期参数的原因。 (否则,编译器假定 impl Trait 的生命周期是 'static,但这里不是这种情况。返回值的生命周期是 tied 到函数输入的生命周期。)

您可以通过避免分配临时 String 来解决此问题,这有望提高效率。诀窍是要意识到 char 有一个方法 char::to_lowercase它在给定字符的小写等价物上返回一个迭代器,并且不是一个String。因此,您可以直接从这里读取:

pub fn decode_to_iter<'a>(cipher: &'a str) -> impl Iterator<Item = char> + 'a {
cipher
.chars()
.flat_map(|c| c.to_lowercase())
.filter(|c| c.is_alphanumeric() && c.is_ascii())
.map(|c| {
if c.is_alphabetic() {
(((b'z' - (c as u8)) + b'a') as char)
} else {
c
}
})
}

这里唯一真正的技巧是使用 flat_map,它类似于普通的 map,但它允许您返回一个迭代器,然后将其展平为原始迭代器(如果你在这里使用普通的 map,你最终会得到一个迭代器的迭代器)。

话虽如此,如果您真的只关心这里的 ASCII 代码点(由于您的 filter 谓词),那么您不需要完整的 Unicode 感知小写机制。所以我可能会用类似于你的第二个变体的方式来编写它,使用 char::to_ascii_lowercase:

pub fn decode_to_iter<'a>(cipher: &'a str) -> impl Iterator<Item = char> + 'a {
cipher
.chars()
.filter(|c| c.is_ascii_alphanumeric())
.map(|c| c.to_ascii_lowercase())
.map(|c| {
if c.is_alphabetic() {
(((b'z' - (c as u8)) + b'a') as char)
} else {
c
}
})
}

这是一个 playground link显示代码。

关于rust - 为什么我可以返回一个使用 'char.to_ascii_lowercase()` 而不是 `str.to_lowercase()` 的拥有值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57174539/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com