这个递归连接在 Rust 中是如何工作的
How does this recursive join work in Rust
所以我用 Rust 编写了这个递归字符串连接函数,它似乎有效,但我有点困惑为什么它有效。
fn join(separator: &str, strs: &[&str]) -> String {
match strs.len() {
l if l > 1 => return strs[0].to_string() + separator + &join(separator, &strs[1..]),
l if l == 1 => return strs[0].to_string(),
_ => return "".to_string(),
}
}
所以我有一个包含 3 个字符串的数组和一个字符串分隔符。该函数将对 str &str
的引用作为其第一个参数,然后将对字符串数组 &[&str]
的引用作为第二个参数。
let j1 = ["one", "two", "three"];
println!("{}", join(", ", &j1));
- 为什么递归连接必须定义为
&join(separator, &strs[1..])
?
- 为什么
&strs[1..]
必须再次取消引用?
std::ops::Add<&'_ str>
is implemented for String
(滚动到页面底部)。 std::ops::Add<String>
不是。因此,您只能将 &'_ str
s 添加到 String
s,并且只能在右侧。您必须引用对 join
的调用,因为它使用 deref 强制将 String
转换为 &str
.
这个有点复杂,无法提供确切的证据,但用最简单的术语来说,切片(使用索引位置的范围)切片或数组将产生一个切片,IE,[T]
。由于您不能传递裸露的 [T]
s,因此您需要引用它。
更确切的原因是:
Index<i>
is impl-ed for [T]
where I: SliceIndex<[T]>
.
- 输出为
I
's output as a SliceIndex
。
1..
是一个 [std::ops::RangeFrom](https://doc.rust-lang.org/beta/std/ops/struct.RangeFrom.html)
.
SliceIndex<[T]>
is impl-ed for all [T]
on RangeFrom<usize>
.
- 输出是
[T]
而不是 &[T]
。
此外,这不是编写此函数的最惯用的方式:
pub fn join(separator: &str, strs: &[&str]) -> String {
match strs {
[last] => last.to_string(),
[current, rest @ ..] => current.to_string() + separator + &join(separator, &rest),
[] => "".to_string(),
}
}
- 为什么递归连接必须定义为
&join(separator, &strs[1..])
?
+
运算符是 std::ops::Add
的语法糖。
String
只有one Add
implementation、impl<'_> Add<&'_ str> for String
,这意味着你可以String + &str
但不能String + String
。
- 因为
join
returns 一个 String
,你必须从 &join(...)
的结果中得到一个 &str
才能使用 +
运算符。
- 为什么
&strs[1..]
必须再次取消引用?(我假设你的意思是,“为什么我不能只写 strs[1..]
? ")
- 语法
strs[1..]
表示“strs
包含的值序列从索引1到末尾”。
- 这个序列的长度,因此它的大小,在编译时是未知的,所以它不能放在堆栈上(它必须放在堆栈上才能用作函数的参数)。
- 您不是将值放在堆栈上,而是取对具有已知大小的值的引用。 (Rust 将此引用编译为“胖指针”,它由指向切片内存的指针和跟踪切片的长度值组成。)
所以我用 Rust 编写了这个递归字符串连接函数,它似乎有效,但我有点困惑为什么它有效。
fn join(separator: &str, strs: &[&str]) -> String {
match strs.len() {
l if l > 1 => return strs[0].to_string() + separator + &join(separator, &strs[1..]),
l if l == 1 => return strs[0].to_string(),
_ => return "".to_string(),
}
}
所以我有一个包含 3 个字符串的数组和一个字符串分隔符。该函数将对 str &str
的引用作为其第一个参数,然后将对字符串数组 &[&str]
的引用作为第二个参数。
let j1 = ["one", "two", "three"];
println!("{}", join(", ", &j1));
- 为什么递归连接必须定义为
&join(separator, &strs[1..])
? - 为什么
&strs[1..]
必须再次取消引用?
std::ops::Add<&'_ str>
is implemented forString
(滚动到页面底部)。std::ops::Add<String>
不是。因此,您只能将&'_ str
s 添加到String
s,并且只能在右侧。您必须引用对join
的调用,因为它使用 deref 强制将String
转换为&str
.这个有点复杂,无法提供确切的证据,但用最简单的术语来说,切片(使用索引位置的范围)切片或数组将产生一个切片,IE,
[T]
。由于您不能传递裸露的[T]
s,因此您需要引用它。
更确切的原因是:Index<i>
is impl-ed for[T]
whereI: SliceIndex<[T]>
.- 输出为
I
's output as aSliceIndex
。 1..
是一个[std::ops::RangeFrom](https://doc.rust-lang.org/beta/std/ops/struct.RangeFrom.html)
.SliceIndex<[T]>
is impl-ed for all[T]
onRangeFrom<usize>
.- 输出是
[T]
而不是&[T]
。
此外,这不是编写此函数的最惯用的方式:
pub fn join(separator: &str, strs: &[&str]) -> String {
match strs {
[last] => last.to_string(),
[current, rest @ ..] => current.to_string() + separator + &join(separator, &rest),
[] => "".to_string(),
}
}
- 为什么递归连接必须定义为
&join(separator, &strs[1..])
?+
运算符是std::ops::Add
的语法糖。String
只有oneAdd
implementation、impl<'_> Add<&'_ str> for String
,这意味着你可以String + &str
但不能String + String
。- 因为
join
returns 一个String
,你必须从&join(...)
的结果中得到一个&str
才能使用+
运算符。
- 为什么
&strs[1..]
必须再次取消引用?(我假设你的意思是,“为什么我不能只写strs[1..]
? ")- 语法
strs[1..]
表示“strs
包含的值序列从索引1到末尾”。 - 这个序列的长度,因此它的大小,在编译时是未知的,所以它不能放在堆栈上(它必须放在堆栈上才能用作函数的参数)。
- 您不是将值放在堆栈上,而是取对具有已知大小的值的引用。 (Rust 将此引用编译为“胖指针”,它由指向切片内存的指针和跟踪切片的长度值组成。)
- 语法