I find it difficult to understand what's wrong with the below code. I'm getting expected struct Vec, found enum Result error at Ok(from_cache), but I have adopted the code from https://github.com/platy/update-tracker/blob/843092708906063704442f352231bfbac5b06196/server/src/web/mod.rs#L216-L226
During web scraping, I'm trying to cache the content of the URL in the cache and trying to reuse it.
use std::error::Error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let url: &str = "https://example.com/";
let html = match cacache::read("./cache", url).await? {
Ok(from_cache) => String::from_utf8(from_cache),
Err(_) => {
let t_html = reqwest::get(url).await?.text().await?;
cacache::write("./cache", url, &t_html).await?;
t_html
},
};
println!("html = {:?}", html);
Ok(())
}
Here's the playground (but, it shows other errors due to missing dependencies). Can anyone please explain this or share any relevant guide to gather more information about this topic?
Recall that the ? operator unwraps a Result (or Option) by propagating the Err (or None) case out of the current function. Therefore, this expression:
cacache::read("./cache", url).await?
Has type Vec<u8> as the ? operator has unwrapped the Result. If you want to handle errors yourself, then omit the ? operator:
cacache::read("./cache", url).await
Related
I'm trying to iterate depth-first over a tree structure in Rust. I thought I had a really nice concise solution for this, but I can't get it to compile. Conceptually it's pretty simple: iterate over the children, get each child's depth first iterator, flatten them, and chain the current node's metadata iterator to it.
#[derive(Debug, Eq, PartialEq)]
struct Node {
metadata: Vec<i64>,
children: Vec<Box<Node>>,
}
impl Node {
fn depth_first_metadata_iter(&self) -> impl Iterator<Item = &i64> + '_ {
self.children
.iter()
.map(|child| child.depth_first_metadata_iter())
.flatten()
.chain(self.metadata.iter())
}
}
fn main() {
let tree = Node {
metadata: vec![1, 2, 3],
children: vec![
Box::new(Node {
metadata: vec![4, 5],
children: vec![],
}),
Box::new(Node {
metadata: vec![6, 7],
children: vec![],
}),
],
};
println!(
"{:?}",
tree.depth_first_metadata_iter().collect::<Vec<&i64>>()
);
}
However, when I compile this, I get the following error:
error[E0275]: overflow evaluating the requirement `impl std::iter::Iterator`
|
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
(You can check this out yourself on the playground.)
It makes sense that this would be an error, as I am making recursive calls inside depth_first_metadata_iter which return nested iterators, but it would be really nice if something like this code could work without having to implement a custom iterator.
All other solutions to the E0275 error I have seen (eg. this, this, this) seem to involve strategically placing a type annotation somewhere - is something like that possible here, or am I trying something "impossible"?
if something like this code could work
Depends on how "like" you mean. This is similar, works, and doesn't require a custom iterator; thus meeting all of your requirements:
fn depth_first_metadata_iter(&self) -> Box<Iterator<Item = &i64> + '_> {
Box::new({
self.children
.iter()
.flat_map(|child| child.depth_first_metadata_iter())
.chain(self.metadata.iter())
})
}
At the heart, this is the same problem as shown in
What does "Overflow evaluating the requirement" mean and how can I fix it?
"Overflow evaluating the requirement" but that kind of recursion should not happen at all
Curiously recurring generic trait pattern: overflow evaluating the requirement
Put yourself in the compiler's shoes for a while. Your original code says "I'm going to return a concrete iterator type, but I'm not going to say the exact type". The compiler still has to be able to figure out that type, so let's be the compiler:
let a = self.children.iter();
// std::slice::Iter<'_, Box<Node>>
let cls = |child| child.depth_first_metadata_iter();
// Fn(&Box<Node>) -> ?X?
let b = a.flat_map(cls);
// FlatMap<Iter<'_, Box<Node>>, ?X?, Fn(&Box<Node>) -> ?X?>
let d = self.metadata.iter();
// std::slice::Iter<'_, i64>
b.chain(d);
// Chain<FlatMap<Iter<'_, Box<Node>>, ?X?, Fn(&Box<Node>) -> ?X?>, Iter<'_, i64>>
This end result is the return value, so we have our equation:
Chain<FlatMap<Iter<'_, Box<Node>>, ?X?, Fn(&Box<Node>) -> ?X?>, Iter<'_, i64>> === ?X?
AFAIK, it's impossible to perform the type-level algebra to solve for ?X?, thus you get the error.
Changing the return type to a boxed trait object short circuits all of the logic needed and forces a specific concrete type.
strategically placing a type annotation somewhere
I don't believe this to be the case. If so, that would mean that the algebra is solvable but that the compiler isn't smart enough to solve it. While this is undoubtedly true in other situations, I don't think it is here.
I don't think this is a great solution, as this will involve lots of tiny allocations. I'd assume (but have not tested) that a custom iterator using a stack data structure would be more efficient.
A middle ground would be to build up the entire set of nodes:
impl Node {
fn depth_first_metadata_iter(&self) -> impl Iterator<Item = &i64> + '_ {
self.x().into_iter()
}
fn x(&self) -> Vec<&i64> {
fn x_inner<'a>(node: &'a Node, v: &mut Vec<&'a i64>) {
for c in &node.children {
x_inner(c, v)
}
v.extend(&node.metadata);
}
let mut v = Vec::new();
x_inner(self, &mut v);
v
}
}
I am working through the Rust book, namely the minigrep project. There I came across the following snippet:
fn main() {
let args: Vec<String> = env::args().collect();
let (query, filename) = parse_config(&args);
// --snip--
}
fn parse_config(args: &[String]) -> (&str, &str) {
let query = &args[1];
let filename = &args[2];
(query, filename)
}
The confusing piece for me is args: &[String]. If I replace it with args: &Vec<String>, it also works. My guess is that &[String] is a more general type annotation that matches not only &Vec<String>, but also some other types. Is that correct? If so, what other types are matched by [T]?
Generally speaking, [T] is a contiguous sequence and &[T] is a slice.
The reason why the compiler allows &[String] instead of &Vec<String> is that Vec<T> dereferences to [T]. This is called Deref coercion. It can be said that the former notation (in function parameters) is more general; it is also the preferred one. Further details about automatic dereferencing rules can be found in this question.
What I am trying to do:
enum Test {
Value1,
Value2,
Value3
}
fn main() {
let mut test_vec: Vec<Test> = Vec::new();
test_vec.push(Test::Value2);
if let Some(last) = test_vec.last() {
test_vec.push(*last);
}
//Wanted output: vector with [Test::Value2, Test::Value2]
}
I understand that when I call last(), it will return Option<&Test>
So it will borrow the test_vec till the end of the if-let block.
I tried the following without success:
if let Some(last) = test_vec.last().map(|v| v.clone()) {
test_vec.push(*last);
}
//and
let last = test_vec.last().unwrap().clone();
test_vec.push(*last);
When trying to figure out why the borrow checker complains it can be useful to identify the types involved.
If you type out:
let _: () = test_vec.last().map(|v| v.clone());
you get an error complaining that () and core::option::Option<&Test> are not the same type.
What's going on? Very simply put, if you clone an &Test you get an &Test, thus calling .map(|v| v.clone()) on a Option<&Test> gives an Option<&Test>. Obviously, it still borrows.
The same problem occurs with your next attempt, if you type out:
let _: () = test_vec.last().unwrap().clone();
you get an error complaining that () and &Test are not the same type.
By calling unwrap on an Option<&Test> you get an &Test which is then cloned into an &Test.
So, the problem is a lack of dereferencing. You need to dereference earlier, to avoid borrowing test_vec in Some(last):
if let Some(last) = test_vec.last().map(|v| (*v).clone()) {
test_vec.push(last);
}
of course, this does not work because Test does not implement clone. Once that is fixed (by #[derive(Clone)]), it compiles.
Since cloning from references is such a common need, there is a dedicated method on Option (and Iterator) called cloned:
if let Some(last) = test_vec.last().cloned() {
test_vec.push(last);
}
To solve the borrow problem you can call Option::cloned, that produces a Option<T> from a Option<&T>. To do this, Test must implement Clone. You can implement Clone for Test using derive:
// To allow assert_eq, we also derive Debug and PartialEq
#[derive(Debug, PartialEq, Clone)]
enum Test {
Value1,
Value2,
Value3
}
fn main() {
let mut test_vec = Vec::new();
test_vec.push(Test::Value2);
if let Some(last) = test_vec.last().cloned() {
test_vec.push(last);
}
assert_eq!(vec![Test::Value2, Test::Value2], test_vec);
}
I'm working on a code challenge which will detect case-insensitive anagrams of a given word from a list of words.
My first cut is to use something like this:
pub fn anagrams_for(s: &'static str, v: &[&'static str]) -> Vec<&'static str> {
let mut outputs: Vec<&str> = vec![];
// Find the case-insensitive, sorted word to check
let mut s_sorted: Vec<_> = s.to_string().to_lowercase().chars().collect();
s_sorted.sort();
for word in v {
// Case-desensitize and sort each word in the slice
let mut word_sorted: Vec<_> = word.to_string().to_lowercase().chars().collect();
word_sorted.sort();
// if the case-insensitive words are the same post sort and not presort (to avoid self-anagrams), add it to the vector
if word_sorted == s_sorted && s.to_string().to_lowercase() != word.to_string().to_lowercase() {
outputs.push(word)
}
}
outputs
}
This works as expected, but is not very idiomatic. I'm now trying a second iteration which uses more functional features of Rust:
pub fn anagrams_for(s: &'static str, v: &[&'static str]) -> Vec<&'static str> {
let mut s_sorted: Vec<_> = s.to_string().to_lowercase().chars().collect();
s_sorted.sort();
v.iter().map(&|word: &str| {
let mut word_sorted: Vec<_> = word.to_string().to_lowercase().chars().collect();
word_sorted.sort();
if word_sorted == s_sorted && s.to_string().to_lowercase() != word.to_string().to_lowercase() {
word
}
}).collect()
}
I'm currently getting a few errors (most of which I could likely resolve), but the one I'm interested in solving is
if may be missing an else clause:
expected `()`,
found `&str`
(expected (),
found &-ptr) [E0308]
This is because in the case of a non-anagram, map attempts to push something into the vector (seemingly ()).
How can I handle this? It's possible that map isn't the best idiom because it requires some operation to be performed on each element in a list, not a subset (maybe filter?).
As you noticed, the problem is that in the non-anagram-case your closure (the || { ... } block) doesn't return a value.
You can solve this by using filter_map instead of map. That function takes a closure that returns Option<U> instead of U, so the last expression of your closure looks something like:
if /* ... */ {
Some(word)
} else {
None
}
Unrelated to the main question, some notes on your code:
You can remove the .to_string() calls before .to_lowercase() calls. the latter method belongs to the type str, so it works fine. Calling to_string() adds unnecessary allocations.
the & in front of the closure (&|...|) can most probably be removed...
... as can the : &str type annotation in the closures argument list
I am trying to parse a string into a list of floating-point values in Rust. I would assume there is a clever way to do this using iterators and Options; however, I cannot get it to ignore the Err values that result from failed parse() calls. The Rust tutorial doesn't seem to cover any examples like this, and the documentation is confusing at best.
How would I go about implementing this using the functional-style vector/list operations? Pointers on better Rust style for my iterative implementation would be appreciated as well!
"Functional"-style
Panics when it encounters an Err
input.split(" ").map(|s| s.parse::<f32>().unwrap()).collect::<Vec<_>>()
Iterative-style
Ignores non-float values as intended
fn parse_string(input: &str) -> Vec<f32> {
let mut vals = Vec::new();
for val in input.split_whitespace() {
match val.parse() {
Ok(v) => vals.push(v),
Err(_) => (),
}
}
vals
}
fn main() {
let params = parse_string("1 -5.2 3.8 abc");
for ¶m in params.iter() {
println!("{:.2}", param);
}
}
filter_map does what you want, transforming the values and filtering out Nones:
input.split(" ").filter_map(|s| s.parse::<f32>().ok()).collect::<Vec<_>>();
Note the ok method to convert the Result to an Option.