test2.json
[
{"name":"Lucy","age":18,"achievement":[95,86.5,90]},
{"name":"Lily","age":19,"achievement":[92.5,89,91]},
{"name":"Jack","age":20,"achievement":[93,90,93.5]}
]
main.rs
use std::fs::File;
use std::io::BufReader;
use serde_json::{Result as SResult, Value};
fn get_profile() -> SResult<Value> {
let file = File::open("test2.json").expect("file should open read only");
let reader = BufReader::new(file);
let mut v: Value = serde_json::from_reader(reader)?;
Ok(v.take())
}
fn main() {
let profiles = get_profile().unwrap();
for element in profiles.as_array().iter() {
println!("the value is: {}", element["age"]);
}
}
error:
λ cargo run
Compiling hello-rust v0.1.0 (D:\workspace\rust-projects\hello-rust)
error[E0277]: the type `[Value]` cannot be indexed by `&str`
--> src\main.rs:20:38
|
20 | println!("the value is: {}", element["age"]);
| ^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
|
= help: the trait `SliceIndex<[Value]>` is not implemented for `&str`
= note: required because of the requirements on the impl of `std::ops::Index<&str>` for `Vec<Value>`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `hello-rust` due to previous error
How to fix the issue?
Any help please, thanks.
You are calling .iter() on an Option<&Vec<Value>>. The iterator yields one value if the Option is a Some, otherwise none.
You can change your loop to this instead, which will yield one Value (here the object) at a time:
for element in profiles.as_array().unwrap().iter() {
println!("the value is: {}", element["age"]);
}
Note: I'd at least use is_array() etc. to validate the structure and improve error handling (not just unwrap(). You get a lot for free when using structs and Deserialize instead of parsing everything manually.
Example:
#[derive(Deserialize)]
struct Profile {
name: String,
age: usize,
achievement: Vec<f64>,
}
let profiles: Vec<Profile> =
serde_json::from_reader(BufReader::new(File::open("test2.json").unwrap())).unwrap();
for profile in profiles {
println!("the value is: {}", profile.age);
}
Related
I'm currently working with the 'sha2' library.
One of the functions in my project is supposed to calculate the hash of a file using
different methods (Sha224, Sha256, Sha384, Sha512).
All of the hash methods use the trait sha2::Digest and
all return a GenericArray<u8, Self::OutputSize>.
However, since a generic type is used, i get following error message:
cannot add `<T as OutputSizeUser>::OutputSize` to `<T as OutputSizeUser>::OutputSize`
with this code:
use sha2::{Digest, Sha224, Sha256, Sha384, Sha512};
use std::error::Error;
use std::{env, fs, io, process};
//...
impl AutoSha {
//...
fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> {
let mut hasher = T::new();
let mut file = fs::File::open(file_path)?;
io::copy(&mut file, &mut hasher)?;
let hash = hasher.finalize();
Ok(format!("{:x}", hash)) // This results in the error message
}
}
//Call example:
let auto_sha: AutoSha = AutoSha::new();
let hash = auto_sha.calc_hash::<Sha256>("path/to/file".to_string());
Using, for example, Sha256::new() instead of T::new() works fine.
I have tried to set both 'OutputSizeUser' and 'OutputSize' as trait bounds but was unable
to find what namespace they are located in.
According to the documentation it is supposedly part of 'crypto_common' which should be included in the project as it is a dependency of 'sha2', but i was not able to include it.
Additionally, i do not know whether that would solve my issue either.
How can i format the return value of 'hasher.finalize()' to a hexadecimal string?
Full error log
error[E0277]: cannot add `<T as OutputSizeUser>::OutputSize` to `<T as OutputSizeUser>::OutputSize`
--> src/main.rs:80:28
|
80 | Ok(format!("{:x}", hash))
| ^^^^ no implementation for `<T as OutputSizeUser>::OutputSize + <T as OutputSizeUser>::OutputSize`
|
= help: the trait `Add` is not implemented for `<T as OutputSizeUser>::OutputSize`
= note: required because of the requirements on the impl of `LowerHex` for `GenericArray<u8, <T as OutputSizeUser>::OutputSize>`
note: required by a bound in `ArgumentV1::<'a>::new_lower_hex`
= note: this error originates in the macro `$crate::__export::format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
|
73 | fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> where <T as OutputSizeUser>::OutputSize: Add {
| ++++++++++++++++++++++++++++++++++++++++++++
For more information about this error, try `rustc --explain E0277`.
error: could not compile `hash-cmp` due to previous error
I have tried to use the recommended solution with std::ops::Add but did not manage to get it to work either.
Useful links
sha2 Documentation
Full source code (Github)
After experimenting/reading for a while i found following solution:
fn calc_hash<T: Digest + io::Write>(&self, file_path: String) -> Result<String, Box<dyn Error>> {
let mut hasher = T::new();
let mut file = fs::File::open(file_path)?;
io::copy(&mut file, &mut hasher)?;
let hash = hasher.finalize();
let mut hash_string = "".to_string();
for byte in hash.iter() {
hash_string = format!("{}{:02x}", hash_string, byte);
}
Ok(hash_string)
}
I'm basically just iterating over each byte of the GenericArray and then concat it with the end result.
I'm attempting to merge a JSON string as Vec<u8> with the result of decoding a Base64VecU8 value:
pub fn decode_and_merge(&mut self, args: Base64VecU8) {
// This unwraps correctly
let args_des: Vec<u8> = serde_json::to_string(&args).unwrap().into_bytes();
// This also returns a Vec<u8>
let args_des = args.0;
// But when trying to extend, append or merge:
let init_fn_args = [
Into::<Vec<u8>>::into(args_des)[..],
serde_json::json!({ "market_creator_account_id": env::signer_account_id() })
.to_string()
.into_bytes()[..],
];
let promise = Promise::new("cc".to_string())
.create_account()
.deploy_contract(MARKET_CODE.to_vec())
.transfer(env::attached_deposit())
.function_call(
"new".to_string(),
init_fn_args, // FAILS with expected struct `std::vec::Vec`, found array `[[u8]; 2]`
0,
GAS_FOR_CREATE_MARKET,
);
// Also tried with
let init_fn_args = args_des.extend(
serde_json::json!({ "market_creator_account_id": env::signer_account_id() })
.to_string()
.into_bytes(),
);
let promise = Promise::new("cc".to_string())
.create_account()
.deploy_contract(MARKET_CODE.to_vec())
.transfer(env::attached_deposit())
.function_call(
"new".to_string(),
init_fn_args, // FAILS with expected struct `std::vec::Vec`, found `()`
0,
GAS_FOR_CREATE_MARKET,
);
}
The most confusing part is expected struct 'std::vec::Vec', found '()'. I still don't understand why it results in () instead of the Vec
The full compiler errors for the first attempt:
error[E0308]: mismatched types
--> src/contract.rs:69:47
|
69 | .function_call("new".to_string(), init_fn_args, 0, GAS_FOR_CREATE_MARKET);
| ^^^^^^^^^^^^ expected struct `Vec`, found array `[[u8]; 2]`
|
= note: expected struct `Vec<u8>`
found array `[[u8]; 2]`
The full compiler errors for the second attempt:
error[E0308]: mismatched types
--> src/contract.rs:69:47
|
69 | .function_call("new".to_string(), init_fn_args, 0, GAS_FOR_CREATE_MARKET);
| ^^^^^^^^^^^^ expected struct `Vec`, found `()`
|
= note: expected struct `Vec<u8>`
found unit type `()`
The first version didn't work because [...] creates an array, not a Vec. To fix it, you should use vec![...] instead.
The second version didn't work because Vec::extend() works by side effect and therefore doesn't return a value. (This is very much like Python where list.append or list.sort return None.) To fix it, you should call args_des.extend(...whatever...) and then use args_des instead of init_fn_args (or declare a let init_fn_args = args_des; after the call to extend()).
Thanks for your answers, what finally worked is this:
pub fn merge(&mut self, args: Base64VecU8) -> Promise {
let mut init_args: Value = serde_json::from_slice(&args.0.as_slice()).unwrap();
init_args.as_object_mut().unwrap().insert(
"another_arg".to_string(),
Value::String(env::signer_account_id().to_string()),
);
// ...
I am running into a problem that I do not really understand and hoped
that somebody might be able to see what I have misunderstood.
The problem is quite straightforward: I have a global state (shared
between several tasks) and want to have an infinite cycle over a
vector in the global state. I will then zip that with an interval
stream and hence get a regular emission of the next value in the
stream.
If the vector in the state changes, the inifinite stream should just
reload the vector and start reading from the new one instead, and
discard the old array.
Here is the code that I've gotten this far, and the questions are at
the end of the post.
use futures::stream::Stream;
use futures::{Async, Poll};
use std::iter::Cycle;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use tokio::timer::Interval;
We define a global state that hold an array that can be
updated. Whenever the array is updated, we will step the version and
set the array.
struct State<T> {
version: u32,
array: Vec<T>,
}
impl<T> State<T> {
fn new(array: Vec<T>) -> Self {
Self {
version: 0,
array: Vec::new(),
}
}
fn update(&mut self, array: Vec<T>) {
self.version += 1;
self.array = array;
}
}
Now, we create an stream over the state. When initialized, it will
read the array and version from the state and store it and then keep
an instance of std::iter::Cycle internally that will cycle over the
array.
struct StateStream<I> {
state: Arc<Mutex<State<I::Item>>>,
version: u32,
iter: Cycle<I>,
}
impl<I> StateStream<I>
where
I: Iterator,
{
fn new(state: Arc<Mutex<State<I::Item>>>) -> Self {
let (version, array) = {
let locked_state = state.lock().unwrap();
(locked_state.version, locked_state.array)
};
Self {
state: state,
version: version,
iter: array.iter().cycle(),
}
}
}
We now implement the stream for the StateStream. With each poll, it
will check if the version of the state changed, and if it did, reload
the array and version.
We will then take the next item from the iterator and return that.
impl<I> Stream for StateStream<I>
where
I: Iterator + Clone,
{
type Item = I::Item;
type Error = ();
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let locked_state = self.state.lock().unwrap();
if locked_state.version > self.version {
self.iter = locked_state.array.clone().iter().cycle();
self.version = locked_state.version;
}
Ok(Async::Ready(self.iter.next()))
}
}
The main program looks like this. I do not update the vector here, but
that is not important for the case at hand.
fn main() {
let state = Arc::new(Mutex::new(State::new(vec![2, 3, 5, 7, 11, 13])));
let primes = StateStream::new(state)
.take(20)
.zip(
Interval::new(Instant::now(), Duration::from_millis(500))
.map_err(|err| println!("Error: {}", err)),
)
.for_each(|(number, instant)| {
println!("fire; number={}, instant={:?}", number, instant);
Ok(())
});
tokio::run(primes);
}
When compiling this, I get the following errors:
cargo run --example cycle_stream_shared
Compiling tokio-testing v0.1.0 (/home/mats/crates/tokio-examples)
error[E0308]: mismatched types
--> examples/cycle_stream_shared.rs:66:19
|
66 | iter: array.iter().cycle(),
| ^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::slice::Iter`
|
= note: expected type `std::iter::Cycle<I>`
found type `std::iter::Cycle<std::slice::Iter<'_, <I as std::iter::Iterator>::Item>>`
error[E0308]: mismatched types
--> examples/cycle_stream_shared.rs:81:25
|
81 | self.iter = locked_state.array.clone().iter().cycle();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::slice::Iter`
|
= note: expected type `std::iter::Cycle<I>`
found type `std::iter::Cycle<std::slice::Iter<'_, <I as std::iter::Iterator>::Item>>`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.
error: Could not compile `tokio-testing`.
To learn more, run the command again with --verbose.
Now, the error and the explanation says that the concrete type is not
possible to derive, but in this case, I am using the generic struct
Cycle<I> and expect I to be instantiated to std::slice::Iter<'_,
I::Item>. Since std::slice::Iter has implemented Iterator and, the type have implemented all necessary traits to match.
Some answers to similar questions exist, but nothing that seems to
match this case:
“Expected type parameter” error in the constructor of a generic
struct is showing that the types do not match (same
as the explanation gives) because the generic struct definition allow any type, but the construction require a specific type.
In this case, we are using a generic type Cycle<I>, where I should implement the Iterator trait, and try to use a type std::slice::Iter<..> that does implement Iterator.
How do I return an instance of a trait from a
method? talk about how to return an arbitrary type
matching a trait, which is not the case here.
The other questions are mostly referring to these two, or variations
of these.
Update: Changed it to be a generic type to demonstrate that it still does not work.
I wrote the following code that filters a stream of data which worked fine until I changed from parsing simple numbers to also have types that are bound to lifetimes like &str and &[u8].
use wirefilter::{ExecutionContext, Filter, Scheme};
lazy_static::lazy_static! {
static ref SCHEME: Scheme = Scheme! {
port: Int,
name: Bytes,
};
}
#[derive(Debug)]
struct MyStruct {
port: i32,
name: String,
}
impl MyStruct {
fn scheme() -> &'static Scheme {
&SCHEME
}
fn filter_matches<'s>(&self, filter: &Filter<'s>) -> bool {
let mut ctx = ExecutionContext::new(Self::scheme());
ctx.set_field_value("port", self.port).unwrap();
ctx.set_field_value("name", self.name.as_str()).unwrap();
filter.execute(&ctx).unwrap()
}
}
fn main() -> Result<(), failure::Error> {
let data = expensive_data_iterator();
let scheme = MyStruct::scheme();
let filter = scheme
.parse("port in {2 5} && name matches \"http.*\"")?
.compile();
for my_struct in data
.filter(|my_struct| my_struct.filter_matches(&filter))
.take(2)
{
println!("{:?}", my_struct);
}
Ok(())
}
fn expensive_data_iterator() -> impl Iterator<Item = MyStruct> {
(0..).map(|port| MyStruct {
port,
name: format!("http {}", port % 2),
})
}
If I try to compile it the compiler will fail with this:
error[E0623]: lifetime mismatch
--> src/main.rs:26:16
|
21 | fn filter_matches<'s>(&self, filter: &Filter<'s>) -> bool {
| ----- ----------
| |
| these two types are declared with different lifetimes...
...
26 | filter.execute(&ctx).unwrap()
| ^^^^^^^ ...but data from `self` flows into `filter` here
error: aborting due to previous error
error: Could not compile `wirefilter_playground`.
To learn more, run the command again with --verbose.
Process finished with exit code 101
my first thought was that self and filter should have the same lifetime in fn filter_matches<'s>(&self, filter: &Filter<'s>) -> bool but if I change the signature to fn filter_matches<'s>(&'s self, filter: &Filter<'s>) -> bool I will start getting this error:
error: borrowed data cannot be stored outside of its closure
--> src/main.rs:38:29
|
33 | let filter = scheme
| ------ ...so that variable is valid at time of its declaration
...
38 | .filter(|my_struct| my_struct.filter_matches(&filter))
| ----------- ^^^^^^^^^ -------------- cannot infer an appropriate lifetime...
| | |
| | cannot be stored outside of its closure
| borrowed data cannot outlive this closure
error: aborting due to previous error
error: Could not compile `wirefilter_playground`.
To learn more, run the command again with --verbose.
Process finished with exit code 101
I am failing to understand the reason, Filter<'s> is bound to SCHEME which is lazily generated and is bound to 'static which makes sense not allowing filter.execute to take reference to &self.name.as_str() because it would be outlived but, isn't filter.execute(&ctx) which the signature is pub fn execute(&self, ctx: &ExecutionContext<'s>) -> Result<bool, SchemeMismatchError> supposed to drop the references as soon as it finishes as the result of it has not other lifetimes?
In order to try and compile the code above, you can use this Cargo.toml:
[package]
name = "wirefilter_playground"
version = "0.1.0"
edition = "2018"
[dependencies]
wirefilter-engine = "0.6.1"
failure = "0.1.5"
lazy_static = "1.3.0"
PS: That could be solved by compiling the as inside filter_matches method but that would be sort of bad because the user would only get the parse error when trying to filter and it could potentially be slower.
I see 2 ways to solve this problem:
1) extend lifetime of self.name. This can be achieved by collecting expensive_data_iterator into, say, Vec.
--- let data = expensive_data_iterator();
+++ let data: Vec<_> = expensive_data_iterator().collect();
2) reduce lifetime of filter.
--- let filter = scheme.parse("...")?.compile();
+++ let filter = scheme.parse("...")?;
--- .filter(|my_struct| my_struct.filter_matches(&filter))
+++ .filter(|my_struct| my_struct.filter_matches(&filter.clone().compile()))
I omitted some other minor changes. And yes, filter_matches<'s>(&'s self, ...) is mandatory in either case.
PS yes, 2nd option works because my_struct outlives filter. Well, if both approaches are somewhat bad, then you can combine them! Process data by chunks, collecting each one into vector.
const N: usize = 10; // or any other size
loop {
let cur_chunk: Vec<_> = data.by_ref().take(N).collect();
if cur_chunk.is_empty() {
break;
}
let cur_filter = filter.clone().compile();
// etc
}
it uses only O(N) memory and compiles filter N times less
I wonder if there is a way to simplify the following pattern match arms when two or more different enum's types have the same data member or same function.
(if not it will be nice to explain why)
UPDATE:
as requested a more accurate example of what i want (forgive me for confusing data member access with function) (try it online):
struct Point<T> {
x: i32,
y: T,
}
enum Record {
V4(Point<i64>),
V6(Point<i32>),
}
fn get_record() -> Record {
Record::V4(Point{ x: 1, y: 1})
}
fn main() {
let x = match get_record() {
Record::V4(r) => r.x,
Record::V6(r) => r.x,
};
println!("{}", &x);
// this will not compile
// let rec = get_record();
// println!("{}", rec.x);
// this will not compile either
// note: if V4 Point was i32 it will compile & run
// let rec = get_record();
// let x = match get_record() {
// Record::V4(r) | Record::V6(r) => r.x,
// };
}
Original Post:
use std::net::IpAddr;
use std::str::FromStr;
fn main() {
let v4_or_v6 = IpAddr::from_str("1.2.3.4").unwrap();
// match expression, both arms only differ by 1 char
let s = match v4_or_v6 {
IpAddr::V4(ip) => ip.to_string(),
IpAddr::V6(ip) => ip.to_string(),
};
println!("{}", &s);
// not working:
// let s2 = match v4_or_v6 {
// IpAddr::V4(ip) | IpAddr::V6(ip) => ip.to_string(),
// };
// println!("{}", &s2);
}
I understand that the underlying call to to_string() has different implementation for Ipv4 than Ipv6 but i think the compiler can be smart enough to handle this (am i wrong?)
trying to compile with the commented out code results in compilation error (try it online):
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:16:37
|
16 | IpAddr::V4(ip) | IpAddr::V6(ip) => ip.to_string(),
| ^^ expected struct `std::net::Ipv4Addr`, found struct `std::net::Ipv6Addr`
|
= note: expected type `std::net::Ipv4Addr`
found type `std::net::Ipv6Addr`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: Could not compile `playground`.
The working code de-sugars to:
let s = match v4_or_v6 {
IpAddr::V4(ip) => <Ipv4Addr as ToString>::to_string(&ip),
IpAddr::V6(ip) => <Ipv6Addr as ToString>::to_string(&ip),
};
Even though the statements look the same, they are different functions and in each branch it is known statically which to_string is going to be used. To get this to work in a single match arm, you would have to somehow produce a trait object from the pattern match, so that each ip has the same type (i.e. &dyn ToString). Currently there isn't a way to do that and I haven't seen any proposal like it.
It's pretty common to see identical-looking match arms, where the same trait method is called on each, even in the rustc project. This is just how it is, for now.
If you have an enum where each variant holds types that implement the same traits, it might be convenient to implement the traits on the enum and delegate to the inner types. If you don't have a trait but your types have common structure (as in the x, y fields in the struct of your updated post), then you can provide an accessor on the enum:
impl Record {
fn x(&self) -> i32 {
match self {
Record::V4(Point { x, .. }) => *x,
Record::V6(Point { x, .. }) => *x,
}
}
}
While this is basically the same thing, it means you can write it once instead of everywhere that you need to access x:
let rec = get_record();
let x = get_record().x();
Note that IpAddr already does this so, in your original code, you could have avoided the match altogether with:
let s = v4_or_v6.to_string();