I'm a beginner at Rust and I've been following this tutorial on creating a simple blockchain using Rust.
chain.rs
use byteorder::{BigEndian, ReadBytesExt};
use chrono::offset::Utc;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::io::Cursor;
// Represents the entire chain in the network
pub struct Chain {
// Actual chain
pub blocks: Vec<Block>,
}
// Represents a single block in the blockchain
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Block {
pub id: u64,
// Hash representing block
pub hash: String,
// Hash of the previous block
pub previous_hash: String,
// Time of creation in UTC
pub timestamp: i64,
// Data contained in the block
pub data: String,
// Value for hashing the block(PoW)
pub nonce: u64,
}
p2p.rs
use std::collections::HashSet;
use super::chain::{Block, Chain};
use libp2p::{
floodsub::{Floodsub, FloodsubEvent, Topic},
identity::Keypair,
mdns::{Mdns, MdnsEvent},
swarm::{NetworkBehaviourEventProcess, Swarm},
NetworkBehaviour, PeerId,
};
use log::{error, info};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use serde_json;
use tokio::sync::mpsc;
// Topics for pub/sub protocol
// Impairements: Broadcasts on each request thus extremely inefficient
pub static BLOCK_TOPIC: Lazy<Topic> = Lazy::new(|| Topic::new("blocks"));
pub static CHAIN_TOPIC: Lazy<Topic> = Lazy::new(|| Topic::new("chains"));
// Key Pair for peer identification on network
pub static KEYS: Lazy<Keypair> = Lazy::new(Keypair::generate_ed25519);
// Peer id for peer identification on network
pub static PEER_ID: Lazy<PeerId> = Lazy::new(|| PeerId::from(KEYS.public()));
...
// Defines overall NetworkBehaviour for the chain
#[derive(NetworkBehaviour)]
pub struct ChainBehaviour {
// Chain
#[behaviour(ignore)]
pub chain: Chain,
// Handles FloodSub protocol
pub floodsub: Floodsub,
// Sends response to the UnboundedReceiver
#[behaviour(ignore)]
pub init_sender: mpsc::UnboundedSender<bool>,
// Handles automatic discovery of peers on the local network
// and adds them to the topology
pub mdns: Mdns,
// Sends response to the UnboundedReceiver
#[behaviour(ignore)]
pub response_sender: mpsc::UnboundedSender<ChainResponse>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ChainResponse {
pub blocks: Vec<Block>,
pub receiver: String,
}
// Triggers chain communication for requested ID
#[derive(Debug, Deserialize, Serialize)]
pub struct LocalChainRequest {
pub from_peer_id: String,
}
// Keep states for handling incoming messages, lazy init
// and keyboard input by the client's user
pub enum EventType {
LocalChainRequest(ChainResponse),
Input(String),
Init,
}
// Implemnt FloodsubEvent for ChainBehaviour
impl NetworkBehaviourEventProcess<FloodsubEvent> for ChainBehaviour {
fn inject_event(&mut self, event: FloodsubEvent) {
if let FloodsubEvent::Message(msg) = event {
// If message is of type ChainResponse and that the message is ours,
// we execute our consensus.
if let Ok(response) = serde_json::from_slice::<ChainResponse>(&msg.data) {
if response.receiver == PEER_ID.to_string() {
info!("Response from {}:", msg.source);
response.blocks.iter().for_each(|r| info!("{:?}", r));
self.chain.blocks = self
.chain
.choose(self.chain.blocks.clone(), response.blocks);
}
} else if let Ok(response) = serde_json::from_slice::<LocalChainRequest>(&msg.data) {
// If of type LocalChainRequest, we send ChainResponse to
// initiator
info!("sending local chain to {}", msg.source.to_string());
let peer_id = response.from_peer_id;
if PEER_ID.to_string() == peer_id {
if let Err(e) = self.response_sender.send(ChainResponse {
blocks: self.chain.blocks.clone(),
receiver: msg.source.to_string(),
}) {
error!("error sending response via channel, {}", e);
};
}
} else if let Ok(block) = serde_json::from_slice::<Block>(&msg.data) {
// If of type Block, we try adding the block if valid
info!("received new block from {}", msg.source.to_string());
self.chain.try_add_block(block);
}
}
}
}
// Implement MdnsEvents for ChainBehaviour
impl NetworkBehaviourEventProcess<MdnsEvent> for ChainBehaviour {
fn inject_event(&mut self, event: MdnsEvent) {
match event {
// Add node to list of nodes when discovered
MdnsEvent::Discovered(nodes) => {
for (peer, _) in nodes {
self.floodsub.add_node_to_partial_view(peer)
}
}
// Remove node from list of nodes when TTL expires and
// address hasn't been refreshed
MdnsEvent::Expired(nodes) => {
for (peer, _) in nodes {
if !self.mdns.has_node(&peer) {
self.floodsub.remove_node_from_partial_view(&peer);
}
}
}
}
}
}
Here's my cargo.toml:
byteorder = "1"
chrono = "0.4.19"
getrandom = "0.2.3"
hex = "0.4.3"
libp2p = {version = "0.41.0", features = ['tcp-tokio', "mdns"]}
log = "0.4.14"
once_cell = "1.9.0"
oorandom = "11.1.3"
pretty_env_logger = "0.4.0"
serde = {version = "1.0.133", features = ["derive"]}
serde_json = "1.0.74"
sha2 = "0.10.0"
tokio = { version = "1.15.0", features = ["io-util", "io-std", "macros", "rt", "rt-multi-thread", "sync", "time"] }
I keep getting the following error on this struct:
error[E0277]: the trait bound `(): From<MdnsEvent>` is not satisfied
--> src/p2p.rs:30:10
|
30 | #[derive(NetworkBehaviour)]
| ^^^^^^^^^^^^^^^^ the trait `From<MdnsEvent>` is not implemented for `()`
|
= help: see issue #48214
= note: this error originates in the derive macro `NetworkBehaviour` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `(): From<FloodsubEvent>` is not satisfied
--> src/p2p.rs:30:10
|
30 | #[derive(NetworkBehaviour)]
| ^^^^^^^^^^^^^^^^ the trait `From<FloodsubEvent>` is not implemented for `()`
|
= help: see issue #48214
= note: this error originates in the derive macro `NetworkBehaviour` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`.
I tried following the suggestions from this forum and got the following error:
error[E0599]: no method named `into_inner` found for struct `OneShotHandler` in the current scope
--> src/p2p.rs:30:10
|
30 | #[derive(NetworkBehaviour)]
| ^^^^^^^^^^^^^^^^ method not found in `OneShotHandler<FloodsubProtocol, FloodsubRpc, floodsub::layer::InnerMessage>`
|
= note: this error originates in the derive macro `NetworkBehaviour` (in Nightly builds, run with -Z macro-backtrace for more info)
I also keep getting an add reference here error on #[derive(NetworkBehaviour)]. What could be causing the error and how can I fix it? I'm using rust-analyzer.
Link to the tutorial's github repo.
Link to my github repo. The entire code is rather large but should only the afforementioned errors.
This question is nearly a year old, but maybe will run into this again in the future.
I stumbled over the same issue, funnily enough while working through the same tutorial. It seems that newer versions of libp2p require you to do two things that the version used for the tutorial did not seem to require:
Specify #[behaviour(out_event="Event")] For the AppBehaviour struct
This is mentioned to be optional in the docs, but if you don't specify this then the macro will use StructName<Event>.
Implement the trait From<> for the enum Event for all events emitted by struct members of AppBehaviour, as the events emitted by the struct members are wrapped in the event enum.
I've added two new enum values for this as shown in the libp2p docs:
use crate::net::{ChainResponse};
use libp2p::{floodsub::FloodsubEvent, mdns::MdnsEvent};
pub enum Event {
ChainResponse(ChainResponse),
Floodsub(FloodsubEvent),
Mdns(MdnsEvent),
Input(String),
Init,
}
impl From<FloodsubEvent> for Event {
fn from(event: FloodsubEvent) -> Self {
Self::Floodsub(event)
}
}
impl From<MdnsEvent> for Event {
fn from(event: MdnsEvent) -> Self {
Self::Mdns(event)
}
}
Related
I'm currently building an API engine on top of Rocket, and as modern apps do, I wanted to include an automated scheduler to run async tasks (aka crons) while the Rocket API is running.
I decided to use Rocket fairings to enable the said scheduler built around tokio-schedule on the "liftoff" event.
The fact is, I set up all the required parts (logging into database, structs and traits), but I get a strange error while compiling regarding lifetimes of my fairing.
Here is a walk through of my code :
-> this is my "command" module, containing all structural parts to build and move commands (aka crons) with my application.
/// Synthetize a command execution result.
pub enum CommandResult {
SUCCESS,
ERROR(String),
SKIPPED(String),
}
/// Trait to define structs as runnable async crons with tokio_scheduler
#[async_trait]
pub trait Command: Send + Sync {
/// returns the current command name
fn get_command_name(&self) -> String;
/// returns the current command argument payload
fn get_command_args(&self) -> Option<HashMap<String, String>>;
/// returns the "cron_middleware"
fn get_cron_middleware(&self) -> CronLogMiddleware<CronLogRepository>;
/// real body for the command execution, must be overriden in impls.
async fn do_run(&self) -> Result<CommandResult>;
/// starts the command process by validating command lock, and registering an open cron log into database.
async fn begin(&self) -> Result<CronLog> {
// ...
}
/// ends the command process by releasing command lock, and registering the result of the command to an opened cron log into database.
async fn end(&self, cron_log: &CronLog, result: CommandResult) -> Result<()> {
// ...
}
/// hidden runner of commands, uses begin, end and do_run, and will be used by runner.
async fn run(&self) -> Result<()> {
// ...
}
/// generates a unique key for this command name + args, for locks purposes
fn generate_unicity_key(&self) -> String {
// ...
}
/// converts command args as a string payload
#[allow(clippy::or_fun_call)]
fn get_command_args_as_string(&self) -> String {
// ...
}
}
/// struct to move a command + its cron schedule into scheduler.
pub struct CommandHandle<T: Command + ?Sized + Send + Sync> {
pub command: Box<T>,
pub schedule: String,
}
Then, for testing purposes, I created a test command struct like this :
/// a testing purpose command
pub struct TestCommand {
pub name: String,
pub args: Option<HashMap<String, String>>,
pub cron_log_middleware: CronLogMiddleware<CronLogRepository>,
}
#[async_trait]
impl Command for TestCommand {
// accessors (get_... functions)
async fn do_run(&self) -> Result<CommandResult> {
debug!("executed !");
Ok(CommandResult::SUCCESS)
}
}
The rocket builder looks like this :
let mut sched = CronScheduler::default();
sched.add_cron(CommandHandle {
command: Box::new(TestCommand {
name: "app:test".to_string(),
args: None,
cron_log_middleware: cron_log_middleware.clone(),
}),
schedule: "*/1 * * * *".to_string(),
});
// then I add sched to rocket with .manage()
And the fairing looks like this :
/// a rocket fairing enabling async tasks (eg crons) while rocket is launching
#[derive(Default)]
pub struct CronScheduler {
crons: Vec<CommandHandle<dyn Command>>,
}
impl CronScheduler {
/// adds a cron (eg CommandHandle with a given command) to run with the scheduler.
pub fn add_cron(&mut self, cron: CommandHandle<dyn Command>) {
self.crons.push(cron);
}
}
#[rocket::async_trait]
impl Fairing for CronScheduler {
//...
v -- error is here
async fn on_liftoff(&self, _rocket: &Rocket<Orbit>) {
let sched = SchedulerBuilder::build().await;
for handle in self.crons.iter() {
let job = Job::new_cron_job_async(handle.schedule.as_str(), |_uid, _lock| {
Box::pin(async move {
handle.command.run().await;
})
})
.unwrap();
sched.add(job).await.unwrap();
}
sched.start().await.unwrap();
}
}
Aaaand I get this error :
error[E0759]: `self` has lifetime `'life0` but it needs to satisfy a `'static` lifetime requirement
--> src/core/fairings/cron_scheduler.rs:34:26
|
34 | async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
| ^^^^ this data with lifetime `'life0`...
...
39 | / Box::pin(async move {
40 | | handle.command.run().await;
41 | | })
| |__________________- ...is used and required to live as long as `'static` here
|
note: `'static` lifetime requirement introduced by the return type
--> src/core/fairings/cron_scheduler.rs:34:5
|
34 | async fn on_liftoff(&self, rocket: &Rocket<Orbit>) {
| ^^^^^ requirement introduced by this return type
...
39 | / Box::pin(async move {
40 | | handle.command.run().await;
41 | | })
| |__________________- because of this returned expression
And my cargo toml shortened:
[dependencies]
rocket = {version = "0.5.0-rc.2", features = ["json"]}
// ...
tokio-cron-scheduler = {version = "0.8.1", features = ["signal"]}
// ...
I tried different solutions, and remarked that this is the Command that is causing issues, as if I replace the content of the "Box::pin(...)" with something like a println!, nothing goes wrong.
I don't know if it's a conflict between async-trait and rocket async traits, or else, but I cannot figure it out.
Edit 1: shortened alot of code 'cause the ticket was way too long.
Edit 2: solution found thanks to the validated answer ; here is the final code patched if it can help anyone.
FTR, I did have to impl Clone myself (not using macros) and used the code of the pinned answer as a reference.
// command
pub struct CommandHandle<T: Command + ?Sized + Send + Sync> {
pub command: Arc<T>,
pub schedule: String,
}
impl<T> Clone for CommandHandle<T>
where
T: Command + ?Sized + Send + Sync,
{
fn clone(&self) -> Self {
Self {
command: self.command.clone(),
schedule: self.schedule.clone(),
}
}
}
// fairing
async fn on_liftoff(&self, _rocket: &Rocket<Orbit>) {
let sched = SchedulerBuilder::build().await;
for handle in self.crons.iter() {
let schedule = handle.schedule.clone();
let handle = handle.clone();
let job = Job::new_cron_job_async(schedule.as_str(), move |_uid, _lock| {
let handle = handle.clone();
Box::pin(async move {
handle.command.run().await.unwrap();
})
})
.unwrap();
sched.add(job).await.unwrap();
}
sched.start().await.unwrap();
}
Job::new_cron_job_async requires that your closure is 'static, but it is not since handle is a reference to self.crons.
Taking a quick look at your structure, if you use Arc instead of Box in CommandHandle, then it is easily cloned and thus can give a 'static handle to the cron job:
pub struct CommandHandle<T: Command + ?Sized + Send + Sync> {
pub command: Arc<T>, // <------------
pub schedule: String,
}
impl Clone for CommandHandle ...
for handle in self.crons.iter() {
let handle = handle.clone(); // <------------ vvvv
let job = Job::new_cron_job_async(handle.schedule.as_str(), move |_uid, _lock| {
let handle = handle.clone(); // <------------
Box::pin(async move {
handle.command.run().await;
})
})
.unwrap();
sched.add(job).await.unwrap();
}
Its hard to verify since your posted code is incomplete, but I believe you need both clones above since the function needs ownership to be 'static, but also must be FnMut in order to be called multiple times, so the handle can't be moved into the async block directly.
I'm getting blocked on what I think it's a simple problem. I'm still learning Rust, and I want to do the following:
I want to create an async trait (using async-trait) that will instantiate a DB connection instance and it will return the struct that is implementing that trait.
mongo.rs
#[async_trait]
pub trait DB {
async fn init<T, E>() -> Result<T, E>;
}
Then: favorites.rs (See the implementation of the DB trait down below)
use async_trait::async_trait;
use mongodb::Collection;
use rocket::form::FromForm;
use rocket::serde::ser::StdError;
use serde::{Deserialize, Serialize};
use std::error::Error;
use uuid::Uuid;
pub struct FavoritesDB {
collection: Collection<Favorite>,
}
#[derive(Debug)]
pub enum FavoritesError {
UnknownError(Box<dyn Error>),
}
// Conflicts with the one down below
// impl From<Box<dyn Error>> for FavoritesError {
// fn from(err: Box<dyn Error>) -> FavoritesError {
// FavoritesError::UnknownError(err)
// }
// }
impl From<Box<dyn StdError>> for FavoritesError {
fn from(err: Box<dyn StdError>) -> FavoritesError {
FavoritesError::UnknownError(err)
}
}
#[async_trait]
impl mongo::DB for FavoritesDB {
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError> {
let main_db = mongo::init::<Favorite>("Favorites").await?;
let db = FavoritesDB {
collection: main_db.collection,
};
Ok(db)
}
}
There are a list of problems with this:
1)
error[E0574]: expected struct, variant or union type, found type parameter `FavoritesDB`
--> src\db\favorites.rs:41:18
|
41 | let db = FavoritesDB {
| ^^^^^^^^^^^ not a struct, variant or union type
|
help: consider importing this struct instead
I've tried implementing From<Box<dyn tdError>> manually but it conflicts with what I have.
error[E0277]: `?` couldn't convert the error to `FavoritesError`
--> src\db\favorites.rs:40:65
|
40 | let main_db = mongo::init::<Favorite>("Favorites").await?;
| ^ the trait `From<Box<dyn StdError>>` is not implemented for `FavoritesError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<dyn StdError>>>` for `Result<FavoritesDB, FavoritesError>`
note: required by `from_residual`
--> C:\Users\asili\.rustup\toolchains\nightly-2021-11-15-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\ops\try_trait.rs:339:5
|
339 | fn from_residual(residual: R) -> Self;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider further restricting this bound
|
39 | async fn init<FavoritesDB, FavoritesError + std::convert::From<std::boxed::Box<dyn std::error::Error>>>() -> Result<FavoritesDB, FavoritesError> {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Some errors have detailed explanations: E0277, E0282, E0574.
For more information about an error, try `rustc --explain E0277`.
Just for more context, here's the DB struct and impl (Currently connecting to a local MongoDB) included in mongo.rs
pub struct Database<T> {
client: mongodb::Database,
pub collection: Collection<T>,
}
impl<T> Database<T> {
pub async fn init() -> Result<mongodb::Database, Box<dyn Error>> {
let mut client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
let db = client.database("rust-svelte");
return Ok(db);
}
}
pub async fn init<T>(collection: &str) -> Result<Database<T>, Box<dyn Error>> {
let client = Database::<T>::init().await?;
let collection = client.collection::<T>(collection);
let db = Database { client, collection };
Ok(db)
}
I've been searching for a few days over SO and the Rust community and my Google-Rust-Fu isn't good enough to spot what's the problem. Any ideas?
You've declared init to take 2 generic parameters: T and E.
This means that the code that calls init has to provide the concrete types to fill in those parameters. For example, if someone was using your library, it would be totally feasible for them to write init::<i64, ()>(), and your code should deal with that.
Because of that, when you define your impl DB for FavouritesDB, you write this:
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError>
This is no different to writing:
async fn init<T, E>() -> Result<T, E>
you've just given the type parameters different names that happen to match a struct that you're probably trying to use.
A better pattern might be an associated type. Instead of the caller deciding what the concrete types are (as is the case with generics), with associated types, the implementation of the trait on the type sets the type.
This is common with things like Iterator. Iterator has no generic parameters, but a single associated type Item. This is because it wouldn't make sense to be able to impl Iterator<String> for MyStruct and impl Iterator<i64> for MyStruct at the same time. Instead, we want to implement Iterator for a type once, and that implementation carries with it the definition of the types it expects.
So something like this (I've omitted the async-ness for brevity since it doesn't seem to be a factor here):
trait DB {
type InitOk;
type InitErr;
fn init() -> Result<Self::InitOk, Self::InitErr>;
}
impl Db for FavouritesDB {
type InitOk = FavouritesDB;
type InitErr = FavouritesError;
fn init() -> Result<Self::InitOk, Self::InitErr> {
// now you can reference FavouritesDB the struct, rather than the generic parameter
}
}
I'd also add you may want to not have the InitOk type, and just return Self, but that's up to you if you think you might want a struct to be able to create a different type.
For part 2, Rust assumes nothing (other than Sized) about generic parameters. If you want Rust to force a generic to have some property, you have to add a bound.
The compiler is telling you here that it can't use the ? operator to convert automatically, because it doesn't know that your error type has a From<Box<dyn Error>> implementation.
If you know that every error type is going to implement that, you can add it as a bound on the associated type, like this:
trait DB {
type InitOk;
type InitErr: From<Box<dyn Error>>;
// ...
}
I am trying to implement tokio::sync::RwLock because my app should re-load a configuration file if/when it changes. This involves re-assigning a Vec variable with a new one, but there could be many readers at any one time.
There is a struct called Manifest that represents a "list" of binaries available for a piece of hardware that I designed. The list is a JSON file which is parsed at startup and whenever it changes.
use notify::{watcher, RecursiveMode, Watcher};
use semver::Version;
use serde::{Deserialize, Serialize};
use std::sync::mpsc::channel;
use std::time::Duration;
use std::{fs::File, sync::Arc, thread};
use tokio::sync::RwLock;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Binary {
version: Version,
hardware: Version,
requires: Version,
file: String,
}
pub struct Manifest {
path: String,
manifest_changed: bool,
binaries: Arc<RwLock<Vec<Binary>>>,
}
impl Manifest {
pub fn new(path: String) -> Manifest {
let bins = Arc::new(RwLock::new(Vec::new()));
thread::spawn(move || async move {
let bins = Arc::clone(&bins);
let (tx, rx) = channel();
let mut watcher = watcher(tx, Duration::from_secs(10)).unwrap();
watcher.watch(path, RecursiveMode::NonRecursive).unwrap();
loop {
match rx.recv() {
Ok(event) => {
let file = File::open(path).unwrap();
let mut bins = bins.write().await;
*bins = serde_json::from_reader(file).unwrap();
}
Err(e) => println!("watch error: {:?}", e),
}
}
});
Manifest {
path: path,
manifest_changed: true,
binaries: bins,
}
}
pub async fn GetAvailableBinaries(self, hwv: Version, fwv: Version) -> Vec<Binary> {
self.binaries.read().await.to_vec();
}
}
My issue is that the compiler complains:
future cannot be sent between threads safely
future created by async block is not `Send`
help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Receiver<notify::DebouncedEvent>`rustc
manifest.rs(28, 9): future created by async block is not `Send`
manifest.rs(35, 23): has type `&std::sync::mpsc::Receiver<notify::DebouncedEvent>` which is not `Send`
manifest.rs(38, 40): await occurs here, with `rx` maybe used later
manifest.rs(44, 13): `rx` is later dropped here
mod.rs(617, 8): required by this bound in `std::thread::spawn`
I'm not sure I understand why it matters if the channel Receiver implements Send because it's being created in the closure/thread. Further that that reasoning, I really don't know what to look for.
I'm trying to implement a simple logger by implementing the log crate.
The logger should behave like this:
[1] First log message
[2] Second log message
[3] Third log message
To implement this, I have my logger struct
struct SeqLogger {
seq: i64,
}
and implement the Log trait's
fn enabled(&self, metadata: &Metadata) -> bool
fn log(&self, record: &Record)
fn flush(&self)
In log(&self, record: &Record) implementation, I would do
fn log(&self, record: &Record) {
println!("[{}] {}", self.seq, record.args());
self.seq = self.seq + 1;
}
However, the compiler complains that self is not mutable. Am I working in a right way to implement this? How can I update the state of the logger without &mut self?
It seems that the logger crate doesn't intend for loggers to have any internal state, so it forces them to be shared as immutable. This easies things a lot, in fact, since a logger should usually be shared between threads and used simultaneously, and that's not possible with & mut self.
However, there's a usual workaround: interior mutability. There's a type std::cell::Cell designed exactly for that use case: to have a immutable reference to something that should be mutable. Your internal state is simply an integer, so it's Copy, and we can just try to use Cell as-is:
extern crate log; // 0.4.5
use log::*;
use std::cell::Cell;
struct SeqLogger {
seq: Cell<i64>,
}
impl Log for SeqLogger {
fn log(&self, record: &Record) {
println!("[{}] {}", self.seq.get(), record.args());
self.seq.set(self.seq.get() + 1);
}
fn enabled(&self, metadata: &Metadata) -> bool { if false {true} else {unimplemented!()} }
fn flush(&self) { unimplemented!(); }
}
However, the compiler immediately becomes angry again:
error[E0277]: `std::cell::Cell<i64>` cannot be shared between threads safely
--> src/lib.rs:9:6
|
9 | impl Log for SeqLogger {
| ^^^ `std::cell::Cell<i64>` cannot be shared between threads safely
|
= help: within `SeqLogger`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell<i64>`
= note: required because it appears within the type `SeqLogger`
This makes sence, since, as I said before, the logger itself must be Sync, so we must guarantee that it's safe to share its contents too. At the same time, Cell is not Sync - exactly because of the interior mutability we're using here. Again, there's a usual way to fix it - Mutex:
extern crate log; // 0.4.5
use log::*;
use std::cell::Cell;
use std::sync::Mutex;
struct SeqLogger {
seq: Mutex<Cell<i64>>,
}
impl Log for SeqLogger {
fn log(&self, record: &Record) {
let seq = self.seq.lock().unwrap(); // perhaps replace this with match in production
println!("[{}] {}", seq.get(), record.args());
seq.set(seq.get() + 1);
}
fn enabled(&self, metadata: &Metadata) -> bool { if false {true} else {unimplemented!()} }
fn flush(&self) { unimplemented!(); }
}
Now it compiles just fine.
Playground with the last variant
EDIT: According to comments, we can strip one layer of indirection, since Mutex grants us both the internal mutability (sort of) and the Syncability. So we can remove the Cell and dreference the MutexGuard directly:
// --snip--
fn log(&self, record: &Record) {
let mut seq = self.seq.lock().unwrap(); // perhaps replace this with match in production
println!("[{}] {}", *seq, record.args());
*seq = *seq + 1;
}
// --snip--
And furthermore, since our state is just an integer, we can use a standard atomic type instead of Mutex. Note that AtomicI64 is unstable, so you might want to use AtomicIsize or AtomicUsize instead:
use std::sync::atomic::{AtomicIsize, Ordering};
struct SeqLogger {
seq: AtomicIsize,
}
impl Log for SeqLogger {
fn log(&self, record: &Record) {
let id = self.seq.fetch_add(1, Ordering::SeqCst);
println!("[{}] {}", id, record.args());
}
// --snip--
}
Playground
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`
But I have had many different problems depending on the approach I have taken, related to Sync, Sized, etc etc.
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")
}
}
}
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")
}
}
}