【问题标题】:Best practice for returning values from for loops in rust?从 rust 中的 for 循环返回值的最佳实践?
【发布时间】:2020-10-26 20:45:08
【问题描述】:

我刚开始生锈,我已经读完紫色螃蟹书的第 1 章,但我被卡住了。
我将这个箱子用于 sha1 https://docs.rs/sha-1/0.9.1/sha1/

我想返回 m.finalize(); 的输出 我如何在 rust 中实现这一点?

在 python 中我会做这样的事情

def gen_chain():
    chain_redux = "some_Str"
    hash = None 
    hasher = Sha1()
    for i in range(0,iterations):
        hasher.update(chain_redux)
        hash = hasher.finalize() 
        hasher.reset()
        chain_redux = redux(hash)
        print(chain_redux)
    return hash

这就是我所拥有的 rust

fn calc_chain(name_table:&Vec<String> ,iterations:i32) -> GenericArray<u8, <sha1::Sha1 as Digest>::OutputSize>{

    let mut m = sha1::Sha1::new();
    let mut chain_redux = &name_table[0];
    let mut hash;
    // The above doesnt fly in rust. and i can't figure out how to make the arr! macro return a 
    // GenericArray<u8, <sha1::Sha1 as Digest>::OutputSize> I also don't know if I should. 
    for i in 0..iterations {
        println!("Curr string: {}",chain_redux);
        m.update(chain_redux);
        hash = m.finalize(); // let hash = m.finalize() here keeps me from returning it because rust 
                             // deletes it after the for loop scope
        m.reset();
        chain_redux = redux(&hash, name_table);
        println!("Redux Output: {}",chain_redux);
    };
    hash 

}

任何防锈提示/指针也将在这里受到赞赏。

【问题讨论】:

标签: rust


【解决方案1】:

Rustacean 的一个原则是尽可能多地使用迭代器,因为它们通常比普通的 for 循环更快,并且标准库有很多奇特的函数可以用迭代器解决任何问题。

不幸的是,使用迭代器的方法在这里并不理想。首先,因为它看起来并不花哨,我们必须使用丑陋的.last().unwrap() 来获取最后一个哈希值。其次,那里的map() 迭代器有副作用(它改变了mchain_redux),这对于迭代器来说不是那么惯用的,但是按照惯用方式来操作fold() 迭代器需要多几行

fn calc_chain(name_table: &Vec<String>, iterations:i32) -> GenericArray<u8, <sha1::Sha1 as Digest>::OutputSize> {
    let mut m = sha1::Sha1::new();
    let mut chain_redux = &name_table[0];
    (0..iterations)
        .map(|_| { 
            m.update(chain_redux);
            let hash = m.finalize_reset()
            chain_redux = redux(&hash, name_table);
            hash 
        })
        .last()
        .unwrap()
}

如果您还不熟悉迭代器,可以按如下方式阅读:

  1. 0..iterations 与您在 for 循环中使用的迭代器相同,如果您要求这样做,它将为您提供从 0, 1, 2, .. iterations-1 的数字
  2. .map(|_| { ... }) 是一个迭代器,当您向它询问一个值时,它将从前一个迭代器中获取一个值(即来自0..iterations 的数字),并根据给定的函数返回另一个值。在那里,每次迭代都会返回一个对应的哈希
  3. .last() 是最终开始要求前一个迭代器提供值的函数,一旦前一个迭代器没有更多值可以按需提供,它将返回最后一个值(即最后一个哈希)
  4. .unwrap() 是必需的,因为 .last() 实际上返回一个 Option,但我们相信至少有一个计算哈希,所以我们只是解开它

【讨论】:

    【解决方案2】:

    我认为这里的问题在于,在 Python 代码中,如果循环执行零次(因为 iterations = 0),您将返回一个特殊值 - 您返回 None。在 Python 中,任何变量都可以是 None,所以这很好。

    在 Rust 中,变量默认没有 None 值 - GenericArray 类型的变量不能是 None,因此您需要更加明确并使用 可以 是None,例如Option(可以是NoneSome 的类型)。比如:

    fn calc_chain(name_table:&Vec<String> ,iterations:i32) -> Option<GenericArray<u8, <sha1::Sha1 as Digest>::OutputSize>> {
        let mut m = sha1::Sha1::new();
        let mut chain_redux = &name_table[0];
        let mut maybe_hash = None;
        for i in 0..iterations {
            println!("Curr string: {}",chain_redux);
            m.update(chain_redux);
            let this_iteration_hash = m.finalize()
            m.reset();
            chain_redux = redux(&this_iteration_hash, name_table);
            println!("Redux Output: {}",chain_redux);
            maybe_hash = Some(this_iteration_hash)
        };
        maybe_hash 
    }
    

    如果您想要返回默认值而不是 Option,您可以将仅读取 maybe_hash 的行更改为使用 unwrap_orunwrap_or_else

    maybe_hash.unwrap_or_else(make_default_array)
    

    或者,如果您知道 iterations 始终大于 0,则可以通过将 maybe_hash 行更改为:

    maybe_hash.unwrap()
    

    这是断言maybe_hash 不是None,但如果有人确实iterations = 0 调用你的函数,程序会恐慌。

    【讨论】:

      【解决方案3】:

      你可以这样做:

      // Use NonZeroU32 because we always run one iteration
      fn calc_chain(name_table:&Vec<String> ,iterations:NonZeroU32) -> GenericArray<u8, <sha1::Sha1 as Digest>::OutputSize>{
          let mut m = sha1::Sha1::new();
          let mut chain_redux = &name_table[0];
          let mut hash = {
              // Run first iteration
              println!("Curr string: {}",chain_redux);
              m.update(chain_redux);
              let hash = m.finalize();
              m.reset();
              chain_redux = redux(&hash, name_table);
              println!("Redux Output: {}",chain_redux);
              hash // Scope returns first value
          };
          for i in 1..iterations.get() {
              println!("Curr string: {}",chain_redux);
              m.update(chain_redux);
              hash = m.finalize();
              m.reset();
              chain_redux = redux(&hash, name_table);
              println!("Redux Output: {}",chain_redux);
          };
          hash 
      }
      

      【讨论】:

        猜你喜欢
        • 2018-09-23
        • 2012-09-20
        • 2011-02-23
        • 1970-01-01
        • 1970-01-01
        • 2013-04-10
        • 1970-01-01
        • 1970-01-01
        • 2018-11-01
        相关资源
        最近更新 更多