指定可变长度的静态切片的方法

时间:2016-02-16 12:00:11

标签: rust type-mismatch

假设我有一个带有以下签名的函数:

fn validate(samples: &[(&str, &[Token])])

其中Token是自定义枚举。 我希望能够沿着这些方向写出一些东西:

    let samples = vec![
        ("a string", &[Token::PLUS, Token::MINUS, Token::PLUS]),
        ("another string", &[Token::MUL]),
    ];
    validate(&samples);

但像这样的代码会产生不匹配的类型编译错误:

error: mismatched types:
expected `&[(&str, &[Token])]`,
   found `&collections::vec::Vec<(&str, &[Token; 3])>`

是否有可能以某种方式将静态长度(&[Token; 3])的版本转换为静态切片(&[Token])? 换句话说,我希望能够以类似于&str的方式指定静态切片,作为某种“切片文字”。

或者我完全错了?

编辑: 简而言之,我想找到一种语法来创建一个具有静态生命周期的数组(或者至少是一个与samples向量的生命周期一样长的生命周期),并返回它的切片。

类似于字符串的工作方式,只需输入“字符串”就可以引用类型&'static str

EDIT2: @ Pablo的回答为我的特定问题提供了很好的解决方案,虽然它不是我最初的意思。

我想我想到的确切事情可能是不可能的,所以我现在只接受那个,除非我最初的想法出现更多内容。

2 个答案:

答案 0 :(得分:3)

注意:此答案在这种特殊情况下无效,因为嵌套切片指向的数组不能超过矢量,因为它们仅在各自表达式的持续时间内分配,因此切片到它们不能存储在载体中。

正确的方法是将切片提升到上层并将它们放在矢量之前,或者使用完全不同的结构,例如:嵌套VecPablo's answer中提供了所有这些示例。

你需要这样做:

let samples = vec![
    ("a string", &[Token::PLUS, Token::MINUS, Token::PLUS] as &[_]),
    ("another string", &[Token::MUL] as &[_]),
];
validate(&samples);
当目标类型已知时,

Rust会自动将对数组(&[T; n])的引用转换为切片(&[T]),但在这种情况下类型推断由于必要的deref而无法正常工作强制,所以编译器不能推断你需要一个切片而不是数组,并且不能插入适当的转换,因此你需要明确指定类型。

此外,没有&#34;静态切片&#34;。最接近的实体将是具有静态生命期的切片&'static [T],但据我记忆,情况并非如此。

答案 1 :(得分:2)

  

简而言之,我想找到一个用于创建数组的语法   静态寿命(或至少与样本一样长的寿命)   vector's one),并返回它的切片。

你想要这样的东西:

fn sliced(array: [Token; 3]) -> &'static [Token] { unimplemented!() }

所以你可以在你的例子中使用它:

let samples: Vec<(&str, &[Token])> = vec![
    ("a string", sliced([Token::PLUS, Token::MINUS, Token::PLUS])),
    // ...

但它有两个问题。第一个也是最明显的是,你不能从不接受static引用的函数中获取static引用(在这种情况下它只会返回它)。

因此,由于您希望切片至少与数组一样长,因此您要声明const / static切片(还需要const / {{1它的数组的声明),或者首先用static语句声明数组,然后制作切片。 (这是我在下面的第一个替代方案中所做的。)如果在使用let内部创建数组及其切片,则数组以vec!结束其生命,使切片无效。作为一个例子,考虑一下,由于同样的原因失败了:

vec!

fn main() { let slice; { let array: [u8; 3] = [1,2,3]; slice = &array; } } 函数的第二个问题是它的输入数组具有固定的大小,并且你想要在任意大小的数组上进行泛型工作。但是,Rust [1] 目前不支持此功能。您必须使用切片才能处理任意大小的数组。

然后,一种可能性是执行以下[playpen]:

sliced

此处有两项与您的代码相关的更改。

一,此代码依赖于通过引用数组(enum Token { PLUS, MINUS, MUL, } fn validate(samples: &[(&str, &[Token])]) { unimplemented!() } fn main() { let tokens_0 = [Token::PLUS, Token::MINUS, Token::PLUS]; let tokens_1 = [Token::MUL]; let samples: Vec<(&str, &[Token])> = vec![ ("a string", &tokens_0), ("another string", &tokens_1), ]; validate(&samples); } )作为切片([T; N])的隐式强制转换。声明&[T]samples类型要求这一点。当使用Vec<(&str, &[Token])>时,通过将引用传递给数组,从而引出适当的强制,后来满足了这一点。

二,它在使用vec!宏之前创建了Token数组,这保证了它们足够活着,可以从它创建的vec!中引用,保留这些引用Vec完成后有效。解决之前的类型不匹配后,这是必要的。

附录

或者,为方便起见,您可能更喜欢使用vec!而不是切片。请考虑以下备选方案[playpen]:

Vec

在这种情况下,元组的第二个元素上绑定的enum Token { PLUS, MINUS, MUL, } fn validate<T>(samples: &[(&str, T)]) where T: AsRef<[Token]> { let _: &[Token] = samples[0].1.as_ref(); unimplemented!() } fn main() { let samples: Vec<(&str, Vec<Token>)> = vec![ ("a string", vec![Token::PLUS, Token::MINUS, Token::PLUS]), ("another string", vec![Token::MUL]), ]; validate(&samples); } 接受您可以从中AsRef<[Token]>的任何类型,提供返回预期引用的&[Token]方法。 as_ref()就是这种类型的一个例子。

[1] “Rust目前不支持超出数组类型大小的泛型。”[source]

相关问题