创建在Rust中实现特征的对象的向量

时间:2016-06-30 22:19:34

标签: rust

在Java中,我试图创建一个对象(严格实例)的集合(向量),每个对象实现一个接口(trait),所以我可以迭代集合并调用所有方法他们。

我已经将其缩小为一个样本文件,其中包含了我希望能够更容易获得答案的所有部分。

// main.rs - try and compile using just "rustc main.rs"
use std::io::Result;

/// ////// Part 1
// Types used by implementors of the trait, and in creating a vector of implementors of the trai
pub struct SampleResult {
    metric: String,
}

pub trait SampleRunner {
    fn run(&self, &'static str) -> Result<SampleResult>;
}

pub struct Sample {
    name: &'static str,
    runner: &'static SampleRunner,
}

/// /////// Part 2
/// Create one specific static instance of such as Sample
static SAMPLE1: Sample = Sample {
    name: "sample",
    runner: &Sample1,
};

// need a struct to hold the run method to satisfy the trait???
struct Sample1;

// Implement the trait for this specific struct
impl SampleRunner for Sample1 {
    fn run(&self, name: &'static str) -> Result<SampleResult> {
        println!("Name: {}", name);
        Ok(SampleResult { metric: "OK".to_string() })
    }
}

/// /////// Part 3
/// Using the existing static instances of Sample struct, by creating a vector of references for them
/// then iterating over the vector and calling the trait method on each one
fn main() {
    let sample_set: Vec<&Sample> = vec![&SAMPLE1];

    for sample in sample_set.iter() {
        match sample.runner.run(sample.name) {
            Ok(result) => println!("Success"),
            _ => panic!("failed"),
        }
    }
}

该特定示例失败并显示以下消息:

error[E0277]: the trait bound `SampleRunner + 'static: std::marker::Sync` is not satisfied in `Sample`
  --> <anon>:21:1
   |
21 |   static SAMPLE1: Sample = Sample {
   |  _^ starting here...
22 | |     name: "sample",
23 | |     runner: &Sample1,
24 | | };
   | |__^ ...ending here: within `Sample`, the trait `std::marker::Sync` is not implemented for `SampleRunner + 'static`
   |
   = note: `SampleRunner + 'static` cannot be shared between threads safely
   = note: required because it appears within the type `&'static SampleRunner + 'static`
   = note: required because it appears within the type `Sample`
   = note: shared static variables must have a type that implements `Sync`

但是我遇到了许多不同的问题,具体取决于我采用的方法,与SyncSized等有关。

1 个答案:

答案 0 :(得分:1)

代码中有两个小错误。第一个是由错误描述的,它告诉我们静态值不安全,因为它没有实现Sync特性。它只是试图准备从多个线程操纵静态值的情况。在这里,最好的解决方案是将值标记为const。在那之后,main中&SAMPLE1的生命周期存在一些问题,可以通过“使用let关键字来增加它的生命周期”来解决。

这些小修改后的代码编译,看起来像这样:

use std::io::{Result};

pub struct SampleResult {
    metric: String
}

pub trait SampleRunner {
    fn run(&self, &'static str) -> Result<SampleResult>;
}

pub struct Sample {
    name: &'static str,
    runner: &'static SampleRunner
}

// Make it const
const SAMPLE1: Sample = Sample { name: "sample", runner: &Sample1 };

struct Sample1;

impl SampleRunner for Sample1 {
    fn run(&self, name: &'static str) -> Result<SampleResult> {
        println!("Name: {}", name);
        Ok(SampleResult {metric: "OK".to_string() })
    }
}

fn main() {
    // Extend the lifetime of the borrow by assigning it to a scope variable
    let borrowed_sample1 : &Sample = &SAMPLE1;
    let sample_set: Vec<&Sample> = vec!(borrowed_sample1);

    for sample in sample_set.iter() {
        match sample.runner.run(sample.name) {
        Ok(result) => println!("Success"),
        _ => panic!("failed")
        }
    }
}

但是,我发现您对代码不满意,因为您必须为SampleRunner的每个实现创建一个新结构。还有另一种方法,使用lambda函数(Rust文档只是将它们称为Closures)。

use std::io::{Result};

pub struct SampleResult {
    metric: String
}

type SampleRunner = Fn(&'static str) -> Result<SampleResult>;

pub struct Sample {
    name: &'static str,
    runner: &'static SampleRunner
}

// Still const, use a lambda as runner    
const SAMPLE1: Sample = Sample { name: "sample", runner: &|name| {
  println!("Name: {}", name);
  Ok(SampleResult {metric: "OK".to_string() })
} };

fn main() {
    let borrowed_sample1 : &Sample = &SAMPLE1;
    let sample_set: Vec<&Sample> = vec!(borrowed_sample1);

    for sample in sample_set.iter() {
        // Must parenthese sample.runner so rust knows its a field not a method
        match (sample.runner)(sample.name) {
        Ok(result) => println!("Success"),
        _ => panic!("failed")
        }
    }
}