Rust compiler complaints about 2 almost identical closures - rust

I have this simple struct with 2 Hashsets:
pub struct IpAddresses {
pub ipv4s: HashSet<String>,
pub ipv6s: HashSet<String>,
}
and then a simple function which is supposed to provide an iterator to one of the sets:
pub fn shared2(&self, ipv6: bool) -> impl Iterator<Item = IpAddr> + '_ {
if ipv6 {
self
.ipv6s
.iter()
.filter_map(|a| IpAddr::from_str(a).ok())
} else {
self
.ipv4s
.iter()
.filter_map(|a| IpAddr::from_str(a).ok())
}
}
I get the following error with the suggestion to use box:
error[E0308]: `if` and `else` have incompatible types
--> src/models/ip_address.rs:131:13
|
125 | / if ipv6 {
126 | | self
| _|_____________-
127 | | | .ipv6s
128 | | | .iter()
129 | | | .filter_map(|a| IpAddr::from_str(a).ok())
| |_|_____________________________________________________- expected because of this
130 | | } else {
131 | / | self
132 | | | .ipv4s
133 | | | .iter()
134 | | | .filter_map(|a| IpAddr::from_str(a).ok())
| |_|_____________________________________________________^ expected closure, found a different closure
135 | | }
| |_________- `if` and `else` have incompatible types
|
= note: expected type `FilterMap<std::collections::hash_set::Iter<'_, _>, [closure#src/models/ip_address.rs:129:25: 129:53]>`
found struct `FilterMap<std::collections::hash_set::Iter<'_, _>, [closure#src/models/ip_address.rs:134:25: 134:53]>`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
help: you could change the return type to be a boxed trait object
|
122 | pub fn shared2(&self, ipv6: bool) -> Box<dyn Iterator<Item = IpAddr> + '_> {
| ~~~~~~~ +
help: if you change the return type to expect trait objects, box the returned expressions
|
126 ~ Box::new(self
127 | .shared_ipv6s
128 | .iter()
129 ~ .filter_map(|a| IpAddr::from_str(a).ok()))
130 | } else {
131 ~ Box::new(self
Interestingly enough if I copy paste one of the wings into a function, the compiler works fine without any error or need for a Box:
fn list_shared<'a>(&'a self, items: &'a HashSet<String>) -> impl Iterator<Item = IpAddr> + 'a {
items
.iter()
.filter_map(|a| IpAddr::from_str(a).ok())
}
pub fn shared<'a>(&'a self, ipv6: bool) -> impl Iterator<Item = IpAddr> + 'a {
if ipv6 {
self.list_shared(&self.ipv6s)
} else {
self.list_shared(&self.ipv4s)
}
}
As you can see this is a copy-paste of the inner block. Why is this happening? How are those 2 identical blocks not identical in the first instance but just putting them inside a function made them identical?

Each closure gets its own, anonymous type. Even though the closures have the same call signature, and even if neither of them borrows anything so no lifetimes are part of the signature, these types are not the same!
Therefore, the generic <F> in the returned FilterMap struct has a different type in each if branch, leading to the error message about trying to return incompatible types.
Note that -> impl Iterator tells the compiler that you're returning some type that implements Iterator, but it has to be statically the same type every time, determined at compile time.
When you extract the filter_map call to a separate function, there is only one closure, hence one type returned from that function. And since this is the same type for both if branches, the problem goes away.
It also goes away if you assign the closure to a variable, because then it's the same type in both cases as well:
pub fn shared2(&self, ipv6: bool) -> impl Iterator<Item = IpAddr> + '_ {
let from_str = |a: &String| IpAddr::from_str(a).ok();
if ipv6 {
self
.ipv6s
.iter()
.filter_map(from_str)
} else {
self
.ipv4s
.iter()
.filter_map(from_str)
}
}

