Rust 为什么我可以返回使用';char.to_ascii_lowercase()`但不与'str.to_lowercase()连用`
在一个接受Rust 为什么我可以返回使用';char.to_ascii_lowercase()`但不与'str.to_lowercase()连用`,rust,lifetime,borrow-checker,Rust,Lifetime,Borrow Checker,在一个接受&str并返回impl Iterator的函数中,我试图将输入转换为小写,然后过滤并映射该小写形式的字符。我在使用str.to_lowercase()时遇到以下错误,已经有一段时间了: 原始形式的功能: pub fn decode_to_iter(cipher: &str) -> impl Iterator<Item = char> { cipher .to_lowercase() .chars() .f
&str
并返回impl Iterator
的函数中,我试图将输入转换为小写,然后过滤并映射该小写形式的字符。我在使用str.to_lowercase()
时遇到以下错误,已经有一段时间了:
原始形式的功能:
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
}
})
}
最让我困惑的是str.to_lowercase()
和char.to_ascii_lowercase()
之间的区别是什么。下面的.to_ascii_lowercase()
文档显示:
而下面的.to_lowercase()
文档显示:
除非我有误解,否则这两个函数似乎都返回一个自有值,所以我不确定为什么只有char.to\u ascii\u lowercase()
起作用
我想知道:
.to\u lowercase()
而不是.to\u ascsii\u lowercase()
的Impl迭代器
值char.to_lowercase()
和str.to_ascii_lowercase()
之间有什么区别字符串
值作为字符串的小写版本,然后该方法借用该新的字符串
值。(通过查看struct,可以看出它借用了字符串值,struct有一个生命周期参数,该参数引用它正在迭代的字符串。)
那么,这为什么会有问题呢?嗯,分配给_lowercase
的字符串
值是作为迭代器链的一部分创建的临时值,它反过来会被丢弃在函数作用域的末尾(编译器的错误消息应该告诉您这一点)。因此,编译器阻止您在释放bug后使用。如果它允许您返回迭代器,那么它将允许调用方读取已解除分配的字符串
,这违反了内存安全
使用char::to_ascii_lowercase
的变体可以工作,因为您从不分配中间String
值。因此,您将返回一个迭代器,该迭代器从函数的输入中借用,这是有效的,这也是您需要添加生存期参数的原因。(否则,编译器假定impl Trait
的生存期是静态的
,而这里不是这种情况。返回值的生存期与函数输入的生存期相关联。)
您可以通过避免分配一个临时的字符串来解决这个问题,这应该更有效率。诀窍是认识到char
有一个方法,该方法返回给定字符的小写等价物的迭代器,而不是String
。因此,您可以直接从中阅读:
pub fn decode_to_iter impl Iterator+'a{
密码
.chars()
.flat_映射(| c | c.to_小写()
.filter(| c | c.是字母数字()&c.是ascii())
.map(| c|{
如果c.是字母(){
(((b'z'-(c作为u8))+b'a')作为字符)
}否则{
C
}
})
}
这里唯一真正的技巧是使用flat_map
,这类似于普通的map
,但它允许您返回一个迭代器,然后将其展平为原始迭代器(如果您在这里使用普通的map
,您将得到一个迭代器迭代器)
话虽如此,如果您真的只关心这里的ASCII码点(由于您的过滤器
谓词),那么您就不需要完全支持Unicode的小写机制。因此,我可能会将其编写为类似于您的第二个变体,使用char::to_ascii_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 impl Iterator+'a{
密码
.chars()
.filter(| c | c.是_ascii_字母数字()
.map(| c | c.to_ascii_lowercase())
.map(| c|{
如果c.是字母(){
(((b'z'-(c作为u8))+b'a')作为字符)
}否则{
C
}
})
}
这是一个显示代码的示例。这是因为借用了字符串。您的字符串是否只包含ASCII字符?感谢@BurntSushi5的解释并指出了平面地图的使用。关于使用char
小写方法避免不必要的字符串分配,您提出了一个很好的观点,但我仍然想知道是否可以在迭代器链中从str::to_lowercase
获得临时字符串的所有权,以避免将其丢弃在函数作用域的末尾?@AC-5,这将得到回答通过
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()
}
})
}
pub fn to_ascii_lowercase(&self) -> char
pub fn to_lowercase(&self) -> String