假设我有一个带有以下签名的函数:
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的回答为我的特定问题提供了很好的解决方案,虽然它不是我最初的意思。
我想我想到的确切事情可能是不可能的,所以我现在只接受那个,除非我最初的想法出现更多内容。
答案 0 :(得分:3)
注意:此答案在这种特殊情况下无效,因为嵌套切片指向的数组不能超过矢量,因为它们仅在各自表达式的持续时间内分配,因此切片到它们不能存储在载体中。
正确的方法是将切片提升到上层并将它们放在矢量之前,或者使用完全不同的结构,例如:嵌套Vec
。 Pablo'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]