impl Iterator<Item=IpAddr> is saying 'this function will return a static type that is determined at runtime that conforms to Iterator<Item=IpAddr>.
It is not the same as Box<dyn Iterator<Item=IpAddr> which means any type that conforms to Iterator<Item=IpAddr>.
The reason it dosent work is because each each one of |a| IpAddr::from_str(a).ok()) are diffrent types that are generated by the compiler, and filter_map rerurns a struct that has the iterator of the type FilterMap<I, F>, with F being the type of the function.
You can see the same issue if you move your closures to named functions
fn ipv6_iter(a: &String) -> Option<IpAddr> {
IpAddr::from_str(a).ok()
}
fn ipv4_iter(a: &String) -> Option<IpAddr> {
IpAddr::from_str(a).ok()
}
impl IpAddresses {
pub fn shared2(&self, ipv6: bool) -> impl Iterator<Item = IpAddr> + '_ {
if ipv6 {
self
.ipv6s
.iter()
.filter_map( ipv6_iter)
} else {
self
.ipv4s
.iter()
.filter_map( ipv4_iter)
}
}
}
In your first branch returns a FilterMap<IpAddresses, ipv6_iter>, but the else branch returns a FilterMap<IpAddresses, ipv4_iter>.
By moving the logic into list_shared, both filter_maps use the same anonymous function to do the mapping, therefore have the same FilterMap type.
Same as using the same static function in each filter_map
fn ip_iter(s: &String) -> Option<IpAddr> {
IpAddr::from_str(s).ok()
}
impl IpAddresses {
pub fn shared2(&self, ipv6: bool) -> impl Iterator<Item = IpAddr> + '_ {
if ipv6 {
self
.ipv6s
.iter()
.filter_map( ip_iter)
} else {
self
.ipv4s
.iter()
.filter_map( ip_iter)
}
}
}
So each branch returns a FilterMap<IpAddresses, ip_iter>, therefore impl Iterator<Item=IpAddr> has a single type to resolve to.

Related

How to pass reference of Into to another function?

I have a function that accepts a type Into<A>, how do you call into it, assuming I have a Vec of Into<A>?
Here is some sample code which fails to compile:
struct A {}
struct B {}
impl From<B> for A {
fn from(value: B) -> A {
A {}
}
}
impl From<&B> for A {
fn from(value: &B) -> A {
A {}
}
}
fn do_once<H: Into<A>>(item: H) {
println!("do_once");
}
fn do_many<J: Into<A>>(items: Vec<J>) {
let item = &items[0];
do_once(item);
// In the real code, we iterate here over all items.
}
Error:
error[E0277]: the trait bound `A: From<&J>` is not satisfied
--> src\main.rs:28:5
|
22 | fn do_once<H: Into<A>>(item: H) {
| ------- required by this bound in `do_once`
...
28 | do_once(item);
| ^^^^^^^ the trait `From<&J>` is not implemented for `A`
|
= note: required because of the requirements on the impl of `Into<A>` for `&J`
I think the problem is that I am passing an &Into<A>, not an Into<A>.
Is there any solution other than changing do_once to accept &H instead of H? In my real world case, I would like to avoid changing that API.
Yes, just move the trait bound so &J is Into<A> instead of J:
fn do_many<J>(items: Vec<J>)
where
for<'a> &'a J: Into<A>
{
let item = &items[0];
do_once(item);
// In the real code, we iterate here over all items.
}
Playground link

Problem with lifetimes and implementing Clone

I'm trying to define a struct representing a function that can be composed using different arithmetic operations (only addition has been implemented).
I would like to implement Clone for my struct, however I can't seem to it to work:
use std::ops::Add;
use std::boxed::Box;
use std::clone::Clone;
type InputT = i32;
type OutputT = f64;
pub struct ComposableFn<'a> {
f: Box<dyn 'a + Fn(InputT) -> OutputT>,
}
impl<'a> ComposableFn<'a> {
pub fn new<F: 'a + Fn(InputT) -> OutputT>(f: F) -> Self {
Self {
f: Box::new(f)
}
}
pub fn compute(&self, x: InputT) -> OutputT {
(self.f)(x)
}
}
impl<'a> Add<&'a ComposableFn<'a>> for &'a ComposableFn<'a> {
type Output = ComposableFn<'a>;
fn add(self, rhs: &'a ComposableFn) -> Self::Output {
ComposableFn::new(move |x| self.compute(x) + rhs.compute(x))
}
}
impl<'a> Clone for ComposableFn<'a> {
fn clone(&'a self) -> Self {
ComposableFn::new(move |x| self.compute(x))
}
}
fn main() {
let id = ComposableFn::new(|x| x.into());
println!("{}", id.compute(12));
let double = &id + &id;
println!("{}", double.compute(7));
let triple = &double + &id;
println!("{}", triple.compute(3));
}
When compiling I get the following error:
error[E0308]: method not compatible with trait
--> src/main.rs:33:5
|
33 | fn clone(&'a self) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected fn pointer `fn(&ComposableFn<'a>) -> ComposableFn<'_>`
found fn pointer `fn(&'a ComposableFn<'a>) -> ComposableFn<'_>`
note: the anonymous lifetime #1 defined on the method body at 33:5...
--> src/main.rs:33:5
|
33 | fn clone(&'a self) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 32:6
--> src/main.rs:32:6
|
32 | impl<'a> Clone for ComposableFn<'a> {
| ^^
error: aborting due to previous error
Removing the 'a from fn clone(&'a self) results in the following error instead:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:34:27
|
34 | ComposableFn::new(move |x| self.compute(x))
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 33:5...
--> src/main.rs:33:5
|
33 | fn clone(&self) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that the types are compatible
--> src/main.rs:34:27
|
34 | ComposableFn::new(move |x| self.compute(x))
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `&ComposableFn<'_>`
found `&ComposableFn<'a>`
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 32:6...
--> src/main.rs:32:6
|
32 | impl<'a> Clone for ComposableFn<'a> {
| ^^
note: ...so that the expression is assignable
--> src/main.rs:34:9
|
34 | ComposableFn::new(move |x| self.compute(x))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: expected `ComposableFn<'a>`
found `ComposableFn<'_>`
error: aborting due to previous error
Is there a way to fix this?
You cannot implement Clone this way. Clone requires the return type to match exactly, which means the exact same lifetime 'a. But you're trying to make a clone that references self which has a different lifetime.
The straightforward solution would be to just clone f. Unfortunately, you can't clone a Box<dyn...>, at least not without some help. See: How to clone a struct storing a boxed trait object?
The only direct solution would be to swap Box out with Rc so they share ownership:
use std::rc::Rc;
pub struct ComposableFn<'a> {
f: Rc<dyn 'a + Fn(InputT) -> OutputT>,
}
impl Clone for ComposableFn<'_> {
fn clone(&self) -> Self {
ComposableFn { f: self.f.clone() }
}
}

