Issue
I've been working on an Exercism problem (acronyms) in the Rust track for the past week plus. And I'm stuck trying to satisfy the compiler (I think the borrow checker to be more specific but I'm not entirely sure when something is borrow checker or not).
Attempted solution:
I was porting a solution of the same problem from Haskell and Swift to Rust. The solution is uses spitter combinators and I didn't have many issues porting the Haskell library to Swift. So I'm trying to do the same with Rust. The basic idea is to traverse a vector of characters. We create chunks that are either delimiters or text. There is also a condense policy that dictates how we should handle delimiters and blanks. condense (the function I'm having difficulty with) is recursive in nature (maybe relevant, I'm not sure) and combines contiguous delimiters together. For example, if you have a vector of chunks [Text(it_doesnt_matter), Delimiter("a"), Delimiter("b"), Delimiter("c"), Text(shrug)], then condense would return [Text(it_doesnt_matter), Delimiter(vec!["a", "b", "c"]), Text(shrug)] (or something similar).
The Chunk is as follows:
#[derive(Clone)]
pub enum Chunk<A> {
Delimit(Vec<A>),
Text(Vec<A>)
}
impl<A> Chunk<A> {
// convenience methods
}
and the CondensePolicy is as follows
enum CondensePolicy {
Condense,
DropBlanks,
KeepBlanks,
}
impl CondensePolicy {
fn condense<A: Clone>(&self, list: Vec<Chunk<A>>) -> &Vec<Chunk<A>> {
match (self, list.first()) {
(Self::DropBlanks, _) | (Self::KeepBlanks, _) => &list,
(Self::Condense, None) => &vec![],
(Condense, Some(first @ Chunk::Text(xs))) => &vec![Chunk::Text(xs.clone())]
.iter()
.chain(list.iter().rev().skip(1).rev())
.cloned()
.collect(),
(Condense, Some(Chunk::Delimit(xs))) => {
let (delimiters, others) = span(list, |x| x.is_delimit());
let vector = vec![Chunk::Delimit(
delimiters
.iter()
.flat_map(|&x| match x {
Chunk::Delimit(xss) => xss.clone(),
Chunk::Text(xss) => xss.clone(),
})
.collect(),
)];
let ys = &vector
.iter()
.cloned()
.chain(self.condense(others).iter().cloned());
&vector
.iter()
.cloned()
.chain(self.condense(others).iter().cloned())
.cloned() // OFFENDING LINE
.collect()
}
}
}
}
fn span<'a, A, F>(xs: Vec<A>, predicate: F) -> (Vec<A>, Vec<A>)
where
A: Clone,
F: Fn(&A) -> bool,
{
match xs.iter().position(predicate) {
None => (xs.into_iter().collect(), vec![]),
Some(index) => (
xs.iter().take(index).cloned().collect(),
xs.iter().skip(index).cloned().collect(),
),
}
}
Consistent problem I don't know how to address:
The above details blocks of code yield the following error that I don't know how to resolve in this case:
Error message 1:
type mismatch resolving
`<std::iter::Chain<std::iter::Cloned<std::slice::Iter<'_, chunk::Chunk<A>>>, std::iter::Cloned<std::slice::Iter<'_, chunk::Chunk<A>>>> as std::iter::Iterator>::Item == &_`
expected enum `chunk::Chunk`, found reference
note: expected type `chunk::Chunk<A>`
found reference `&_`
This version of the code is the closest I've been to compiling. Another similar solution yielded another error which I wasn't sure how to handle when using chain or using collect:
Error message 2:
a value of type `std::vec::Vec<chunk::Chunk<A>>` cannot be built from an iterator over elements of type `&chunk::Chunk<A>`
and several similar error messages with different references types (more &). I resolved this error by enforcing A to implement the Clone trait.
Questions:
- Which traits are a good idea to implement / derive on your custom types?
- Are there any traits you should avoid with generic types like
Chunk<A>? - Are there any traits you should include with generic types like
Chunk<A>? - How do I resolve Error message 1?
- Was adding
<A: Clone>a / the correct way to resolve Error message 2? - How do I remove the
.cloned()and.clone()?- I think I have two many but I'm not sure how to remove them. I'm used to Swift's copy-on-write semantics and value types, which seem to be somewhat related to Rust's references when properly borrowed / managed. But I could be completely wrong. I'm aware of
COWbut haven't done a deep dive yet into how to use it.
- I think I have two many but I'm not sure how to remove them. I'm used to Swift's copy-on-write semantics and value types, which seem to be somewhat related to Rust's references when properly borrowed / managed. But I could be completely wrong. I'm aware of