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",
});
Related
I am trying to concatenate every element in a slice of &strs (&[&str]) into a single owned String. For example, I want to turn &['Hello', ' world!'] into "Hello world!".
I tried to do this by converting the slice into an iterator, then mapping over the iterator and converting each &str into an owned String, then collect them all into a Vec<String> and running .join(""), but I keep getting a type error.
Here is my code:
fn concat_str(a: &[&str]) -> String {
a.into_iter().map(|s| s.to_owned()).collect::<Vec<String>>().join("")
}
fn main() {
let arr = ["Dog", "Cat", "Bird", "Lion"];
println!("{}", concat_str(&arr[..3])); // should print "DogCatBird"
println!("{}", concat_str(&arr[2..])); // should print "BirdLion"
println!("{}", concat_str(&arr[1..3])); // should print "CatBird"
}
And here is the compiler error that I am getting:
error[E0277]: a value of type `Vec<String>` cannot be built from an iterator over elements of type `&str`
--> code.rs:2:38
|
2 | a.into_iter().map(|s| s.to_owned()).collect::<Vec<String>>().join("")
| ^^^^^^^ value of type `Vec<String>` cannot be built from `std::iter::Iterator<Item=&str>`
|
= help: the trait `FromIterator<&str>` is not implemented for `Vec<String>`
note: required by a bound in `collect`
--> /Users/michaelfm1211/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:1780:19
|
1780 | fn collect<B: FromIterator<Self::Item>>(self) -> B
| ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
It says that I cannot collect into a Vec<String> because the iterator is not of a String, but I thought that I had converted it to a String with .map(|s| s.to_owned()).
How can I fix this? I am also new to rust, so it would be very helpful is someone could explain what I did wrong.
into_iter will yield an iterator with Item=&&str. In your map, .to_owned() converts that into a &str, which doesn't work. There are a few ways to fix that, you could use .copied or .cloned to get a &str:
a.into_iter().copied().map(|s| s.to_owned()).collect::<Vec<String>>().join("")
// or
a.into_iter().cloned().map(|s| s.to_owned()).collect::<Vec<String>>().join("")
Or you could use .to_string() to get a String directly:
a.into_iter().map(|s| s.to_string()).collect::<Vec<String>>().join("")
Note, you can also just collect into a String when you don't want a separator directly:
a.into_iter().map(|s| s.to_string()).collect::<String>()
There are the methods join and concat on slice directly, so you can write:
fn concat_str(a: &[&str]) -> String {
a.concat()
}
which gives you the desired output
fn concat_str(a: &[&str]) -> String {
a.iter()
.cloned()
.collect()
}
In this toy example I'd like to map the items from a HashMap<String, String> with a helper function. There are two versions defined, one that takes arguments of the form &String and another with &str. Only the &String one compiles. I had thought that String always dereferences to &str but that doesn't seem to be the case here. What's the difference between a &String and a &str?
use std::collections::HashMap;
// &String works
fn process_item_1(key_value: (&String, &String)) -> String {
let mut result = key_value.0.to_string();
result.push_str(", ");
result.push_str(key_value.1);
result
}
// &str doesn't work (type mismatch in fn arguments)
fn process_item_2(key_value: (&str, &str)) -> String {
let mut result = key_value.0.to_string();
result.push_str(", ");
result.push_str(key_value.1);
result
}
fn main() {
let mut map: HashMap<String, String> = HashMap::new();
map.insert("a".to_string(), "b".to_string());
for s in map.iter().map(process_item_2) { // <-- compile error on this line
println!("{}", s);
}
}
Here's the error for reference:
error[E0631]: type mismatch in function arguments
--> src/main.rs:23:29
|
12 | fn process_item_2(key_value: (&str, &str)) -> String {
| ---------------------------------------------------- found signature of `for<'r, 's> fn((&'r str, &'s str)) -> _`
...
23 | for s in map.iter().map(process_item_2) {
| ^^^^^^^^^^^^^^ expected signature of `fn((&String, &String)) -> _`
Thanks for your help with a beginner Rust question!
It goes even stranger than that:
map.iter().map(|s| process_item_2(s)) // Does not work
map.iter().map(|(s1, s2)| process_item_2((s1, s2))) // Works
The point is that Rust never performs any expensive coercion. Converting &String to &str is cheap: you just take the data pointer and length. But converting (&String, &String) to (&str, &str) is no so cheap anymore: you have to take the data+length of the first string, then of the second string, then concatnate them together (and also, if it was done for tuple, what about (((&String, &String, &String), &String), (&String, &String))? And it was probably done then for arrays too, so what about &[&String; 10_000]?) That's why the first closure fails. The second closure, however, destruct the tuple and rebuild it. That means that instead of coercing a tuple, we coerce &String twice, and build a tuple from the results. That's fine.
The version without the closure is even more expensive: since you're passing a function directly to map(), and map produces &String, someone needs to convert this to &str! In order to do that, the compiler would have to introduce a shim - a small function that does that works: it takes (&String, &String) and calls process_item_2() with the (&String, &String) coerced to (&str, &str). This is a hidden cost, so Rust (almost) never creates shims. This is why it wouldn't work even for &String and not just for (&String, &String). And why |v| f(v) is not always the same as f - the first one performs coercions, while the second doesn't.
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 :/
I'm learning Rust and one thing didn't seem right
let vector: VecDeque<u32> = VecDeque::new();
How can the new method know which type of VecDeque to return? Could be VecDeque<T> for any T. I come from C++ where, if there were a static new method it'd have to be templated and I'd have to call like this: CppObject<int> cppObject = CppObject<int>() for example.
Rust does a lot of type inference. In this case, it knows to call VecDeque::<u32>::new() because you explicitly specified that you want to assign it to a variable of type VecDeque<u32> and the only way you can get there is if you call the associated method new() on exactly the type VecDeque<u32>.
Note that if you did not annotate the type of vector, it would fail to compile:
use std::collections::VecDeque;
fn main() {
let vector = VecDeque::new();
}
error[E0282]: type annotations needed for `std::collections::VecDeque<T>`
--> src/main.rs:4:18
|
4 | let vector = VecDeque::new();
| ------ ^^^^^^^^^^^^^ cannot infer type for type parameter `T`
| |
| consider giving `vector` the explicit type `std::collections::VecDeque<T>`, where the type parameter `T` is specified
But the compiler will also successfully infer the type if there are operations afterwards that make clear that VecDeque elements are of type u32:
use std::collections::VecDeque;
fn main() {
let mut vector = VecDeque::new(); // type not yet known
vector.push_back(123); // append untyped integer, type not yet known
let expected: u32 = 123;
assert_eq!(vector[0], expected); // A-ha! vector[0] returns T
// and T is compared with u32,
// so it must follow that T is u32
}
The idea is to send a set of characters of a vector and let the function display the current correct guesses.
Here is my main:
fn main() {
let mut guessedLetters = vec![];
displayWord(guessedLetters);
}
And here is the function:
fn displayWord(correctGuess: Vec<char>) {
let mut currentWord = String::new();
for x in 0..5 {
currentWord.push(correctGuess[x]);
}
println!("Current guesses: {}", currentWord);
}
I don't know what I'm supposed to write inside the parameters of displayWord.
There's a couple of things wrong with your code.
The first error is pretty straight forward:
--> src/main.rs:38:25
|
38 | displayWord(guessed_Letters);
| ^^^^^^^^^^^^^^^ expected char, found enum `std::option::Option`
|
= note: expected type `std::vec::Vec<char>`
found type `std::vec::Vec<std::option::Option<char>>`
The function you wrote is expecting a vector a characters ... but you're passing it a vector of Option<char>. This is happening here:
guessed_Letters.push(line.chars().nth(0));
According to the documentation, the nth method returns an Option. The quick fix here is to unwrap the Option to get the underlying value:
guessed_Letters.push(line.chars().nth(0).unwrap());
Your next error is:
error[E0382]: use of moved value: `guessed_Letters`
--> src/main.rs:38:25
|
38 | displayWord(guessed_Letters);
| ^^^^^^^^^^^^^^^ value moved here in previous iteration of loop
|
= note: move occurs because `guessed_Letters` has type `std::vec::Vec<char>`, which does not implement the `Copy` trait
This is transferring ownership of the vector on the first iteration of the loop and the compiler is telling you that subsequent iterations would be in violation of Rust's ownership rules.
The solution here is to pass the vector by reference instead:
displayWord(&guessed_Letters);
..and your method should also accept a reference:
fn displayWord(correctGuess: &Vec<char>) {
let mut currentWord = String::new();
for x in 0..5 {
currentWord.push(correctGuess[x]);
}
println!("Current guesses: {}", currentWord);
}
This can be shortened to use a slice and still work:
fn displayWord(correctGuess: &[char]) {