Using impl Trait in a recursive function

I've been experimenting with impl Trait and I came across this error when building a recursive function:
error[E0308]: if and else have incompatible types
--> src/main.rs:16:5
|
16 | / if logic {
17 | | one(false)
18 | | } else {
19 | | two()
20 | | }
| |_____^ expected opaque type, found a different opaque type
|
= note: expected type `impl Meow` (opaque type)
found type `impl Meow` (opaque type)
Here's the code to reproduce (Rust playground link):
trait Meow {
fn meow();
}
struct Cat(u64);
impl Meow for Cat {
fn meow() {}
}
fn one(gate: bool) -> impl Meow {
if gate {
one(false)
} else {
two()
}
}
fn two() -> impl Meow {
Cat(42)
}
fn main() {
let _ = one(true);
}
I haven't been able to find documentation about this particular issue and I find it odd that the compiler returns an error that roughly says "these two identical things are different".
Is there a way I can support the impl Trait syntax whilst doing this kind of recusion, please?
Disclaimer: this answer assumes that the reader understands that -> impl Trait requires a single type to be returned; see this question for returning different types.
Opacity
One of the core principles of Rust is that type-checking is entirely driven by the interface of functions, types, etc... and the implementation is ignored.
With regard to -> impl Trait functionality, this manifests by the language treating each -> impl Trait as an opaque type, solely identified by the function it comes from.
As a result, you can call the same function twice:
use std::fmt::Debug;
fn cat(name: &str) -> impl Debug { format!("Meow {}", name) }
fn meow(g: bool) -> impl Debug {
if g {
cat("Mario")
} else {
cat("Luigi")
}
}
fn main() {
println!("{:?}", meow(true));
}
But you cannot call different functions, even when they return the same type, if at least one is hidden behind -> impl Trait:
use std::fmt::Debug;
fn mario() -> impl Debug { "Meow Mario" }
fn luigi() -> &'static str { "Meow Luigi" }
fn meow(g: bool) -> impl Debug {
if g {
mario()
} else {
luigi()
}
}
fn main() {
println!("{:?}", meow(true));
}
Yields:
error[E0308]: if and else have incompatible types
--> src/main.rs:8:9
|
8 | / if g {
9 | | mario()
10 | | } else {
11 | | luigi()
12 | | }
| |_________^ expected opaque type, found &str
|
= note: expected type `impl std::fmt::Debug`
found type `&str`
And with two hidden behind -> impl Trait:
use std::fmt::Debug;
fn mario() -> impl Debug { "Meow Mario" }
fn luigi() -> impl Debug { "Meow Luigi" }
fn meow(g: bool) -> impl Debug {
if g {
mario()
} else {
luigi()
}
}
fn main() {
println!("{:?}", meow(true));
}
Yields the same error message than you got:
error[E0308]: if and else have incompatible types
--> src/main.rs:8:5
|
8 | / if g {
9 | | mario()
10 | | } else {
11 | | luigi()
12 | | }
| |_____^ expected opaque type, found a different opaque type
|
= note: expected type `impl std::fmt::Debug` (opaque type)
found type `impl std::fmt::Debug` (opaque type)
Interaction with Recursion
None.
The language does not special-case recursion here, and therefore does not realize that, in the case presented in the question, there is only ever one type involved. Instead, it notices fn one(...) -> impl Meow and fn two(...) -> impl Meow and concludes that those are different opaque types and therefore compile-time unification is impossible.
It may be reasonable to submit a RFC to tweak this aspect, either by arguing on the point of view of recursion, or by arguing on the point of view of module-level visibility; this is beyond the scope of this answer.
Work around
The only possibility is to ensure that the type is unique, and this requires naming it. Once you have captured the type in a name, you can consistently apply it everywhere it needs to match.
I'll refer you to #Anders' answer for his clever work-around.
I think an ideal compiler would accept your code, but the current language doesn’t allow for the recursive reasoning that would be needed to figure out that the types are actually the same in this case. You can work around this missing feature by abstracting over the impl Meow type with a type variable:
fn one_template<T: Meow>(gate: bool, two: impl FnOnce() -> T) -> T {
if gate {
one_template(false, two)
} else {
two()
}
}
fn one(gate: bool) -> impl Meow {
one_template(gate, two)
}
Rust playground link

