Here's where I'm starting from:
#[derive(PartialEq)]
enum ControlItem {
A {
name: &'static str,
},
B {
name: &'static str,
},
}
struct Control {
items: Vec<(ControlItem, bool)>,
}
impl Control {
pub fn set(&mut self, item: ControlItem, is_ok: bool) {
match self.items.iter().position(|ref x| (**x).0 == item) {
Some(idx) => {
self.items[idx].1 = is_ok;
}
None => {
self.items.push((item, is_ok));
}
}
}
pub fn get(&self, item: ControlItem) -> bool {
match self.items.iter().position(|ref x| (**x).0 == item) {
Some(idx) => return self.items[idx].1,
None => return false,
}
}
}
fn main() {
let mut ctrl = Control { items: vec![] };
ctrl.set(ControlItem::A { name: "a" }, true);
assert_eq!(ctrl.get(ControlItem::A { name: "a" }), true);
ctrl.set(ControlItem::B { name: "b" }, false);
assert_eq!(ctrl.get(ControlItem::B { name: "b" }), false);
}
I have a Control type that should save the state of some predefined items and report it back to user.
I have a virtual table in my mind, like this:
|Name in program | Name for user |
|item_1 | Item one bla-bla |
|item_2 | Item two bla-bla |
|item_3 | Item three another-bla-bla|
I want Control to have get / set methods that accept only things with names item_1, item_2, item_3.
I want to hold this virtual table in two crates: "main" and "platform". Most of the implementation of Control should be in the main crate, and definitions of the items (like item_3) should go into the platform crate. I want to register item_3 at compile time.
Any ideas on how achieve this?
It sounds like you should use a trait, not an enum. You could define a trait and implement it like this:
pub trait ControlItem {
fn name(&self) -> &str;
}
struct A(&'static str);
impl ControlItem for A {
fn name(&self) -> &str {
self.0
}
}
// ... similar struct and impl blocks for other items
Then these structs can be moved into separate crates.
You'd need to change Control to store a Vec<(Box<ControlItem>, bool)>, and either change get and set to take a Box<ControlItem>, or to be generic over T: ControlItem.
Read about traits and trait objects for more.
Related
I am implementing a derive macro to reduce the amount of boilerplate I have to write for similar types.
I want the macro to operate on structs which have the following format:
#[derive(MyTrait)]
struct SomeStruct {
records: HashMap<Id, Record>
}
Calling the macro should generate an implementation like so:
impl MyTrait for SomeStruct {
fn foo(&self, id: Id) -> Record { ... }
}
So I understand how to generate the code using quote:
#[proc_macro_derive(MyTrait)]
pub fn derive_answer_fn(item: TokenStream) -> TokenStream {
...
let generated = quote!{
impl MyTrait for #struct_name {
fn foo(&self, id: #id_type) -> #record_type { ... }
}
}
...
}
But what is the best way to get #struct_name, #id_type and #record_type from the input token stream?
One way is to use the venial crate to parse the TokenStream.
use proc_macro2;
use quote::quote;
use venial;
#[proc_macro_derive(MyTrait)]
pub fn derive_answer_fn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Ensure it's deriving for a struct.
let s = match venial::parse_declaration(proc_macro2::TokenStream::from(item)) {
Ok(venial::Declaration::Struct(s)) => s,
Ok(_) => panic!("Can only derive this trait on a struct"),
Err(_) => panic!("Error parsing into valid Rust"),
};
let struct_name = s.name;
// Get the struct's first field.
let fields = s.fields;
let named_fields = match fields {
venial::StructFields::Named(named_fields) => named_fields,
_ => panic!("Expected a named field"),
};
let inners: Vec<(venial::NamedField, proc_macro2::Punct)> = named_fields.fields.inner;
if inners.len() != 1 {
panic!("Expected exactly one named field");
}
// Get the name and type of the first field.
let first_field_name = &inners[0].0.name;
let first_field_type = &inners[0].0.ty;
// Extract Id and Record from the type HashMap<Id, Record>
if first_field_type.tokens.len() != 6 {
panic!("Expected type T<R, S> for first named field");
}
let id = first_field_type.tokens[2].clone();
let record = first_field_type.tokens[4].clone();
// Implement MyTrait.
let generated = quote! {
impl MyTrait for #struct_name {
fn foo(&self, id: #id) -> #record { *self.#first_field_name.get(&id).unwrap() }
}
};
proc_macro::TokenStream::from(generated)
}
Suppose my library definespub struct MyStruct { a: i32 }. Then, suppose I define a procedural macro my_macro!. Perhaps I want my_macro!(11) to expand to MyStruct { a: 11 }. This proves that I must have used my_macro! to obtain MyStruct, since the a field is not public.
This is what I want to do:
lib.rs:
struct MyStruct {
a: i32,
}
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
let ast: syn::Expr = syn::parse(input).unwrap();
impl_my_macro(ast)
}
fn impl_my_macro(expr: syn::Expr) -> TokenStream {
let gen = match expr {
syn::Expr::Lit(expr_lit) => match expr_lit.lit {
syn::Lit::Int(lit_int) => {
let value = lit_int.base10_parse::<i32>().unwrap();
if value > 100 {
quote::quote! {
compile_error("Integer literal is too big.");
}
} else {
quote::quote! {
MyStruct{a: #lit_int}
}
}
}
_ => quote::quote! {
compile_error!("Expected an integer literal.");
},
},
_ => quote::quote! {
compile_error!("Expected an integer literal.");
},
};
gen.into()
}
And I would use it like this:
fn test_my_macro() {
let my_struct = secret_macro::my_macro!(10);
}
But the compiler gives this warning:
error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope
--> tests/test.rs:14:21
|
14 | secret_macro::my_macro!(10);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
Is it possible to achieve this in Rust?
I have two FnvHashMap which is declared like first: FnvHashMap<(i32, i32), Employee>
second: FnvHashMap<(i32, i32), Employee>
Where Employee is
pub struct Employee {
pub emp_id: i32,
pub lang_id: i32,
pub dept_id: f64,
pub description: String,
}
I need to iterate through 'first' FnvHashMap and see if there is a matching record(emp_id and lang_id) in 'second' FnvHashMap
I may not need to consider dept_id and description
Thanks in Advance.
New code after implementing nested loop
for (_, employee1) in &first {
for (_, employee2) in &second {
if employee1.emp_id == employee2.emp_id && employee1.lang_id == employee2.lang_id {
values.push(OldNew {
old: employee2,
new: employee1,
});
}
}
}
let new = first
.into_iter()
.filter(|(a, _)| !old.contains_key(a))
.map(|(_, a)| a)
.collect();
let deleted = second
.iter()
.filter(|(a, _)| !new.contains_key(a))
.map(|(&a, _)| a)
.collect();
Changes {
deleted,
new,
values,
}
pub struct Changes<T, I> {
pub deleted: Vec<I>,
pub new: Vec<T>,
pub values: Vec<OldNew<T>>,
}
expected struct `organization::models::employee_stat::Employee`, found `&organization::models::employee_stat::Employee`
Simply make two nested for loops to iterate through both maps and then compare the values you need from the two iterations of the loops, for example
for (_, employee1) in &first {
for (_, employee2) in &second {
if employee1.emp_id == employee2.emp_id && employee1.lang_id == employee2.lang_id {
/* Code here to run if a matching value is found */
}
}
}
I'm using the tracing library in my project and there is one thing I'm not able to figure out: How can I access a value (that I set in my span when I create it) in my Layer?
My layer looks like this:
impl<S> Layer<S> for CustomLayer where S: Subscriber {
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
Interest::sometimes() //hardcoding so enabled() will be called everytime a span is created
}
fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool {
if metadata.is_span() {
// How do I access value of key here?
if value == X {
true
} else if value == Y {
false
}
}
true // default
}
}
You can access the data in a Span if you have access to either its ValueSet (as found in new_span() or on_new_span() via Attributes) or a Record entry for it (as found in record() or on_record()). With that you can use the visitor pattern to find the information you desire. Here's a simple implementation that checks if a field exists and its value is a matching string:
use std::fmt::Debug;
use tracing::field::{ValueSet, Visit, Field};
use tracing::span::Record;
struct MatchStrVisitor<'a> {
field: &'a str,
value: &'a str,
matched: bool,
}
impl Visit for MatchStrVisitor<'_> {
fn record_debug(&mut self, _field: &Field, _value: &dyn Debug) {}
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == self.field && value == self.value {
self.matched = true;
}
}
}
fn value_in_valueset(valueset: &ValueSet<'_>, field: &str, value: &str) -> bool {
let mut visitor = MatchStrVisitor { field, value, matched: false };
valueset.record(&mut visitor);
visitor.matched
}
fn value_in_record(record: &Record<'_>, field: &str, value: &str) -> bool {
let mut visitor = MatchStrVisitor { field, value, matched: false };
record.record(&mut visitor);
visitor.matched
}
This is pretty rudimentary but hopefully demonstrates what is possible. One thing to note is that the "value" that is stored is either a primitive value (i64, u64, bool, str, etc.) or in a type-erased form via &dyn Debug. Those are the only types of values you can receive from the visitor.
Addressing OP's case in particular, as explained in this issue you cannot access this information in the enabled() method since that occurs before any values are recorded. You will need to make your determination in the new_span() method, and use span extensions via the registry to track whether you consider the span is "enabled" in your other methods.
Here's another rudimentary example:
use tracing::span::Attributes;
use tracing::{Subscriber, Metadata, Id, Event};
use tracing::subscriber::Interest;
use tracing_subscriber::layer::{Context, Layer};
use tracing_subscriber::registry::LookupSpan;
struct CustomLayer;
struct CustomLayerEnabled;
impl<S> Layer<S> for CustomLayer where S: Subscriber + for <'a> LookupSpan<'a> {
fn register_callsite(&self, _metadata: &'static Metadata<'static>) -> Interest {
Interest::sometimes()
}
fn enabled(&self, metadata: &Metadata<'_>, _ctx: Context<'_, S>) -> bool {
metadata.is_span()
}
fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
if value_in_valueset(attrs.values(), "myfield", "myvalue") {
ctx.span(id).unwrap().extensions_mut().insert(CustomLayerEnabled);
}
}
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
let span_id = event.parent().unwrap();
if let None = ctx.span(span_id).unwrap().extensions().get::<CustomLayerEnabled>() {
return;
}
// ... rest of your logic
}
}
Note: I've completely rewritten this answer taking info from the comments and my newfound experience.
Example:
let response = add_customer(InputCustomer)
.validate()?
.generate_code()
.create(DB::create(pool_conextion))?;
I tried using various structures, but I don't know if it is the best way to do it:
struct InputCustomer {}
fn add_customer(i: InputCustomer) -> Validate {
Validate {
result: InputCustomer {},
}
}
struct Validate {
result: InputCustomer,
}
impl Validate {
fn do_validate() -> GenCode {
// valdiate struct customer
GenCode {
result: InputCustomer {},
}
}
}
struct GenCode {
result: InputCustomer,
}
impl GenCode {
fn generate_code() -> Create {
// generate customer code
Create { result: true }
}
}
struct Create {
result: bool,
}
You can implement all the functions on a single struct using phantom type parameters. The Customer struct holds some state:
pub struct Customer<State> {
state: PhantomData<State>,
}
We can create the possible states that a Customer can be in:
pub struct CustomerStateNew;
pub struct CustomerStateValidated;
pub struct CustomerStateWithCode;
When you create a Customer, it's state is CustomerStateNew:
pub fn add_customer() -> Customer<CustomerStateNew> {
Customer { state: PhantomData }
}
To validate a Customer it must be in the CustomerStateNew state:
impl Customer<CustomerStateNew> {
pub fn validate(&self) -> Customer<CustomerStateValidated> {
Customer { state: PhantomData }
}
}
The Customer must be validated (CustomerStateValidated) to generate a code:
impl Customer<CustomerStateValidated> {
pub fn generate_code(&self) -> Customer<CustomerStateWithCode> {
Customer { state: PhantomData }
}
}
And it must have a generated code (CustomerStateWithCode) to be created. create consumes self, so the customer cannot be used after it is created (you might not want this behavior, but I included it here for completeness):
impl Customer<CustomerStateWithCode> {
pub fn create(self) -> Result<(), ()> {
Ok(())
}
}
Now we can chain together the methods to create the user:
let result = add_customer().validate().generate_code().create()?;
However, it we try to create the Customer before it is validated, the code will not compile:
let result = add_customer().create();
// error[E0599]: no method named `create` found for struct `Customer<CustomerStateNew>`
// --> src/main.rs:36:20
// 36 | add_customer().create();
// | ^^^^^^ method not found in `Customer<CustomerStateNew>`
Also, no one else can create a Customer with an arbitrary state, because the state field is private:
mod somewhere_else {
fn bla() {
let customer: Customer<CustomerStateWithCode> = Customer { state: PhantomData };
customer.create();
}
}
// error[E0451]: field `state` of struct `Customer` is private
// --> src/main.rs:41:64
// |
// 41 | let customer: Customer<CustomerStateWithCode> = Customer { state: PhantomData };
// |
If you want to store data specific to each state, you can store that actual State inside the Customer instead of PhantomData. Now however, the state is more than just compile time safety and will be stored at runtime:
pub struct CustomerStateWithCode(pub usize);
pub struct Customer<State> {
state: State,
}
impl Customer<CustomerStateValidated> {
pub fn generate_code(&self) -> Customer<CustomerStateWithCode> {
Customer { state: CustomerStateWithCode(1234) }
}
}
We have created a simple state machine using phantom types. This is also knows as the type state pattern. Note that the states will be compiled away to nothing, so there is no runtime cost, only compile time safety!
Playground link