指定可变长度静态切片的方法
Way to specify a static slice of variable length
假设我有一个具有以下签名的函数:
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
的方式指定静态切片,作为某种 "slice literal".
还是我完全错了?
编辑:
简而言之,我想找到一种语法来创建一个具有静态生命周期(或至少与 samples
向量一样长的生命周期)的数组,以及它的 returns 切片。
类似于字符串的工作方式,只需键入 "a string" 即可获得 &'static str
.
类型的引用
编辑2:
@Pablo 的回答为我的特定问题提供了很好的解决方案,尽管这并不是我最初的意思。
我想我的想法可能不太可能,所以我暂时接受那个,除非出现更符合我最初想法的东西。
In short, I would like to find a syntax that creates an array with
static lifetime (or at least a lifetime that is as long as the samples
vector's one), and returns slice of it.
你想要这样的东西:
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
引用(在这种情况下它只会 return 它) .
因此,由于您希望切片至少与数组一样长寿,因此您可以声明一个 const
/static
切片(这还需要一个 const
/static
声明它的数组),或者先用 let
语句声明数组,然后再制作切片。 (这是我在下面的第一个替代方案中所做的。)如果您在 vec!
的使用中创建数组及其切片,数组将以 vec!
结束其生命周期,使切片无效.作为一个例子,考虑这个,由于同样的原因而失败:
fn main() {
let slice;
{
let array: [u8; 3] = [1,2,3];
slice = &array;
}
}
sliced
函数的第二个问题是它的输入数组具有固定大小,而您通常希望处理任意大小的数组。但是,目前 Rust[1] 不支持此功能。您必须使用切片才能处理任意大小的数组。
那么,一种可能性是执行以下操作 [playpen]:
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!
时通过传递对数组的引用来满足,从而引发适当的强制转换。
第二,它在使用 vec!
宏之前创建了 Token
的数组,这保证了它们的寿命足以从它创建的 Vec
中引用,保持这些引用在 vec!
完成后有效。这在解决之前的类型不匹配后是必要的。
附录:
或者,为了方便起见,您可能更喜欢使用 Vec
而不是切片。考虑以下替代方案 [playpen]:
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()
方法 returns 预期的参考。 Vec<Token>
就是这种类型的一个例子。
[1] “Rust 目前不支持超过数组类型大小的泛型。” [source]
注意:此答案在这种特定情况下无效,因为嵌套切片指向的数组不能比向量长寿,因为它们仅在各自表达式的持续时间内分配,因此不能将它们的切片存储在向量中。
正确的方法是将切片提升到上层并将它们放在向量之前,或者使用完全不同的结构,例如嵌套 Vec
s。 .
中提供了所有这些示例
您需要这样做:
let samples = vec![
("a string", &[Token::PLUS, Token::MINUS, Token::PLUS] as &[_]),
("another string", &[Token::MUL] as &[_]),
];
validate(&samples);
当目标类型已知时,Rust 会自动将对数组 (&[T; n]
) 的引用转换为切片 (&[T]
),但在这种情况下,由于必要的 deref,类型推断无法正常工作强制转换,因此编译器无法推断出您需要切片而不是数组,也无法插入适当的转换,因此您需要明确指定类型。
另外,没有"static slice"这样的东西。最接近的实体是具有静态生命周期的切片,&'static [T]
,但据我所知,情况并非如此。
假设我有一个具有以下签名的函数:
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
的方式指定静态切片,作为某种 "slice literal".
还是我完全错了?
编辑:
简而言之,我想找到一种语法来创建一个具有静态生命周期(或至少与 samples
向量一样长的生命周期)的数组,以及它的 returns 切片。
类似于字符串的工作方式,只需键入 "a string" 即可获得 &'static str
.
编辑2: @Pablo 的回答为我的特定问题提供了很好的解决方案,尽管这并不是我最初的意思。
我想我的想法可能不太可能,所以我暂时接受那个,除非出现更符合我最初想法的东西。
In short, I would like to find a syntax that creates an array with static lifetime (or at least a lifetime that is as long as the samples vector's one), and returns slice of it.
你想要这样的东西:
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
引用(在这种情况下它只会 return 它) .
因此,由于您希望切片至少与数组一样长寿,因此您可以声明一个 const
/static
切片(这还需要一个 const
/static
声明它的数组),或者先用 let
语句声明数组,然后再制作切片。 (这是我在下面的第一个替代方案中所做的。)如果您在 vec!
的使用中创建数组及其切片,数组将以 vec!
结束其生命周期,使切片无效.作为一个例子,考虑这个,由于同样的原因而失败:
fn main() {
let slice;
{
let array: [u8; 3] = [1,2,3];
slice = &array;
}
}
sliced
函数的第二个问题是它的输入数组具有固定大小,而您通常希望处理任意大小的数组。但是,目前 Rust[1] 不支持此功能。您必须使用切片才能处理任意大小的数组。
那么,一种可能性是执行以下操作 [playpen]:
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!
时通过传递对数组的引用来满足,从而引发适当的强制转换。
第二,它在使用 vec!
宏之前创建了 Token
的数组,这保证了它们的寿命足以从它创建的 Vec
中引用,保持这些引用在 vec!
完成后有效。这在解决之前的类型不匹配后是必要的。
附录:
或者,为了方便起见,您可能更喜欢使用 Vec
而不是切片。考虑以下替代方案 [playpen]:
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()
方法 returns 预期的参考。 Vec<Token>
就是这种类型的一个例子。
[1] “Rust 目前不支持超过数组类型大小的泛型。” [source]
注意:此答案在这种特定情况下无效,因为嵌套切片指向的数组不能比向量长寿,因为它们仅在各自表达式的持续时间内分配,因此不能将它们的切片存储在向量中。
正确的方法是将切片提升到上层并将它们放在向量之前,或者使用完全不同的结构,例如嵌套 Vec
s。
您需要这样做:
let samples = vec![
("a string", &[Token::PLUS, Token::MINUS, Token::PLUS] as &[_]),
("another string", &[Token::MUL] as &[_]),
];
validate(&samples);
当目标类型已知时,Rust 会自动将对数组 (&[T; n]
) 的引用转换为切片 (&[T]
),但在这种情况下,由于必要的 deref,类型推断无法正常工作强制转换,因此编译器无法推断出您需要切片而不是数组,也无法插入适当的转换,因此您需要明确指定类型。
另外,没有"static slice"这样的东西。最接近的实体是具有静态生命周期的切片,&'static [T]
,但据我所知,情况并非如此。