What happens when a Rust struct contains a lifetimed trait?

Ok, so I'm a total Rust newbie, and I'm experimenting with Rocket. That web framework passes a Form<MyStruct>, and I want to transfer that MyStruct into my own custom struct.
struct Consumer<T> {
d: T,
}
impl<T> Consumer<T> {
fn new(form: Form<T>) -> Self {
Consumer { d: form.into_inner() }
}
}
That doesn't work of course, I get:
the trait `rocket::request::FromForm<'_>` is not implemented for `T`
Next attempt:
impl<T> Consumer<T> where T: FromForm {
fn new(form: Form<T>) -> Self {
Consumer { d: form.into_inner }
}
}
Uh oh:
impl<T> Consumer<T> where T: FromForm {
^^^^^^^^ expected lifetime parameter
So now I find myself completely unable to fix this! The best I can come up with is:
impl<'f, T> Consumer<T> where T: FromForm<'f> {
fn new(form: Form<T>) -> Self {
Consumer { d: form.into_inner }
}
}
But that results in this error:
51 | fn new(form: Form<T>) -> Self {
| _________^
52 | | Consumer { d: form.into_inner }
53 | | }
| |_________^ lifetime mismatch
= note: expected type `rocket::request::FromForm<'_>`
found type `rocket::request::FromForm<'f>`
Verifiable example: https://hastebin.com/eqihaqodux.makefile
Form also has a lifetime parameter. If you tie it to the lifetime of FromForm, then you'll move forward a little:
impl<'f, T> Consumer<T> where T: FromForm<'f> {
fn new(form: Form<'f, T>) -> Self {
Consumer(form.into_inner())
}
fn get(&self) -> &T {
&self.0
}
}
As a general rule, if you return an object that depends on data in another object, then you'll need to link their lifetimes together like this.
At this point, you'll see another error, which conveniently gives you all the information you need to fix it:
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:50:17
|
48 | impl<'f, T> Consumer<T> where T: FromForm<'f> {
| - help: consider adding an explicit lifetime bound `T: 'static`...
49 | fn new(form: Form<'f, T>) -> Self {
50 | Consumer(form.into_inner())
| ^^^^^^^^^^
|
note: ...so that the type `T` will meet its required lifetime bounds
--> src/main.rs:50:17
|
50 | Consumer(form.into_inner())
| ^^^^^^^^^^
The into_inner method on Form requires that it's type parameter T has the 'static lifetime, and the error message suggests adding this constraint.
With these changes, it will compile:
impl<'f, T: 'static> Consumer<T> where T: FromForm<'f> {
fn new(form: Form<'f, T>) -> Self {
Consumer(form.into_inner())
}
fn get(&self) -> &T {
&self.0
}
}

Is there a way to tell the compiler that nobody will implement a trait for a reference to a generic type?

Here is toy code that demonstrates the problem:
trait Foo {}
trait Boo<T> {
fn f() -> T;
}
impl<T> Boo<T> for i32
where
T: Foo,
{
fn f() -> T {
unimplemented!();
}
}
impl<'a, T> Boo<&'a T> for i32
where
T: Foo,
{
fn f() -> T {
unimplemented!();
}
}
I want to have two generic implementations of trait Boo, but it doesn't compile:
error[E0119]: conflicting implementations of trait `Boo<&_>` for type `i32`:
--> src/main.rs:16:1
|
7 | / impl<T> Boo<T> for i32
8 | | where
9 | | T: Foo,
10 | | {
... |
13 | | }
14 | | }
| |_- first implementation here
15 |
16 | / impl<'a, T> Boo<&'a T> for i32
17 | | where
18 | | T: Foo,
19 | | {
... |
22 | | }
23 | | }
| |_^ conflicting implementation for `i32`
|
= note: downstream crates may implement trait `Foo` for type `&_`
I do not plan to make this part of functionality to other crates. I tried:
moving this code to binary crate that obviously can not be used from other crates
moving this to a private mod
marking the trait as pub(crate)
all with no success.
Is there anyway to give the compiler a hint that it should not care that anybody will implement Foo for any reference?
Maybe my toy example is not the best, so here is the real code.
It's used for integration with the C part of my program, so it's a little
complicated.
impl<T: MyTrait> MyFrom<Option<T>> for *mut c_void {
fn my_from(x: Option<T>) -> Self {
match x {
Some(x) => <T>::alloc_heap_for(x),
None => ptr::null_mut(),
}
}
}
impl<'a, T: MyTrait> MyFrom<Option<&'a T>> for *mut c_void {
fn my_from(x: Option<&'a T>) -> Self {
match x {
Some(x) => x as *const T as *mut c_void,
None => ptr::null_mut(),
}
}
}
The conflict here doesn't have anything to do with the reference-ness of the latter implementation. The issue is that, in the first implementation, T can be any type, including reference types. Suppose you make the following function call:
let x: i32 = 10;
let result: &u8 = x.f();
At this point, the type resolver needs to figure out what function is being called. It finds a conflicting implementation:
impl Boo<&u8> for i32 via Boo<T> (T == &u8),
impl Boo<&u8> for i32 via Boo<&T> (T == u8),
You'd have exactly the same issue if you used a concrete type in the latter implementation:
// This will fail to compile
impl<T> Boo<T> for i32 { ... }
impl Boo<String> for i32 { ... }
This conflict means that the compiler can't allow these two implementations to coexist.
The specific thing you're looking to do here is called "specialization"; it refers to a proposal for a set of rules that says that overlapping implementations like this are allowed to exist if one of them is unambiguously more "specific" than the other, in which case the compiler will pick the more specific implementation. This is tracked as RFC #1210.

Resources