【问题标题】:Way to specify a static slice of variable length指定可变长度的静态切片的方法
【发布时间】:2016-02-16 12:00:10
【问题描述】:

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

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])>`

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

还是我做错了?

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

类似于字符串的工作方式,只需键入“a string”即可为我提供 &amp;'static str 类型的引用。

编辑2: @Pablo 的回答为我的特定问题提供了很好的解决方案,尽管这并不是我最初的意思。

我想我想的确切的东西可能是不可能的,所以我现在就接受那个,除非出现更符合我最初想法的东西。

【问题讨论】:

    标签: rust type-mismatch


    【解决方案1】:

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

    你会想要这样的东西:

    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/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]) 隐式强制转换为切片 (&amp;[T])。这是samples 声明为Vec&lt;(&amp;str, &amp;[Token])&gt; 类型所要求的。稍后,当使用 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&lt;[Token]&gt; 接受您可以从中获取&amp;[Token] 的任何类型,提供一个返回预期引用的as_ref() 方法。 Vec&lt;Token&gt; 就是这种类型的一个例子。


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

    【讨论】:

    • 是的,我考虑过这个解决方案,但我打算用这个函数来处理比两个大得多的样本向量,这样做不太方便。
    • 您的附录是否会导致 Vec 全部移入 validate 函数?或者,因为你把它们当作_ref,所以它们留在原始样本向量中?
    • 他们留下来。 samples 是通过引用传递的,所以内部的Vec 不能移动。
    • 感谢您提供如此详细的回答!我希望可能存在一种语法,允许以与声明静态字符串类似的方式声明静态数组,但我可以使用你的附录。
    【解决方案2】:

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

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


    你需要这样做:

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

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

    此外,没有“静态切片”之类的东西。最接近的实体是具有静态生命周期的切片 &amp;'static [T],但据我所知,情况并非如此。

    【讨论】:

    • 是的,我的意思是具有静态生命周期的切片。很抱歉造成混乱,我还不知道所有的术语;)
    • 我认为我对静态切片的理解可能是错误的,我记得这个词确实经常用作“具有静态生命周期的切片”的同义词,所以有 这样的事物。但同样,在这种情况下,这些切片不会有静态生命周期,因为它们指向包含这些数组的临时变量。
    • 我试过这个,但不幸的是它不起作用,因为向量的寿命不够长:token.rs:15:23: 15:63 error: borrowed value does not live long enough token.rs:15 ("a string", &amp;[Token::PLUS, Token::MINUS, Token::PLUS] as &amp;[_])(没有as &amp;[_]也会发生同样的事情)。我想这里的大问题是如何声明一个数组以使其具有静态生命周期,但我只是引用它。类似于字符串文字的工作方式。我可以使用@Pablo 的解决方案,但它的样板代码有点太多了。
    • 是的,你是对的,我的印象是它应该可以工作,但似乎编译器会尽可能地创建局部变量,并且在复杂的嵌套表达式中,这意味着临时变量变量仅在内部表达式的持续时间内有效。因此,我认为除了@Pablo 建议的方法之外别无他法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    • 2016-09-18
    • 2020-08-29
    • 2012-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多