创建在 Rust 中实现特征的对象向量 [英] Create vector of objects implementing a trait in Rust

查看:59
本文介绍了创建在 Rust 中实现特征的对象向量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Java 中,我试图创建对象(严格实例)的集合(向量),每个对象都实现一个接口(特征),因此我可以遍历集合并调用所有对象的方法其中.

In Java-speak, I am trying to create a collection (vector) of objects (strict instances), each one of which implements an interface (trait), so I can then iterate over the collection and call a method on all of them.

我已将其缩减为下面的一个示例文件,其中包含我希望能让您更轻松地获得答案的所有部分.

I have reduced it down to one sample file below which contains all the parts that I hope will make it easier to get answers.

// 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"),
        }
    }
}

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

That particular example fails with the message:

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 等相关.

But I have had many different problems depending on the approach I have taken, related to Sync, Sized, etc etc.

推荐答案

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

There are two little errors in the code. The first is described by the error, it tells us that the static value is not safe as it does not implement the Sync trait. It just tries to prepare for the case when the static value is manipulated from multiple threads. Here, the best solution is simply to mark the value as const. After that, there is some problem with the lifetime of &SAMPLE1 in main, can be solved by "using the let keyword to increase it's lifetime".

经过这些小修改后的代码编译,如下所示:

The code after these little modifications compiles, and looks like this:

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).

However, I see that you are not satisfied with the code as you have to create a new struct for every implementation of SampleRunner. There is another way, using lambda functions (the Rust docs just refers to them as 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")
        }
    }
}

这篇关于创建在 Rust 中实现特征的对象向量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