rust 无法将`&amp;Vec&lt;Box< str>&gt;`转换为`&amp;[&amp;str]`

wswtfjt7  于 2023-06-23  发布在  其他
关注(0)|答案(2)|浏览(172)

我的代码中有一个函数,它接受一个&[&'a str]

fn user_quicksort<'a>(list: &[&'a str]) -> Vec<&'a str>

在调用者中,我有一个Box<[Box<str>]>变量,我想传递给它;然而,Rust给了我以下错误:

error[E0308]: mismatched types
  --> src\main.rs:36:33
   |
36 |     let sorted = user_quicksort(&input);
   |                  -------------- ^^^^^^ expected `&[&str]`, found `&Box<[Box<str>]>`
   |                  |
   |                  arguments to this function are incorrect
   |
   = note: expected reference `&[&str]`
              found reference `&Box<[Box<str>]>`

我使用Box<str>而不是String的原因是因为Box<str>是不可变的,更快,更小。
我该怎么办?
下面是如何得到input

let input = fs::read_to_string("input.txt")
    .expect("Unable to read file")
    .split("\n")
    .map(|s| s.to_string().into_boxed_str())
    .collect::<Box<[Box<str>]>>();
fhity93d

fhity93d1#

错误消息显示,Rust期望得到一个对字符串引用切片(&[&str])的引用,但实际上它得到了一个对装箱字符串切片(&Box<[Box<str>]>)的引用。类型不匹配。
要解决这个问题,您需要在将Box<str>传递给函数之前将其解引用到str中。下面是你如何做到这一点:

let sorted = user_quicksort(&input.iter().map(|s| &**s).collect::<Vec<&str>>());

它的作用:

  • iter()input的元素上创建一个迭代器。
  • map(|s| &**s)获取每个Box<str>,解引用它两次(**s)以获得str,然后引用它(&),得到&str
  • collect::<Vec<&str>>()将结果&str s收集到Vec<&str>中。
  • 最后,&引用Vec<&str>,得到&[&str],这是user_quicksort所期望的。

这段代码创建了一个临时的Vec<&str>,以便将&[&str]传递给user_quicksort。重要的是要注意,&str的寿命与input相关,因此Vec<&str>不能超过input
也就是说,如果您的用例允许的话,将user_quicksort更改为采用&str s的迭代器而不是切片可能会更有效。这样可以避免创建临时向量。新的函数签名可能看起来像这样:

fn user_quicksort<'a, I: IntoIterator<Item = &'a str>>(list: I) -> Vec<&'a str> {
    // implementation
}

你可以这样称呼它:

let sorted = user_quicksort(input.iter().map(|s| &**s));

这允许更大的灵活性和潜在的更好的性能,但它是否合适取决于您的特定用例。

编辑后:

如果您正在阅读一个文件,然后将行转换为Box<str>,并且您不想创建中间Vec<&str>,则可以将input的类型更改为Vec<Box<str>>而不是Box<[Box<str>]>。通过这种方式,您可以直接从它创建一个字符串引用片段(&[&str]),而无需任何堆分配。
以下是如何调整代码:

let input = fs::read_to_string("input.txt")
    .expect("Unable to read file")
    .split("\n")
    .map(|s| s.to_string().into_boxed_str())
    .collect::<Vec<Box<str>>>();

let sorted = user_quicksort(input.iter().map(|s| &**s).collect::<&[_]>());

这将在堆栈上创建一个临时切片,而不是Vec<&str>collect::<&[_]>()函数将创建一个临时切片,您可以将其传递给user_quicksort函数。这避免了Vec<&str>的堆分配,并且应该满足您的性能要求。
请注意,这只是因为您立即使用切片,而不是试图从函数返回或存储在某个地方。切片仅在input(及其包含的Box<str>值)在作用域中且未被修改时有效。如果您需要切片的生存时间长于input或在input被修改后仍然存在,则需要使用不同的方法。

jxct1oxe

jxct1oxe2#

我不想在排序函数中强加一个重复的序列,而是让它对序列进行排序原地
这样,在调用位点,我们可以选择是否要改变原始序列,或者我们是否要保持它未排序并构建要排序的浅拷贝。
克隆序列的成本很高,因为每个Box<str>也会被克隆。那么我们的排序函数应该能够处理原始序列的Box<str>序列和浅拷贝的&str序列。在参数中使用impl AsRef<str>很有帮助,因为Box<str>&str(显然)都可以被认为是对str的引用。

fn user_quicksort(list: &mut [impl AsRef<str>]) {
    // rely on standard slice::sort_by() here
    list.sort_by(|a, b| a.as_ref().cmp(b.as_ref()));
}

fn main() {
    let mut input = std::fs::read_to_string("input.txt")
        .expect("Unable to read file")
        .split("\n")
        .map(|s| s.to_string().into_boxed_str())
        .collect::<Box<[Box<str>]>>();
    // if we want to keep the initial sequence as is,
    // then we create a shallow clone of the sequence and sort it
    let mut shallow_clone =
        input.iter().map(|s| s.as_ref()).collect::<Box<[&str]>>();
    for (s1, s2) in input.iter().zip(shallow_clone.iter()) {
        // just a check to be sure we didn't copy de str themselves
        if s1.as_ptr() != s2.as_ptr() {
            println!("!!! deep copy {:?} {:?}", s1, s2);
        }
    }
    user_quicksort(shallow_clone.as_mut());
    println!("sorted clone: {:?}", shallow_clone);
    println!("initial input: {:?}", input);
    // but if we want to sort the initial sequence,
    // no new allocation is needed
    user_quicksort(input.as_mut());
    println!("sorted input: {:?}", input);
}
/*
sorted clone: ["", "line 1", "line 2", "line 3", "line 4", "line 5"]
initial input: ["line 4", "line 1", "line 5", "line 2", "line 3", ""]
sorted input: ["", "line 1", "line 2", "line 3", "line 4", "line 5"]
*/

相关问题