Convert array into cow - rust

I'm trying to pass an array to a function but getting the following error:
186 | mqtt_client.publish(
| ------- required by a bound introduced by this call
...
190 | buffer,
| ^^^^^^ the trait `From<[u8; 80]>` is not implemented for `Cow<'_, [u8]>`
The array was created by the following line and the content of it is mofidied before calling publish().
let mut buffer: [cty::uint8_t; 80] = [0; 80];
The function is defined as:
fn publish<'a, V>(
&'a mut self,
payload: V,
) -> Result<client::MessageId, Self::Error>
where
V: Into<Cow<'a, [u8]>>,
Having read about Into and Cow I'm still at a loss how to go about solving this issue.

Cow doesn't recognize your specific array type, but it works with arbitrary slices, which arrays convert to trivially. Just change:
mqtt_client.publish(
buffer,
to:
mqtt_client.publish(
Cow::From(&buffer[..]), // Explicit conversion
or:
mqtt_client.publish(
&buffer[..], // Implicit conversion
so Cow is working with a recognized type.

Related

Cannot understand where a reference to a local value is being returned in Rust

I'm struggling to understand where I'm returning a reference to a local value in this function (full code: https://gist.github.com/9f88f9ded8f2f6a1f3b839422a521073):
fn encode_initial_configs<'a, TSym, NTSym>(
alpha: impl IntoIterator<Item = TSym> + 'a,
grammar: &'a OPGrammar<TSym, NTSym>,
chunk_size: usize,
) -> impl Iterator<Item = GPUParseConfig> + 'a
where
TSym: Eq + std::hash::Hash + Clone,
NTSym: Eq + Clone,
{
assert_ne!(chunk_size, 0, "`chunk_size` must be non-zero.");
let mut alpha_chunks = grammar
.encode_iterator(alpha)
.chunks(chunk_size)
.into_iter()
.map(|chunk| chunk.collect_vec())
.fuse();
let curr_chunk = alpha_chunks.next();
InitialConfigs {
alpha_chunks,
last_sym: 0,
curr_chunk,
}
}
The compiler complains about the value returned in the bottom:
error[E0515]: cannot return value referencing temporary value
--> src/par_parse.rs:77:5
|
70 | let mut alpha_chunks = grammar
| ____________________________-
71 | | .encode_iterator(alpha)
72 | | .chunks(chunk_size)
| |___________________________- temporary value created here
...
77 | / InitialConfigs {
78 | | alpha_chunks,
79 | | last_sym: 0,
80 | | curr_chunk,
81 | | }
| |_____^ returns a value referencing data owned by the current function
|
= help: use `.collect()` to allocate the iterator
For more information about this error, try `rustc --explain E0515`.
But the alpha_chunks iterator returned as part of the InitialConfigs instance is obtained by moving every intermediate iterator into the next one. Where is this reference?
EDIT: encode_iterator might be relevant:
pub fn encode_iterator<'a>(
&'a self,
s: impl IntoIterator<Item = TSym> + 'a,
) -> impl Iterator<Item = u32> + 'a {
s.into_iter().map(|sym| self.encode_term(sym))
}
The culprit seems to be .into_iter(), which instead of consuming the result of .chunks(chunk_size), borrows it. In fact, reading the itertools code, IntoIterator is impl'd for &'a IntoChunks<I>, which is the type of the result of chunks. Furthermore, the borrowed reference to IntoChunks is stored in the returned Chunks struct, which is then stored into the iterator returned by map, and so on. A possible solution could be finding or implementing a chunk iterator that can be moved directly into map and does not hold references to local values.
Short answer: You are returning reference to alpha chunk
What's happening?
Let's look at this statement:
let mut alpha_chunks = grammar
.encode_iterator(alpha)
.chunks(chunk_size)
.into_iter()
.map(|chunk| chunk.collect_vec())
.fuse();
Here are the diffusion of statement:
grammer refer to the variable passed into the function itself
endoe_iterator() returns an iterator which which yields the item that is passed as argument. But note that iterator is local in itself, it just yields value that is not local
chunks(), into_iterator(), map(), fuse() all this just manipulate the iterator and keep re-making new iterators type
final iterator of above statement will be stored in alpha_chunks variable which is also local
So you realize that iterators are lazy i.e they don't compute anything unless called with come methods that actually make them to like collect() method. But in you case you never "collected" anything from iterator and the function just return the iterator itself which is local

Read binary u32 using nom

I'm having a hard time finding any useful examples of how to use nom to parse a binary file, since it seems that the documentation is heavily biased towards &str input parsers.
I just want to make a function here that will read 4 bytes, turn it into a u32 and return it as a result. Here is my function:
fn take_u32(input: &[u8]) -> IResult<&[u8], u32> {
map_res(
take(4),
|bytes| Ok(u32::from_be_bytes(bytes))
)(input)
}
I'm getting the following error:
error[E0277]: the trait bound `[u8; 4]: From<u8>` is not satisfied
--> src\main.rs:16:9
|
16 | take(4),
| ^^^^ the trait `From<u8>` is not implemented for `[u8; 4]`
|
::: C:\Users\cmbas\.cargo\registry\src\github.com-1ecc6299db9ec823\nom-7.1.0\src\bits\complete.rs:39:6
|
39 | O: From<u8> + AddAssign + Shl<usize, Output = O> + Shr<usize, Output = O>,
| -------- required by this bound in `nom::complete::take`
What is the canonical way to do what I'm attempting?
take() return Self aka your input, In your case slice &[u8].
map_res() is used to map a Result, from_be_bytes() doesn't return a result, the doc also contains an example using TryInto::try_into.
To make your code compile you need to use map_opt() and use try_into() to transform the slice to array:
fn take_u32(input: &[u8]) -> IResult<&[u8], u32> {
map_opt(
take(4),
|bytes| int_bytes.try_into().map(u32::from_be_bytes)
)(input)
}
That said nom already have such basic conbinator be_u32

How to sort a Vec<(Cow<str>, Cow<str>) by_key without cloning?

I have a vector of tuples containg key and value pairs and I'd like to sort them by the key. I would like to avoid calling .to_string() on the Cows. I can't seem to find a way to do this without cloning.
use std::borrow::Cow;
fn main() {
let mut v: Vec<(Cow<str>, Cow<str>)> = vec![("a".into(), "xd".into()), ("0".into(), "xy".into())];
v.sort_by_key(|(k,_v)| k);
dbg!(v);
}
Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
--> src/main.rs:4:28
|
4 | v.sort_by_key(|(k,_v)| k);
| ------- ^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 Cow<'_, str>
| has type `&'1 (Cow<'_, str>, Cow<'_, str>)`
error: aborting due to previous error
error: could not compile `playground`
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=72a529fa5b0d39997d5e3738db9c291a
What I tried
I've tried also creating a function instead of a closure so I could assign the same lifetime to the input arguments and the output (See playground), but then I get an error about an invalid signature.
A compile-able solution (See playground) is to clone the Cow which is fine if the Cow is Borrowed, but if it's not Borrowed then why do I need to clone the underlying String? Can't I just call Deref the String into &str?
Also tried to match explicitly the different Cow variants, but the error is very similar to the first one (See playground).
Error message
Most of all I don't understand the error message: "returning this value requires that '1 must outlive '2". Ok I accept that that is required, but why is this an error?
First of all, I`ll simplify your code a bit for two reasons:
To make it more idiomatic
and remove unnecessary code
fn main() {
let mut vector : Vec<(String, String)> = vec![(String::from("a"), String::from("xd")), (String::from("0"), String::from("xy"))];
dbg!(vector);
}
So far, so good.
To sort the vector avoiding the method call .to_string(), we can do it with the function sort_by code (See the playground):
vector.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
Note that the function cmp does not return a copy of the key but instead the function cmp returns an Ordering:
pub fn cmp(&self, other: &Self) -> Ordering
The Ordering indicates that a compared value X is [less, equal, greater] than another Y ( X.cmp(Y) ).
Other option is to use the function partial_cmp:
vector.sort_by(|(k1, _), (k2, _)| k1.partial_cmp(k2).unwrap());
The function partial_cmp returns an Option<Ordering> enumeration. This is because we use the unwrap method.
Another option (which does not solve the problem as you want) is using the function sort_by_key:
vector.sort_by_key(|(k1, _)| String::from(k1));
But since this function returns the key, it is a requirement to create a new one to avoid the problem of the lifetime.
Just use sort_by instead of sort_by_key:
v.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
Most of all I don't understand the error message
The problem is sort_by_key's function declaration:
pub fn sort_by_key<K, F>(&mut self, f: F)
where
F: FnMut(&T) -> K,
K: Ord
This shows that sort_by_key accepts a closure which returns a type K, and &T doesn't have to outlive K. If it were instead defined as
pub fn sort_by_key<'a, K, F>(&mut self, f: F)
where
F: FnMut(&'a T) -> K,
K: Ord + 'a
Then it would work in this case. But it isn't, so we have to live with it :/

Rust rusqlite cannot insert row

The following code fails to compile with:
55 | (":dataset_id", &dataset_id),
| ^^^^^^^^^^^ expected `u32`, found `i32`
pub fn save(&mut self, annotations: Vec<Record>, dataset_id: i32) -> Result<(), Error> {
let mut tx = self.conn.transaction()?;
for record in records {
let json: String = record();
let sql: &str =
"INSERT INTO records (record_id, dataset_id, value)
VALUES (:record_id, :dataset_id, :value)";
let mut statement = tx.prepare(sql)?;
statement.execute(&[
(":record_id", &record.id),
(":dataset_id", &dataset_id),
(":value", "hello world")]);
};
tx.commit()?;
Ok(())
}
And if I remove dataset_id from my SQL statement and comment out the line: (":dataset_id", &dataset_id),
Then it fails to compile with:
56 | (":value", &"hello".to_string()),
| ^^^^^^^^^^^^^^^^^^^^ expected `u32`, found struct `std::string::String`
The argument to execute is a P: Params, i.e. "any type that implements Params". The Rust compiler will not make a guess at what specific type you want and then process the argument accordingly. Instead, it will just resolve the type of the argument on its own and then see if it implements Params.
This is your argument:
&[(":record_id", &record.id),
(":dataset_id", &dataset_id),
(":value", "hello world")]
On its own, what is the type? It's a reference to an array literal containing three tuples: a (&str, &u32), a (&str, &i32), and a (&str, &str).
Lacking any further information, the compiler guesses that the first element is the canonical one, and thus tries to convert the others accordingly. Thus you get the "cannot convert &i32 to &u32" errors.
What you need, however, is an array of (&str, &dyn ToSql) tuples.
So you can do one of two things.
First, explicitly cast the first param value to the right type:
&[(":record_id", &record.id as &dyn ToSql),
(":dataset_id", &dataset_id),
(":value", "hello world")]
Or second, use the named_params! macro that rusqlite provides, which is arguably prettier:
statement.execute(named_params!{
":record_id": record.id,
":dataset_id": dataset_id,
":value": "hello world",
});

How to append to string values in a hash table in Rust?

I have source files that contain text CSV lines for many products for a given day. I want to use Rust to collate these files so that I end up with many new destination CSV files, one per product, each containing portions of the lines only specific to that product.
My current solution is to loop over the lines of the source files and use a HashMap<String, String> to gather the lines for each product. I split each source line and use the element containing the product ID as a key, to obtain an Entry (occupied or vacant) in my HashMap. If it is vacant, I initialize the value with a new String that is allocated up-front with a given capacity, so that I can efficiently append to it thereafter.
// so far, so good (the first CSV item is the product ID)
let mystringval = productmap.entry(splitsource[0].to_owned()).or_insert(String::with_capacity(SOME_CAPACITY));
I then want to append formatted elements of the same source line to this Entry. There are many examples online, such as
https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.entry
of how to make this work if the HashMap value is an integer:
// this works if you obtain an Entry from a HashMap containing int vals
*myval += 1;
I haven't figured out how to append more text to the Entry I obtain from my HashMap<String, String> using this kind of syntax, and I've done my best to research examples online. There are surprisingly few examples anywhere of manipulating non-numeric entries in Rust data structures.
// using the Entry obtained from my first code snippet above
*mystringval.push_str(sourcePortion.as_str());
Attempting to compile this produces the following error:
error: type `()` cannot be dereferenced
--> coll.rs:102:17
|
102 | *mystringval.push_str(sourcePortion.as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How can I append to a String inside the Entry value?
*mystringval.push_str(sourcePortion.as_str()); is parsed as *(mystringval.push_str(sourcePortion.as_str())); and since String::push_str returns (), you get the () cannot be dereferenced error.
Using parentheses around the dereference solves the precedence issue:
(*mystringval).push_str(sourcePortion.as_str());
The reason *myval += 1 works is because unary * has a higher precedence than +=, which means it's parsed as
(*myval) += 1
Since or_insert returns &mut V, you don't need to dereference it before calling its methods. The following also works:
mystringval.push_str(sourcePortion.as_str());
If you inspect the type returned by or_insert:
fn update_count(map: &mut HashMap<&str, u32>) {
let () = map.entry("hello").or_insert(0);
}
You will see it is a mutable reference:
error[E0308]: mismatched types
--> src/main.rs:4:9
|
4 | let () = map.entry("hello").or_insert(0);
| ^^ expected &mut u32, found ()
|
= note: expected type `&mut u32`
found type `()`
That means that you can call any method that needs a &mut self receiver with no extra syntax:
fn update_mapping(map: &mut HashMap<&str, String>) {
map.entry("hello").or_insert_with(String::new).push_str("wow")
}
Turning back to the integer form, what happens if we don't put the dereference?
fn update_count(map: &mut HashMap<&str, i32>) {
map.entry("hello").or_insert(0) += 1;
}
error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut i32`
--> src/main.rs:4:5
|
4 | map.entry("hello").or_insert(0) += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use `+=` on type `&mut i32`
error[E0067]: invalid left-hand side expression
--> src/main.rs:4:5
|
4 | map.entry("hello").or_insert(0) += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid expression for left-hand side
The difference is that the += operator automatically takes a mutable reference to the left-hand side of the expression. Expanded, it might look something like this:
use std::ops::AddAssign;
fn update_count(map: &mut HashMap<&str, i32>) {
AddAssign::add_assign(&mut map.entry("hello").or_insert(0), 1);
}
Adding the explicit dereference brings the types back to one that has the trait implemented:
use std::ops::AddAssign;
fn update_count(map: &mut HashMap<&str, i32>) {
AddAssign::add_assign(&mut (*map.entry("hello").or_insert(0)), 1);
}

Resources