为什么我可以 return 使用“char.to_ascii_lowercase()”但不能使用“str.to_lowercase()”的自有值
Why can I return an owned value which uses 'char.to_ascii_lowercase()` but not with `str.to_lowercase()`
在接受 &str
和 returns 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
}
})
}
我在网上遇到了几个非常相似的问题,询问如何 return 已使用 .to_lowercase()
转换的拥有值,但 none 发布的解决方案对我有用.
我尽量避免使用 &char
并在我的 return 类型中坚持使用 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()
之间的区别。 Primative Type Char 下 .to_ascii_lowercase()
的文档显示:
pub fn to_ascii_lowercase(&self) -> char
而 Primative Type Str 下 .to_lowercase()
的文档显示:
pub fn to_lowercase(&self) -> String
除非我误解了,否则这两个函数似乎 return 一个拥有的值所以我不确定为什么只有 char.to_ascii_lowercase()
有效。
我在想:
如何正确 return 使用 .to_lowercase()
而不是 .to_ascsii_lowercase()
的 Impl Iterator
值?
char.to_lowercase()
和str.to_ascii_lowercase()
有什么区别?
这里的问题是 str::to_lowercase
allocates a new String
value as the lowercased version of your string, and then the str::chars
method borrows from that new String
value. (You can tell it borrows from the String
value by looking at the std::str::Chars
结构,它有一个生命周期参数,指向它正在迭代其字符的字符串。)
那么为什么会出现问题呢?好吧,由 to_lowercase
分配的 String
值是作为迭代器链的一部分创建的临时值,它又在函数作用域的末尾被丢弃(编译器的错误消息应该告诉你这一点) .因此,编译器会阻止您遇到释放后使用错误。如果它让你 return 迭代器,那么它将允许调用者从已被释放的 String
中读取,这违反了内存安全。
您使用 char::to_ascii_lowercase
的变体有效,因为您从不分配中间 String
值。因此,您最终 returning 了一个从输入借用到函数的迭代器,这是有效的,也是您需要添加生命周期参数的原因。 (否则,编译器假定 impl Trait
的生命周期是 'static
,这里不是这种情况。你的 returned 值的生命周期是 tied 到函数输入的生命周期。)
您可以通过避免分配临时 String
来解决此问题,这有望提高效率。诀窍是要意识到 char
有一个方法 char::to_lowercase
,其中 return 是一个 迭代器 在给定字符的小写等价物上,并且 不是一个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
,但它可以让你 return 一个迭代器,然后将其展平为原始迭代器 (如果你在这里使用普通的 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。
在接受 &str
和 returns 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
}
})
}
我在网上遇到了几个非常相似的问题,询问如何 return 已使用 .to_lowercase()
转换的拥有值,但 none 发布的解决方案对我有用.
我尽量避免使用 &char
并在我的 return 类型中坚持使用 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()
之间的区别。 Primative Type Char 下 .to_ascii_lowercase()
的文档显示:
pub fn to_ascii_lowercase(&self) -> char
而 Primative Type Str 下 .to_lowercase()
的文档显示:
pub fn to_lowercase(&self) -> String
除非我误解了,否则这两个函数似乎 return 一个拥有的值所以我不确定为什么只有 char.to_ascii_lowercase()
有效。
我在想:
如何正确 return 使用
.to_lowercase()
而不是.to_ascsii_lowercase()
的Impl Iterator
值?char.to_lowercase()
和str.to_ascii_lowercase()
有什么区别?
这里的问题是 str::to_lowercase
allocates a new String
value as the lowercased version of your string, and then the str::chars
method borrows from that new String
value. (You can tell it borrows from the String
value by looking at the std::str::Chars
结构,它有一个生命周期参数,指向它正在迭代其字符的字符串。)
那么为什么会出现问题呢?好吧,由 to_lowercase
分配的 String
值是作为迭代器链的一部分创建的临时值,它又在函数作用域的末尾被丢弃(编译器的错误消息应该告诉你这一点) .因此,编译器会阻止您遇到释放后使用错误。如果它让你 return 迭代器,那么它将允许调用者从已被释放的 String
中读取,这违反了内存安全。
您使用 char::to_ascii_lowercase
的变体有效,因为您从不分配中间 String
值。因此,您最终 returning 了一个从输入借用到函数的迭代器,这是有效的,也是您需要添加生命周期参数的原因。 (否则,编译器假定 impl Trait
的生命周期是 'static
,这里不是这种情况。你的 returned 值的生命周期是 tied 到函数输入的生命周期。)
您可以通过避免分配临时 String
来解决此问题,这有望提高效率。诀窍是要意识到 char
有一个方法 char::to_lowercase
,其中 return 是一个 迭代器 在给定字符的小写等价物上,并且 不是一个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
,但它可以让你 return 一个迭代器,然后将其展平为原始迭代器 (如果你在这里使用普通的 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。