I'm implementing a bytecode VM and am struggling referencing data stored in a parsed representation of the bytecode. As is the nature of (most) bytecode, it and thus its parsed representation remain unmodified once it's initialized. A separate Vm contains the mutable parts (stack etc.) along with that module. I made an MCVE with additional explanatory comments to illustrate the problem; it's at the bottom and on the playground. The parsed bytecode may look like this:
Module { struct_types: {"Bar": StructType::Named(["a", "b"])} }
The strings "Bar", "a", "b" are references into the bytecode and have lifetime 'b, so we also have lifetimes in the types Module<'b> and StructType<'b>.
After creating this, I will want to create struct instances, think let bar = Bar { a: (), b: () };. At least currently, each struct instance needs to hold a reference to its type, so that type might look like this:
pub struct Struct<'b> {
struct_type: &'b bytecode::StructType<'b>,
fields: Vec<Value<'b>>,
}
The values of a struct's fields may be constants whose value is stored in the bytecode, so the Value enum has a lifetime 'b as well, and that works. The problem is that I have a &'b bytecode::StructType<'b> in the first field: how do I get a reference that lives long enough? I think the reference would actually be valid long enough.
The part of the code that I suspect to be the critical one is here:
pub fn struct_type(&self, _name: &str) -> Option<&'b StructType<'b>> {
// self.struct_types.get(name)
todo!("fix lifetime problems")
}
With the commented out code, I can't get a 'b reference because the reference self.struct_types lives too short; to fix that I'd need to do &'b self which would spread virally through the code; also, most of the times I need to borrow the Vm mutably, which doesn't work if all those exclusive self references have to live long.
Introducing a separate lifetime 'm so that I could return a &'m StructType<'b> sounds like something that I could try as well, but that sounds just as viral and in addition introduces a separate lifetime I need to keep track of; being able to replace 'b with 'm (or at least only having on in each place) would be a bit nicer.
Finally this feels like something that pinning could be helpful with, but I don't understand that topic enough to make an educated guess on how that could be approached.
MCVE
#![allow(dead_code)]
mod bytecode {
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructType<'b> {
/// unit struct type; doesn't have fields
Empty,
/// tuple struct type; fields are positional
Positional(usize),
/// "normal" struct type; fields are named
Named(Vec<&'b str>),
}
impl<'b> StructType<'b> {
pub fn field_count(&self) -> usize {
match self {
Self::Empty => 0,
Self::Positional(field_count) => *field_count,
Self::Named(fields) => fields.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct Module<'b> {
struct_types: BTreeMap<&'b str, StructType<'b>>,
}
impl<'b> Module<'b> {
// here is the problem: I would like to return a reference with lifetime 'b.
// from the point I start executing instructions, I know that I won't modify
// the module (particularly, I won't add entries to the map), so I think that
// lifetime should be possible - pinning? `&'b self` everywhere? idk
pub fn struct_type(&self, _name: &str) -> Option<&'b StructType<'b>> {
// self.struct_types.get(name)
todo!("fix lifetime problems")
}
}
pub fn parse<'b>(bytecode: &'b str) -> Module<'b> {
// this would use nom to parse actual bytecode
assert_eq!(bytecode, "struct Bar { a, b }");
let bar = &bytecode[7..10];
let a = &bytecode[13..14];
let b = &bytecode[16..17];
let fields = vec![a, b];
let bar_struct = StructType::Named(fields);
let struct_types = BTreeMap::from_iter([
(bar, bar_struct),
]);
Module { struct_types }
}
}
mod vm {
use crate::bytecode::{self, StructType};
#[derive(Debug, Clone)]
pub enum Value<'b> {
Unit,
Struct(Struct<'b>),
}
#[derive(Debug, Clone)]
pub struct Struct<'b> {
struct_type: &'b bytecode::StructType<'b>,
fields: Vec<Value<'b>>,
}
impl<'b> Struct<'b> {
pub fn new(struct_type: &'b bytecode::StructType<'b>, fields: Vec<Value<'b>>) -> Self {
Struct { struct_type, fields }
}
}
#[derive(Debug, Clone)]
pub struct Vm<'b> {
module: bytecode::Module<'b>,
}
impl<'b> Vm<'b> {
pub fn new(module: bytecode::Module<'b>) -> Self {
Self { module }
}
pub fn create_struct(&mut self, type_name: &'b str) -> Value<'b> {
let struct_type: &'b StructType<'b> = self.module.struct_type(type_name).unwrap();
// just initialize the fields to something, we don't care
let fields = vec![Value::Unit; struct_type.field_count()];
let value = Value::Struct(Struct::new(struct_type, fields));
value
}
}
}
pub fn main() {
// the bytecode contains all constants needed at runtime;
// we're just interested in how struct types are handled
// obviously the real bytecode is not as human-readable
let bytecode = "struct Bar { a, b }";
// we parse that into a module that, among other things,
// has a map of all struct types
let module = bytecode::parse(bytecode);
println!("{:?}", module);
// we create a Vm that is capable of running commands
// that are stored in the module
let mut vm = vm::Vm::new(module);
// now we try to execute an instruction to create a struct value
// the instruction for this contains a reference to the type name
// stored in the bytecode.
// the struct value contains a reference to its type and holds its field values.
let value = {
let bar = &bytecode[7..10];
vm.create_struct(bar)
};
println!("{:?}", value);
}
&'b bytecode::StructType<'b> is a classic anti-pattern in Rust, it strongly indicates incorrectly annotated lifetimes. It doesn't make sense that an object would depend on some lifetime and borrowing it creates the same lifetime. That is very rare to happen on purpose.
So I suspect you need two lifetimes, which I will call 'm and 'b:
'b: the lifetime of the bytecode string, everything that references it will use &'b str.
'm: the lifetime of the Module object. Everything that references it or its contained StructType will use this lifetime.
If split into two lifetimes and adjusted correctly, it simply works:
#![allow(dead_code)]
mod bytecode {
use std::{collections::BTreeMap, iter::FromIterator};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructType<'b> {
/// unit struct type; doesn't have fields
Empty,
/// tuple struct type; fields are positional
Positional(usize),
/// "normal" struct type; fields are named
Named(Vec<&'b str>),
}
impl<'b> StructType<'b> {
pub fn field_count(&self) -> usize {
match self {
Self::Empty => 0,
Self::Positional(field_count) => *field_count,
Self::Named(fields) => fields.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct Module<'b> {
struct_types: BTreeMap<&'b str, StructType<'b>>,
}
impl<'b> Module<'b> {
// here is the problem: I would like to return a reference with lifetime 'b.
// from the point I start executing instructions, I know that I won't modify
// the module (particularly, I won't add entries to the map), so I think that
// lifetime should be possible - pinning? `&'b self` everywhere? idk
pub fn struct_type(&self, name: &str) -> Option<&StructType<'b>> {
self.struct_types.get(name)
}
}
pub fn parse<'b>(bytecode: &'b str) -> Module<'b> {
// this would use nom to parse actual bytecode
assert_eq!(bytecode, "struct Bar { a, b }");
let bar = &bytecode[7..10];
let a = &bytecode[13..14];
let b = &bytecode[16..17];
let fields = vec![a, b];
let bar_struct = StructType::Named(fields);
let struct_types = BTreeMap::from_iter([(bar, bar_struct)]);
Module { struct_types }
}
}
mod vm {
use crate::bytecode::{self, StructType};
#[derive(Debug, Clone)]
pub enum Value<'b, 'm> {
Unit,
Struct(Struct<'b, 'm>),
}
#[derive(Debug, Clone)]
pub struct Struct<'b, 'm> {
struct_type: &'m bytecode::StructType<'b>,
fields: Vec<Value<'b, 'm>>,
}
impl<'b, 'm> Struct<'b, 'm> {
pub fn new(struct_type: &'m bytecode::StructType<'b>, fields: Vec<Value<'b, 'm>>) -> Self {
Struct {
struct_type,
fields,
}
}
}
#[derive(Debug, Clone)]
pub struct Vm<'b> {
module: bytecode::Module<'b>,
}
impl<'b> Vm<'b> {
pub fn new(module: bytecode::Module<'b>) -> Self {
Self { module }
}
pub fn create_struct(&mut self, type_name: &str) -> Value<'b, '_> {
let struct_type: &StructType<'b> = self.module.struct_type(type_name).unwrap();
// just initialize the fields to something, we don't care
let fields = vec![Value::Unit; struct_type.field_count()];
let value = Value::Struct(Struct::new(struct_type, fields));
value
}
}
}
pub fn main() {
// the bytecode contains all constants needed at runtime;
// we're just interested in how struct types are handled
// obviously the real bytecode is not as human-readable
let bytecode = "struct Bar { a, b }";
// we parse that into a module that, among other things,
// has a map of all struct types
let module = bytecode::parse(bytecode);
println!("{:?}", module);
// we create a Vm that is capable of running commands
// that are stored in the module
let mut vm = vm::Vm::new(module);
// now we try to execute an instruction to create a struct value
// the instruction for this contains a reference to the type name
// stored in the bytecode.
// the struct value contains a reference to its type and holds its field values.
let value = {
let bar = &bytecode[7..10];
vm.create_struct(bar)
};
println!("{:?}", value);
}
Module { struct_types: {"Bar": Named(["a", "b"])} }
Struct(Struct { struct_type: Named(["a", "b"]), fields: [Unit, Unit] })
It can further be simplified, however, due to the fact that 'm is connected to 'b, and therefore everything that depends on 'm automatically also has access to 'b objects, because 'b is guaranteed to outlive 'm.
Therefore, let's introduce 'a, which we will now use inside of the vm mod to reference anything from the bytecode mod. This will further allow lifetime elysion to happen at a couple of points, simplifying the code even further:
#![allow(dead_code)]
mod bytecode {
use std::{collections::BTreeMap, iter::FromIterator};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructType<'b> {
/// unit struct type; doesn't have fields
Empty,
/// tuple struct type; fields are positional
Positional(usize),
/// "normal" struct type; fields are named
Named(Vec<&'b str>),
}
impl<'b> StructType<'b> {
pub fn field_count(&self) -> usize {
match self {
Self::Empty => 0,
Self::Positional(field_count) => *field_count,
Self::Named(fields) => fields.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct Module<'b> {
struct_types: BTreeMap<&'b str, StructType<'b>>,
}
impl<'b> Module<'b> {
// here is the problem: I would like to return a reference with lifetime 'b.
// from the point I start executing instructions, I know that I won't modify
// the module (particularly, I won't add entries to the map), so I think that
// lifetime should be possible - pinning? `&'b self` everywhere? idk
pub fn struct_type(&self, name: &str) -> Option<&StructType<'b>> {
self.struct_types.get(name)
}
}
pub fn parse<'b>(bytecode: &'b str) -> Module<'b> {
// this would use nom to parse actual bytecode
assert_eq!(bytecode, "struct Bar { a, b }");
let bar = &bytecode[7..10];
let a = &bytecode[13..14];
let b = &bytecode[16..17];
let fields = vec![a, b];
let bar_struct = StructType::Named(fields);
let struct_types = BTreeMap::from_iter([(bar, bar_struct)]);
Module { struct_types }
}
}
mod vm {
use crate::bytecode::{self, StructType};
#[derive(Debug, Clone)]
pub enum Value<'a> {
Unit,
Struct(Struct<'a>),
}
#[derive(Debug, Clone)]
pub struct Struct<'a> {
struct_type: &'a bytecode::StructType<'a>,
fields: Vec<Value<'a>>,
}
impl<'a> Struct<'a> {
pub fn new(struct_type: &'a bytecode::StructType, fields: Vec<Value<'a>>) -> Self {
Struct {
struct_type,
fields,
}
}
}
#[derive(Debug, Clone)]
pub struct Vm<'a> {
module: bytecode::Module<'a>,
}
impl<'a> Vm<'a> {
pub fn new(module: bytecode::Module<'a>) -> Self {
Self { module }
}
pub fn create_struct(&mut self, type_name: &str) -> Value {
let struct_type: &StructType = self.module.struct_type(type_name).unwrap();
// just initialize the fields to something, we don't care
let fields = vec![Value::Unit; struct_type.field_count()];
let value = Value::Struct(Struct::new(struct_type, fields));
value
}
}
}
pub fn main() {
// the bytecode contains all constants needed at runtime;
// we're just interested in how struct types are handled
// obviously the real bytecode is not as human-readable
let bytecode = "struct Bar { a, b }";
// we parse that into a module that, among other things,
// has a map of all struct types
let module = bytecode::parse(bytecode);
println!("{:?}", module);
// we create a Vm that is capable of running commands
// that are stored in the module
let mut vm = vm::Vm::new(module);
// now we try to execute an instruction to create a struct value
// the instruction for this contains a reference to the type name
// stored in the bytecode.
// the struct value contains a reference to its type and holds its field values.
let value = {
let bar = &bytecode[7..10];
vm.create_struct(bar)
};
println!("{:?}", value);
}
Module { struct_types: {"Bar": Named(["a", "b"])} }
Struct(Struct { struct_type: Named(["a", "b"]), fields: [Unit, Unit] })
Fun fact: This is now one of the rare cases where we legitimately have to use &'a bytecode::StructType<'a>, so take my opening statement with a grain of salt, and you were kind of right all along :)
The crazy thing is if we then rename 'a to 'b to be consistent with your original code, we get almost your code with only some minor differences:
#![allow(dead_code)]
mod bytecode {
use std::{collections::BTreeMap, iter::FromIterator};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructType<'b> {
/// unit struct type; doesn't have fields
Empty,
/// tuple struct type; fields are positional
Positional(usize),
/// "normal" struct type; fields are named
Named(Vec<&'b str>),
}
impl<'b> StructType<'b> {
pub fn field_count(&self) -> usize {
match self {
Self::Empty => 0,
Self::Positional(field_count) => *field_count,
Self::Named(fields) => fields.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct Module<'b> {
struct_types: BTreeMap<&'b str, StructType<'b>>,
}
impl<'b> Module<'b> {
// here is the problem: I would like to return a reference with lifetime 'b.
// from the point I start executing instructions, I know that I won't modify
// the module (particularly, I won't add entries to the map), so I think that
// lifetime should be possible - pinning? `&'b self` everywhere? idk
pub fn struct_type(&self, name: &str) -> Option<&StructType<'b>> {
self.struct_types.get(name)
}
}
pub fn parse<'b>(bytecode: &'b str) -> Module<'b> {
// this would use nom to parse actual bytecode
assert_eq!(bytecode, "struct Bar { a, b }");
let bar = &bytecode[7..10];
let a = &bytecode[13..14];
let b = &bytecode[16..17];
let fields = vec![a, b];
let bar_struct = StructType::Named(fields);
let struct_types = BTreeMap::from_iter([(bar, bar_struct)]);
Module { struct_types }
}
}
mod vm {
use crate::bytecode::{self, StructType};
#[derive(Debug, Clone)]
pub enum Value<'b> {
Unit,
Struct(Struct<'b>),
}
#[derive(Debug, Clone)]
pub struct Struct<'b> {
struct_type: &'b bytecode::StructType<'b>,
fields: Vec<Value<'b>>,
}
impl<'b> Struct<'b> {
pub fn new(struct_type: &'b bytecode::StructType, fields: Vec<Value<'b>>) -> Self {
Struct {
struct_type,
fields,
}
}
}
#[derive(Debug, Clone)]
pub struct Vm<'b> {
module: bytecode::Module<'b>,
}
impl<'b> Vm<'b> {
pub fn new(module: bytecode::Module<'b>) -> Self {
Self { module }
}
pub fn create_struct(&mut self, type_name: &str) -> Value {
let struct_type: &StructType = self.module.struct_type(type_name).unwrap();
// just initialize the fields to something, we don't care
let fields = vec![Value::Unit; struct_type.field_count()];
let value = Value::Struct(Struct::new(struct_type, fields));
value
}
}
}
pub fn main() {
// the bytecode contains all constants needed at runtime;
// we're just interested in how struct types are handled
// obviously the real bytecode is not as human-readable
let bytecode = "struct Bar { a, b }";
// we parse that into a module that, among other things,
// has a map of all struct types
let module = bytecode::parse(bytecode);
println!("{:?}", module);
// we create a Vm that is capable of running commands
// that are stored in the module
let mut vm = vm::Vm::new(module);
// now we try to execute an instruction to create a struct value
// the instruction for this contains a reference to the type name
// stored in the bytecode.
// the struct value contains a reference to its type and holds its field values.
let value = {
let bar = &bytecode[7..10];
vm.create_struct(bar)
};
println!("{:?}", value);
}
Module { struct_types: {"Bar": Named(["a", "b"])} }
Struct(Struct { struct_type: Named(["a", "b"]), fields: [Unit, Unit] })
So the actual fix for your original code is as follows:
4c4
< use std::collections::BTreeMap;
---
> use std::{collections::BTreeMap, iter::FromIterator};
36,38c36,37
< pub fn struct_type(&self, _name: &str) -> Option<&'b StructType<'b>> {
< // self.struct_types.get(name)
< todo!("fix lifetime problems")
---
> pub fn struct_type(&self, name: &str) -> Option<&StructType<'b>> {
> self.struct_types.get(name)
73c72
< pub fn new(struct_type: &'b bytecode::StructType<'b>, fields: Vec<Value<'b>>) -> Self {
---
> pub fn new(struct_type: &'b bytecode::StructType, fields: Vec<Value<'b>>) -> Self {
91,92c90,91
< pub fn create_struct(&mut self, type_name: &'b str) -> Value<'b> {
< let struct_type: &'b StructType<'b> = self.module.struct_type(type_name).unwrap();
---
> pub fn create_struct(&mut self, type_name: &str) -> Value {
> let struct_type: &StructType = self.module.struct_type(type_name).unwrap();
I hope deriving them step by step made it somewhat clear why those changes are necessary.
The blockchain struct definition, It defines a type and i use the type
pub struct Blockchain<T = SledDb> {
pub storage: T,
pub chain: Vec<Block>,
pub tip: Arc<RwLock<String>>,
pub height: AtomicUsize,
pub mempool: Mempool,
pub wallet: Wallet,
pub accounts: Account,
pub stakes: Stake,
pub validators: Validator,
}
This code is checking if stake is valid.The code for mining a block, the error is immited by is_staking_valid function. I don't know what type its asking for since i already specified one.
impl<T: Storage> Blockchain<T> {
pub fn is_staking_valid(
balance: u64,
difficulty: u32,
timestamp: i64,
prev_hash: &String,
address: &String,
) -> bool {
let base = BigUint::new(vec![2]);
let balance_diff_mul = base.pow(256) * balance as u32;
let balance_diff = balance_diff_mul / difficulty as u64;
let data_str = format!("{}{}{}", prev_hash, address, timestamp.to_string());
let sha256_hash = digest(data_str);
let staking_hash = BigUint::parse_bytes(&sha256_hash.as_bytes(), 16).expect("msg");
staking_hash <= balance_diff
}
pub fn mine_block(&mut self, data: &str) -> Option<Block> {
if self.mempool.transactions.len() < 2 {
info!("Skipping mining because no transaction in mempool");
return None;
}
let balance = self
.stakes
.get_balance(&self.wallet.get_public_key())
.clone();
let difficulty = self.get_difficulty();
info!("New block mining initialized with difficulty {}", difficulty);
let timestamp = Utc::now().timestamp();
let prev_hash = self.chain.last().unwrap().hash.clone();
let address = self.wallet.get_public_key();
if Blockchain::is_staking_valid(balance, difficulty, timestamp, &prev_hash, &address){
let block = self.create_block(&data, timestamp);
self.storage.update_blocks(&prev_hash, &block, self.height.load(Ordering::Relaxed));
Some(block)
} else {
None
}
}
}
Please find the compiler error below
error[E0282]: type annotations needed
--> src/blocks/chain.rs:173:12
|
173 | if Blockchain::is_staking_valid(balance, difficulty, timestamp, &prev_hash, &address){
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T`
For more information about this error, try `rustc --explain E0282`.
Minimized example:
pub struct Blockchain<T> {
pub storage: T,
}
impl<T> Blockchain<T> {
pub fn is_staking_valid() {
todo!()
}
pub fn mine_block(&mut self) {
Blockchain::is_staking_valid();
}
}
Playground
The reason for this error is that Blockchain::<T1>::is_staking_valid and Blockchain::<T2>::is_staking_valid are, as well as compiler is concerned, two separate, entirely unrelated functions. Yes, they have the same code, and yes, they will be deduplicated by the optimizer, but this doesn't have to be the case - e.g., if this function used some associated item available on T:
trait Stakable {
const IS_VALID: bool;
}
impl Stakable for () {
const IS_VALID: bool = false;
}
impl Stakable for i32 {
const IS_VALID: bool = true;
}
struct Blockchain<T> {
pub _storage: T,
}
impl<T: Stakable> Blockchain<T> {
fn validate() {
if !T::IS_VALID {
panic!("Type is not valid");
}
}
}
fn main() {
// This panics - we catch this panic and show that it has indeed happened
std::panic::catch_unwind(|| Blockchain::<()>::validate()).unwrap_err();
// This executes successfully
Blockchain::<i32>::validate();
}
Playground
Because of the possible ambiguity, compiler refuses to choose by itself and forces you to make the selection explicitly.
So, you have several possible ways to go:
Make is_staking_valid a free function, instead of associated function of Blockchain. In this case, it won't be able to depend on Blockchain's type parameter, therefore the call will be unambiguous.
Call Self::is_staking_valid instead of Blockchain::is_staking_valid. In this case, Self will be replaced with Blockchain::<T>, with T taken from the currently executed method; this will, again, resolve ambiguity.
Make is_staking_valid a method on Blockchain, i.e. make it receive &self, and call it via self.is_staking_valid().
Not recommended, but still possible, - make is_staking_valid an associated function on Blockchain<T> for some specific T, e.g.:
pub struct Blockchain<T> {
pub storage: T,
}
impl Blockchain<()> {
// Note - no free type parameters here!
pub fn is_staking_valid() {
todo!()
}
}
impl<T> Blockchain<T> {
pub fn mine_block(&mut self) {
// Here, `Blockchain` is `Blockchain::<()>` - the method is set
Blockchain::is_staking_valid();
}
}
I'm writing tests using mock functions, controlling the return value among the tests with a Mutex:
#[macro_use]
extern crate lazy_static;
#[cfg(test)]
pub use mock::*;
#[cfg(not(test))]
pub use real::*;
mod real {
pub fn say_hello(_name: String) -> String {
unimplemented!()
}
}
/// simulate multiple uses, replace `real` in test.
mod mock {
use std::sync::*;
lazy_static! {
pub static ref LOCK: Mutex<bool> = Mutex::new(true);
pub static ref HELLO_VALUE: Mutex<String> = Mutex::new(String::default());
}
pub fn say_hello(_name: String) -> String {
use std::ops::Deref;
HELLO_VALUE.lock().unwrap().deref().clone()
}
pub fn set_hello_return_value(rtn: String) -> MutexGuard<bool> {
let lock = LOCK.lock().unwrap();
let mut value = HELLO_VALUE.lock().unwrap();
*value = rtn;
lock
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test1() {
// repeated block begin--------------------------
let _lock = LOCK.lock().unwrap();
let mut value = HELLO_VALUE.lock().unwrap();
*value = "Hello Tom!".to_string(); // just this line is different from test2
drop(value);
// repeat block end--------------------------
assert_eq!("Hello Tom!", say_hello("".to_string()));
}
#[test]
fn test2() {
// repeated block begin--------------------------
let _lock = LOCK.lock().unwrap();
let mut value = HELLO_VALUE.lock().unwrap();
*value = "Hello Jack!".to_string(); // just this line is different from test1
drop(value);
// repeat block end--------------------------
assert_eq!("Hello Jack!", say_hello("".to_string()));
}
#[test]
fn test_simplified_but_not_work() {
let _lock = set_hello_return_value("Hello Mark!".to_string());
assert_eq!("Hello Mark!", say_hello("".to_string()));
}
}
You can see the repeat block that I want to simplify. I made a function set_hello_return_value but the compiler complained:
error[E0106]: missing lifetime specifier
--> src/main.rs:28:51
|
28 | pub fn set_hello_return_value(rtn: String) -> MutexGuard<bool> {
| ^^^^^^^^^^^^^^^^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
= help: consider giving it an explicit bounded or 'static lifetime
Please help me to correct it.
Read the complete error message:
consider giving it an explicit bounded or 'static lifetime
Doing so works:
pub fn set_hello_return_value(rtn: String) -> MutexGuard<'static, bool> {
let lock = LOCK.lock().unwrap();
let mut value = HELLO_VALUE.lock().unwrap();
*value = rtn;
lock
}
I'd probably not return the guard at all, however:
pub fn with_hello_return_value<S, F>(rtn: S, f: F)
where
S: Into<String>,
F: FnOnce(),
{
let _lock = LOCK.lock().unwrap();
*HELLO_VALUE.lock().unwrap() = rtn.into();
f()
}
#[test]
fn test_simplified() {
with_hello_return_value("Hello Mark!", || {
assert_eq!("Hello Mark!", say_hello("".to_string()));
});
}
Honestly, I wouldn't do any of this as conditional compilation is overkill. If you need to test components of your system separately, they shouldn't know about each other to start with; they should be dependency-injected. This has the additional benefit that each test can inject its own value, preserving the multithreaded nature of the tests.
fn thing_that_uses_say_hello<G>(greeter: &G, name: &str) -> String
where
G: Greeting,
{
greeter.say_hello(name.into())
}
trait Greeting {
fn say_hello(&self, name: &str) -> String;
}
struct RealGreeting;
impl Greeting for RealGreeting {
fn say_hello(&self, name: &str) -> String {
format!("Hello, {}", name)
}
}
#[cfg(test)]
mod test {
use super::*;
use std::cell::RefCell;
struct MockGreeting<'a> {
called_with: RefCell<Vec<String>>,
value: &'a str,
}
impl<'a> MockGreeting<'a> {
fn new(value: &'a str) -> Self {
Self {
value,
called_with: Default::default(),
}
}
}
impl<'a> Greeting for MockGreeting<'a> {
fn say_hello(&self, name: &str) -> String {
self.called_with.borrow_mut().push(name.to_owned());
self.value.to_owned()
}
}
#[test]
fn test1() {
let g = MockGreeting::new("Hello");
let r = thing_that_uses_say_hello(&g, "Tom");
assert_eq!("Hello", r);
assert_eq!(&*g.called_with.borrow(), &["Tom".to_string()]);
}
}