So I have this program and everything is working just as I want it to. The only problem is "student1.name" at the bottom. I wanted to replace "self.name" with "student1.name", but i'm having Scoping Issues. "self.name" was working perfectly fine. Here is my code:
fn main() {
let student1 = IOT_student {
name: String::from("Husayn Abbas"),
age: 13,
education: String::from("O Levels"),
};
let instructor1 = IOT_instructor {
name: String::from("Imran Ali"),
age: 25,
};
println!("{}", student1.ask_Questions());
println!("{}", instructor1.ask_Questions());
}
trait Questions {
fn ask_Questions(&self) -> String;
}
struct IOT_student {
name: String,
age: i8,
education: String,
}
struct IOT_instructor {
name: String,
age: i8,
}
impl Questions for IOT_student {
fn ask_Questions(&self) -> String {
return format!("Zoom session will be LIVE, Zoom recording will not be available. Quarter 2 studio recorded videos are available on Portal.");
}
}
impl Questions for IOT_instructor {
fn ask_Questions(&self) -> String {
return format!("{} In case of any issue email to education#piaic.org", student1::name);
}
}
And here is my output:
Compiling IOT_Assignment_2 v0.1.0 (/home/memelord/Documents/PIAIC Quarter 2 IOT Assignments/IOT_Assignment_2)
error[E0425]: cannot find value `student1` in this scope
--> src/main.rs:40:80
|
40 | return format!("{} In case of any issue email to education#piaic.org", student1.name);
| ^^^^^^^^ not found in this scope
warning: type `IOT_student` should have an upper camel case name
--> src/main.rs:21:8
|
21 | struct IOT_student {
| ^^^^^^^^^^^ help: convert the identifier to upper camel case: `IotStudent`
|
= note: `#[warn(non_camel_case_types)]` on by default
warning: type `IOT_instructor` should have an upper camel case name
--> src/main.rs:27:8
|
27 | struct IOT_instructor {
| ^^^^^^^^^^^^^^ help: convert the identifier to upper camel case: `IotInstructor`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0425`.
error: could not compile `IOT_Assignment_2`.
To learn more, run the command again with --verbose.
Have any ideas why this is happening (i'm a Rust beginner so please try to make your explanation simple)?
The trait implementation methods are in completely different universe than the function calling them.
If you want to be able to use the name of a student in the call, you have to add an argument to the function. Example:
impl Questions for IOT_instructor {
fn ask_Questions(&self, student: &IOT_student) -> String {
return format!("{} In case of any issue email to education#piaic.org", student.name);
}
}
Now call like:
println!("{}", instructor1.ask_Questions(&student1));
Related
I have the following struct:
#[derive(Default)]
pub struct AppState {
actors: HashMap<String, ActorValue>,
feature: HashMap<String, FeatureValue>,
}
Actors are registered when running the application upon receiving a network request (i.e., they are inserted into the HashMap). Furthermore, a user can create a new feature for which a certain actor may be required.
pub enum ActorValue {
Automotive(AutomotiveActor),
Power(PowerActor),
}
pub enum FeatureValue {
Automotive(AutomotiveFeature),
// ....
}
pub struct AutomotiveFeature {
pub actor_name: String,
// ... more Actor-related String fields
}
pub struct AutomotiveActor {
name: String,
// ... more String fields
}
So, when creating an instance of AutomotiveFeature I am currently cloning the name of the respective AutomotiveActor instance to populate the actor_name:
let automotive_actor = app_state.actors.iter()
.find(|x| matches!(x.1, ActorValue::Automotive(_)))
.map(|x| match x.1 {
ActorValue::Automotive(p) => Some(p),
_ => None,
})
.flatten();
match automotive_actor {
Some(a) => {
let feature = AutomotiveFeature { actor_name: a.name.clone() };
}
None => {}
}
However, I am essentially keeping redundant info. Ideally, I could just replace all the String fields relating to the actor in the feature with a reference:
pub struct AutomotiveFeature {
pub actor: &AutomotiveActor
}
But I am getting lifetime issues and I don't know how I can annotate them correctly, considering I have two HashMaps.
If I use:
pub struct AutomotiveFeature {
pub actor: &'static AutomotiveActor
}
I get the following errors:
error[E0502]: cannot borrow `*state` as mutable because it is also borrowed as immutable
--> crates/code/src/my_code.rs:146:13
|
38 | let automotive_actor: Option<&AutomotiveActor> = app_state
| __________________________________________________-
39 | | .actors()
| |_____________________________- immutable borrow occurs here
...
43 | ActorValue::Automotive(p) => Some(p),
| ------- returning this value requires that `*state` is borrowed for `'static`
...
146 | / app_state
147 | | .features_mut()
| |____________________________^ mutable borrow occurs here
error: lifetime may not live long enough
--> crates/code/src/my_code.rs:43:40
|
35 | app_state: &mut AppState,
| - let's call the lifetime of this reference `'1`
...
43 | ActorValue::Automotive(p) => Some(p),
| ^^^^^^^ returning this value requires that `'1` must outlive `'static`
I have already looked at similar post, such as "Store reference of struct in other struct". Unfortunately, I cannot use std::rc::Rc; because I get the error:
`Rc<AutomotiveActor>` cannot be sent between threads safely
I am getting lifetime issues and I don't know how I can annotate them correctly"
Note that you can only explain to the compiler how long something lives. You can't actually make an object live longer by annotating a lifetime. References do not own an object or keep it alive. Rc/Arc actually keep an object alive, so I have a suspicion that this is what you want.
The reason I want to have a reference is that I can then implement methods as part of AutomotiveActor and then directly call automotive_actor.start_car()
I suspect that start_car() modifies the automotive_actor and is therefore a mut fn. This completely renders your initial idea of using references impossible, because you can only ever have one mutable reference to an object.
Rc/Arc also only provide immutable access to the object, but you can combine them with RefCell/Mutex to create interior mutability.
Rc<AutomotiveActor> cannot be sent between threads safely
This makes me assume that your project is multi-threaded and therefore requires thread safety. This means you probably want to use Arc<Mutex>.
This is one possible layout:
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
#[derive(Default)]
pub struct AppState {
actors: HashMap<String, ActorValue>,
feature: HashMap<String, FeatureValue>,
}
pub enum ActorValue {
Automotive(Arc<Mutex<AutomotiveActor>>),
//Power(PowerActor),
}
pub enum FeatureValue {
Automotive(AutomotiveFeature),
// ....
}
pub struct AutomotiveFeature {
pub actor: Arc<Mutex<AutomotiveActor>>,
// ... more Actor-related String fields
}
pub struct AutomotiveActor {
name: String,
// ... more String fields
}
fn main() {
let mut app_state = AppState::default();
let new_actor = Arc::new(Mutex::new(AutomotiveActor {
name: String::from("MyActor"),
}));
app_state.actors.insert(
new_actor.lock().unwrap().name.clone(),
ActorValue::Automotive(Arc::clone(&new_actor)),
);
let automotive_actor = app_state
.actors
.iter()
.find(|x| matches!(x.1, ActorValue::Automotive(_)))
.map(|x| match x.1 {
ActorValue::Automotive(p) => Some(p),
_ => None,
})
.flatten();
match automotive_actor {
Some(a) => {
let feature = AutomotiveFeature {
actor: Arc::clone(a),
};
}
None => {}
}
}
I'm implementing a simple task queue using redis in Rust, but am struggling to deserialize the returned values from redis into my custom types.
In total I thought of 3 approches:
Deserializing using serde-redis
Manually implementing the FromRedisValue trait
Serializing to String using serde-json > sending as string > then deserializing from string
The 3rd approach worked but feels artificial. I'd like to figure out either 1 or 2, both of which I'm failing at.
Approach 1 - serde-redis
I have a simple Task definition:
#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct Task {
pub id: u32,
pub desc: String,
}
which I'm trying to receive into a worker:
use serde_redis::RedisDeserialize;
pub fn execute_task(conn: &mut Connection) {
let task: Task = redis::cmd("LPOP")
.arg("task_q")
.query::<Task>(conn)
.unwrap()
.deserialize()
.unwrap();
println!("... executing task {} ...", task.id);
}
but I'm getting the following error:
error[E0277]: the trait bound `Task: FromRedisValue` is not satisfied
--> src/bin/worker.rs:38:10
|
38 | .query::<Task>(conn)
| ^^^^^ the trait `FromRedisValue` is not implemented for `Task`
error[E0599]: no method named `deserialize` found for struct `Task` in the current scope
--> src/bin/worker.rs:40:10
|
40 | .deserialize()
| ^^^^^^^^^^^ method not found in `Task`
So clearly I integrated the crate wrong, as it's not working. The documentation is super brief and the source code way over my head as a beginner - what could I be missing?
Approach 2 - manually implementing FromRedisValue
My naive approach:
impl FromRedisValue for Task {
fn from_redis_value(v: &Value) -> RedisResult<Self> {
let t = Task {
id: v.id,
desc: v.desc,
};
RedisResult::Ok(t)
}
fn from_redis_values(items: &[Value]) -> RedisResult<Vec<Self>> {
let tasks = items
.into_iter()
.map(|v| Task {
id: v.id,
desc: v.desc,
})
.collect();
RedisResult::Ok(tasks)
}
}
The errors I'm getting:
error[E0609]: no field `id` on type `&Value`
--> src/redis_tut.rs:203:19
|
203 | id: v.id,
| ^^
error[E0609]: no field `desc` on type `&Value`
--> src/redis_tut.rs:204:21
|
204 | desc: v.desc,
| ^^^^
// ...the same for the vector implementation
So clearly redis's Value doesn't have / know of the fields I want for Task. What's the right way to do this?
Redis doesn't define structured serialization formats. It mostly store strings and integers. So you have to choose or define your format for your struct.
A popular one is JSON, as you noticed, but if you just want to (de)serialize simple pairs of (id, description), it's not very readable nor convenient.
In such a case, you can define your own format, for example the id and the description with a dash in between:
#[derive(Debug, PartialEq)]
pub struct Task {
pub id: u32,
pub desc: String,
}
// assume a task is defined as "<id>-<desc>"
impl FromRedisValue for Task {
fn from_redis_value(v: &Value) -> RedisResult<Self> {
let v: String = from_redis_value(v)?;
if let Some((id, desc)) = v.split_once('-') {
if let Ok(id) = id.parse() {
Ok(Task {
id,
desc: desc.to_string(),
})
} else {
Err((ErrorKind::TypeError, "bad first token").into())
}
} else {
Err((ErrorKind::TypeError, "missing dash").into())
}
}
}
I actually wrote a crate for this kind of Problem, it provides Derive Macros that implements the redis::FromRedisValue and redis::ToRedisArgs traits from mitsuhiko / redis-rs for any struct in which every field's type also implements ToRedisArgs. Check it out redis-redive.
I am trying to implement a function-like macro in Rust. It should be used as follows:
service!(FooService, "/foo_service");
This macro call shall generate a struct FooServiceClient and an impl FooServiceClient with various methods.
I am using the syn + quote duo and here are some relevant bits of my code:
struct ServiceDefinition {
pub service_name: String,
pub scope: String,
pub operations: Vec<Operation>,
}
impl Parse for ServiceDefinition {
fn parse(input: ParseStream) -> Result<Self> {
let params = Punctuated::<Expr, Token![,]>::parse_terminated(&input)?;
let service_name = {
let expr = ¶ms[0];
match expr {
Expr::Path(ref expr_path) => {
let leading_colon = expr_path.path.leading_colon;
if let Some(leading_colon) = leading_colon {
return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
}
if expr_path.path.segments.len() != 1 {
return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
}
expr_path.path.segments.first().unwrap().ident
},
_ => {
return Err(Error::new(expr.span(), "expected service name"));
}
}
};
Ok(ServiceDefinition {
service_name: service_name.to_string(),
scope: "/foo".to_string(),
operations: vec![],
})
}
}
As you can see, this is still at the early stages of work in progress, but when I try to compile it, this is the error I'm getting:
error[E0599]: no method named `span` found for reference `&ExprPath` in the current scope
--> service_commons/src/lib.rs:58:57
|
58 | return Err(Error::new(expr_path.span(), "expected unscoped identifier"));
| ^^^^ method not found in `&ExprPath`
|
As far as I can see in the docs, the method span should be on these types, but it's obviously missing for some reason.
I am using syn = {version = "1.0.70", features = ["full"]} in my Cargo file.
What am I missing here?
span comes from the Spanned trait, so it needs to be in scope:
use syn::spanned::Spanned;
See also:
Why do I need to import a trait to use the methods it defines for a type?
I've implemented a struct which has a list of crontab entries, each of which knows its own recurrence (such as */5 * * * * in crontab):
extern crate chrono;
use chrono::NaiveDateTime;
pub struct Crontab<'a> {
entries: Vec<Entry<'a>>,
}
pub struct Entry<'a> {
pub recurrence: Recurrence,
pub command: &'a str,
}
pub struct Recurrence {
minutes: Vec<u8>,
hours: Vec<u8>,
days_of_month: Vec<u8>,
months: Vec<u8>,
days_of_week: Vec<u8>,
}
Based on the current time you can get the next occurrence of a command:
impl Recurrence {
pub fn next_match(&self, after: NaiveDateTime) -> NaiveDateTime {
unimplemented!()
}
}
I'm trying to write a function on Crontab to get the Entry which will run next (that is, for which recurrence.next_match() is the lowest).
impl<'a> Crontab<'a> {
fn next_run(&self, from: NaiveDateTime) -> Run<'a> {
&self.entries
.into_iter()
.map(|entry| Run {
entry: &entry,
datetime: entry.recurrence.next_match(from),
})
.min_by(|this, other| this.datetime.cmp(&other.datetime))
.unwrap()
}
}
struct Run<'a> {
entry: &'a Entry<'a>,
datetime: NaiveDateTime,
}
This generates the error:
error[E0308]: mismatched types
--> src/main.rs:30:9
|
29 | fn next_run(&self, from: NaiveDateTime) -> Run<'a> {
| ------- expected `Run<'a>` because of return type
30 | / &self.entries
31 | | .into_iter()
32 | | .map(|entry| Run {
33 | | entry: &entry,
... |
36 | | .min_by(|this, other| this.datetime.cmp(&other.datetime))
37 | | .unwrap()
| |_____________________^ expected struct `Run`, found &Run<'_>
|
= note: expected type `Run<'a>`
found type `&Run<'_>`
Similar variants I've tried fail to compile with messages such as "cannot move out of borrowed content" (if changing the return type to &Run<'a>) or that the &entry does not live long enough.
It seems to make most sense that the Run should have a reference to rather than a copy of the Entry, but I'm not sure how to juggle both the lifetimes and references to get to that point (and I don't know whether 'a refers to the same lifetime in both structs). What am I missing here?
As described in Is there any way to return a reference to a variable created in a function?, you cannot create a value in a function and return a reference to it. Nothing would own the result of your iterator chain, thus the reference would point at invalid data.
That doesn't even really matter: as pointed out in the comments, you cannot call into_iter on self.entries because you cannot move out of borrowed content to start with, as described in Cannot move out of borrowed content. This means that we cannot have an owned value of an Entry as the result of the iterator chain to start with.
Crontab owns the Entry; as soon as the Crontab moves, any reference to any Entry becomes invalid. This means that any references need to be tied to how long self lives; the generic lifetime 'a cannot come into play:
fn next_run(&self, from: NaiveDateTime) -> Run {
self.entries
.iter()
.map(|entry| Run {
entry,
datetime: entry.recurrence.next_match(from),
})
.min_by(|this, other| this.datetime.cmp(&other.datetime))
.unwrap()
}
Or the explicit version:
fn next_run<'b>(&'b self, from: NaiveDateTime) -> Run<'b> { /* ... */ }
I've just started with Rust but can't quite grasp lifetimes so I could resolve following issue by myself:
This test project is about simulating a bit to allow tracing it through various bitwise operations, e.g. let newbit = oldbit1 ^ oldbit2 and looking at newbit I can tell afterwards it came out of an XOR operation with oldbit1 and oldbit2 as operands.
#[derive(Copy,Clone)]
pub enum TraceOperation {
AND,
OR,
XOR,
NOT,
}
#[derive(Copy,Clone)]
pub struct TraceBit<'a> {
source_a: Option<&'a TraceBit<'a>>,
source_b: Option<&'a TraceBit<'a>>,
source_op: Option<TraceOperation>,
value: bool,
}
This compiles, but I don't fully understand why the lifetime parameters are needed that way. I assume that the compiler cannot expect that the members source_a and source_b live as long as the struct itself as this may not hold true, so explicit lifetimes are required.
is this assumption correct?
Further I don't fully understand why I have to re-specify the lifetime parameter for the reference type, i.e. why I have to write source_a: Option<&'a TraceBit<'a>> as opposed to source_a: Option<&'a TraceBit>.
What is the second lifetime used for? How do I read that line out loud? I have:
"source_a is a variable of type Option that may have Some reference (that is valid at least as long as the struct itself and as long as member source_b) to an instance of TraceBit"
My final issue is that I cannot make it to work using an overloaded operator:
use std::ops::BitXor;
impl<'a> BitXor for TraceBit<'a> {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self {
let valA: usize = if self.value { 1 } else { 0 };
let valB: usize = if rhs.value { 1 } else { 0 };
let val = if valA ^ valB != 0 { true } else { false };
TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
}
}
This is basically pure guessing based on BitXor documentation. So what I try to do, in a very explicit manner, is to perform an xor operation on the two input variables and create a new TraceBit as output with the inputs stored in it as reference.
error[E0597]: `self` does not live long enough
--> libbittrace/src/lib.rs:37:30
|
37 | TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
| ^^^^ does not live long enough
38 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 31:1...
--> libbittrace/src/lib.rs:31:1
|
31 | / impl<'a> BitXor for TraceBit<'a> {
32 | | type Output = Self;
33 | | fn bitxor(self, rhs: Self) -> Self {
34 | | let valA: usize = if self.value { 1 } else { 0 };
... |
40 | |
41 | | }
| |_^
error[E0597]: `rhs` does not live long enough
--> libbittrace/src/lib.rs:37:53
|
37 | TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
| ^^^ does not live long enough
38 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 31:1...
--> libbittrace/src/lib.rs:31:1
|
31 | / impl<'a> BitXor for TraceBit<'a> {
32 | | type Output = Self;
33 | | fn bitxor(self, rhs: Self) -> Self {
34 | | let valA: usize = if self.value { 1 } else { 0 };
... |
40 | |
41 | | }
| |_^
error: aborting due to 2 previous errors
Seems like nothing lives longer than the xor operation itself, but how can I resolve this?
I've tried various workarounds/changes to the code but to no avail and in any way I rather like to understand the issue than guessing a correct solution....
Tree-like structures must use the Box pointer type (Option<Box<TraceBit>>). In general, in structs you should prefer owned types.
Rust references aren't mere pointers. They are borrows (compile-time read/write locks) of data that must exist as owned somewhere else.
So if you have an owned version of TraceBit:
pub struct TraceBit {
source_a: Option<Box<TraceBit>>,
}
then reference to it is of type: &'a TraceBit, but references to a type don't change how the type looks internally, so the type of source_a is still Box<TraceBit>. You can keep getting the &'a TraceBit references recursively step by step:
trace_bit = trace_bit.source_a.as_ref().unwrap();
but there's no construct in Rust where taking a reference to the root of a tree suddenly changes the whole tree into a tree of references, so the type you are creating can't exist, and that's why you can't get type annotations right.
Maybe instead of passing references around, you should use a contained and cloneable name type.
use std::rc::Rc;
#[derive(Debug)]
pub enum TraceOperation {
AND,
OR,
XOR,
NOT,
}
#[derive(Debug)]
pub enum BitName<T> {
Name(Rc<T>),
Combination(Rc<(TraceOperation, BitName<T>, BitName<T>)>),
}
impl<T> Clone for BitName<T> {
fn clone(&self) -> Self {
match self {
&BitName::Name(ref x) => BitName::Name(Rc::clone(x)),
&BitName::Combination(ref x) => BitName::Combination(Rc::clone(x)),
}
}
}
impl<T> From<T> for BitName<T> {
fn from(x:T) -> Self {
BitName::Name(Rc::new(x))
}
}
impl<T> BitName<T> {
pub fn combine(op : TraceOperation, a : &Self, b :&Self) -> Self {
BitName::Combination(Rc::new((op, (*a).clone(), (*b).clone())))
}
}
fn main() {
let x : BitName<String> = BitName::from(String::from("x"));
let y : BitName<String> = BitName::from(String::from("y"));
let xandy = BitName::combine(TraceOperation::AND, &x, &y);
println!("{:?}", xandy);
}