This small FizzBuzz program using unboxed closures gives a rather mysterious error message.
fn fizzbuzz<F: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: F, bs: &str) {
for i in range(1i64, n+1) {
match (f(i), b(i)) {
(true, true) => println!("{:3}: {}{}", i, fs, bs),
(true, _) => println!("{:3}: {}", i, fs),
(_, true) => println!("{:3}: {}", i, bs),
_ => (),
}
}
}
fn main() {
fizzbuzz(30,
|&: i: i64| { i % 3 == 0 }, "fizz",
|&: j: i64| { j % 5 == 0 }, "buzz");
}
Error message:
<anon>:15:14: 15:40 error: mismatched types: expected `closure[<anon>:14:14: 14:40]`, found `closure[<anon>:15:14: 15:40]` (expected closure, found a different closure)
<anon>:15 |&: j: i64| { j % 5 == 0 }, "buzz");
^~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could someone describe the error please? Thanks.
This piece of code demonstrates the essence of the problem:
fn show_both<S: Show>(x: S, y: S) {
println!("{} {}", x, y);
}
You can only call it with both arguments of the same type, i.e. this is allowed:
let x: i32 = 10;
let y: i32 = 20;
show_both(x, y);
But this is not:
let x: i32 = 10;
let y: f64 = 20.0;
show_both(x, y);
This is fairly natural: you specified that both of the parameters must be of the same type, even though this type can be arbitrary as long as it implements Show.
You have essentially the same thing in your code:
fn fizzbuzz<F: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: F, bs: &str)
You declared that both f and b must have the same type. However, for each unboxed closure the compiler generates a different type - this is quite natural as well, given that different closures can capture different variables.
You need to specify different type parameters in order to be able to pass different closures:
fn fizzbuzz<F1, F2>(n: i64, f: F1, fs: &str, b: F2, bs: &str)
where F1: Fn(i64) -> bool,
F2: Fn(i64) -> bool
Each unboxed closure definition creates a completely different type. This makes each of the closures defined in main a different type. Your fizzbuz function, on the other hand, requires each of the closures passed to it to be the same type, F. If you change fizzbuzz's signature to:
fn fizzbuzz<F: Fn(i64) -> bool, G: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: G, bs: &str)
your code will typecheck.
Basically, the <F: Fn(i64) -> bool> syntax doesn't create a wildcard for types that implement the trait parameter (Fn(i64) -> bool), but declares a single type which must satisfy the trait parameter and be the same type everywhere it is used. Unboxed closure definitions must be different types both because the may wrap different environments but also because they dispatch to different functions (that is, each has a different body). As a result, fizzbuzz needs two different type parameters to accommodate the two closure types.
Related
I'm in the following situation:
fn some_fn<K, T, F, S>(func: F, other_func: S) -> Vec<i64>
where
K: SomeType<T>,
T: SomeOtherType,
F: Fn() -> (),
S: Fn() -> (),
{
//...
}
For the above example, Rust can correctly infer types T, F, and S, but not K (as is expected).
Is there a way to only specify the type of K when calling some_fn without also specifying T, F, and S?
My current workaround is to change the signature to some_fn to fn some_fn<K, T, F, S>(cheat: Option<K>, func: F, other_func: S) and call the function like so:
let cheat: Option<SomethingThatImplements> = None;
let result = some_fn(cheat, func, other_func);
However, I find this to be very clunky. I haven't been able to find anything regarding this topic, is it even possible to specify only part of the type args?
Yes. Use _ for the arguments you want to infer:
some_fn::<Foo, _, _, _>(...);
According the Rustonomicon, &mut T is invariant over T. How can the following code compile when &mut (str, str) is not a subtype of &mut (T, T)?
fn swap<T: Copy>(pair: &mut (T, T)) {
let temp = pair.0;
pair.0 = pair.1;
pair.1 = temp;
}
fn main() {
let mut pair = ("left", "right");
swap(&mut pair);
println!("{:?}", pair);
}
The relevant chapter is here
The Rustonomicon implies that you can call f: (T) -> U on f(V) only if V is a subtype of T. (4th code block, the example is evil_feeder(pet: &mut Animal) being called with &mut Cat). How can the above example compile if it's not a subtype?
Variance is primarily about type coercion and only secondarily about generics.
See also https://ehsanmkermani.com/2019/03/16/variance-in-rust-an-intuitive-explanation/, https://doc.rust-lang.org/reference/subtyping.html, and https://doc.rust-lang.org/reference/type-coercions.html#coercion-types.
It can compile because you are not using a &mut (str, str). What you have is an &mut (&'static str, &'static str). This is because string literals are references to the read-only memory where the actual string was stored in the executable.
It can fulfill the requirement of the function because even though str does not implement Copy, all immutable references implement Copy.
I have been learning Rust, coming from a Swift, C and C++ background. I feel like I have a basic understanding of ownership, borrowing and traits. To exercise a bit, I decided to implement a sum function on a generic slice [T] where T has a default value and can be added to itself.
This is how far I got:
trait Summable {
type Result;
fn sum(&self) -> Self::Result;
}
impl<T> Summable for [T]
where
T: Add<Output = T> + Default,
{
type Result = T;
fn sum(&self) -> T {
let x = T::default();
self.iter().fold(x, |a, b| a + b)
}
}
Compiler complains with expected type parameter T, found &T for a + b.
I understand why the error happens, but not exactly how to fix it. Yes, the type of x is T. It cannot be &T because, if nothing else, if the slice is empty, that's the value that is returned and I cannot return a reference to something created inside the function. Plus, the default function returns a new value that the code inside the function owns. Makes sense. And yes, b should be a shared reference to the values in the slice since I don't want to consume them (not T) and I don't want to mutate them (not &mut T).
But that means I need to add T to &T, and return a T because I am returning a new value (the sum) which will be owned by the caller. How?
PS: Yes, I know this function exists already. This is a learning exercise.
The std::ops::Add trait has an optional Rhs type parameter that defaults to Self:
pub trait Add<Rhs = Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
Because you've omitted the Rhs type parameter from the T: Add<Output = T> bound, it defaults to T: hence to your a you can add a T, but not an &T.
Either specify that T: for<'a> Add<&'a T, Output = T>; or else somehow obtain an owned T from b, e.g. via T: Copy or T: Clone.
When learning rust closures,I try Like Java return "A Function"
fn equal_5<T>() -> T
where T: Fn(u32) -> bool {
let x:u32 = 5;
|z| z == x
}
But when i use it
let compare = equal_5();
println!("eq {}", compare(6));
Build error
11 | let compare = equal_5();
| ------- consider giving `compare` a type
12 | println!("eq {}", compare(6));
| ^^^^^^^^^^ cannot infer type
|
= note: type must be known at this point
See: https://doc.rust-lang.org/stable/rust-by-example/trait/impl_trait.html
Currently T simply describes a type, which in this case implements the Fn trait. In other words, T isn't a concrete type. In fact with closures, it's impossible to declare a concrete type because each closure has it's own unique type (even if two closures are exactly the same they have different types.)
To get around directly declaring the type of the closure (which is impossible) we can use the impl keyword. What the impl keyword does is convert our description of a type (trait bounds) into an invisible concrete type which fits those bounds.
So this works:
fn equal_5() -> impl Fn(u32) -> bool {
let x:u32 = 5;
move |z| z == x
}
let compare = equal_5();
println!("eq {}", compare(6));
One thing to note is we can also do this dynamically. Using boxes and the dyn trait. So this also works, however incurs the associated costs with dynamic resolution.
fn equal_5() -> Box<dyn Fn(u32) -> bool> {
let x:u32 = 5;
Box::new(move |z| z == x)
}
let compare = equal_5();
println!("eq {}", compare(6));
The compiler seems to be complaining that it's expecting a type parameter but finds a closure instead. It knows the type, and doesn't need a type parameter, but also the size of the closure object isn't fixed, so you can either use impl or a Box. The closure will also need to use move in order to move the data stored in x into the closure itself, or else it wont be accessible after equal_5() returns, and you'll get a compiler error that x doesn't live long enough.
fn equal_5() -> impl Fn(u32) -> bool {
let x:u32 = 5;
move |z| z == x
}
or
fn equal_5() -> Box<Fn(u32) -> bool> {
let x:u32 = 5;
Box::new(move |z| z == x)
}
This is what the std says:
pub trait PartialEq<Rhs: ?Sized = Self> {
/// This method tests for `self` and `other` values to be equal, and is used
/// by `==`.
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn eq(&self, other: &Rhs) -> bool;
/// This method tests for `!=`.
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
fn ne(&self, other: &Rhs) -> bool {
!self.eq(other)
}
}
And the link: https://doc.rust-lang.org/src/core/cmp.rs.html#207
This is my code:
fn main() {
let a = 1;
let b = &a;
println!("{}", a==b);
}
and the compiler told me:
error[E0277]: can't compare `{integer}` with `&{integer}`
--> src\main.rs:4:21
|
4 | println!("{}", a==b);
| ^^ no implementation for `{integer} == &{integer}`
|
= help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}`
But when I used eq(), it compiled:
fn main() {
let a = 1;
let b = &a;
println!("{}", a.eq(b));
}
It's actually quite simple, but it requires a bit of knowledge. The expression a == b is syntactic sugar for PartialEq::eq(&a, &b) (otherwise, we'd be moving a and b by trying to test if they're equal if we're dealing with non-Copy types).
In our case, the function PartialEq::eq needs to take two arguments, both of which are of type &i32. We see that a : i32 and b : &i32. Thus, &b will have type &&i32, not &i32.
It makes sense that we'd get a type error by trying to compare two things with different types. a has type i32 and b has type &i32, so it makes sense that no matter how the compiler secretly implements a == b, we might get a type error for trying to do it.
On the other hand, in the case where a : i32, the expression a.eq(b) is syntactic sugar for PartialEq::eq(&a, b). There's a subtle difference here - there's no &b. In this case, both &a and b have type &i32, so this is totally fine.
The difference between a.eq(b) and a == b in that dot operator does autoref/autoderef on receiver type for call-by-reference methods.
So when you write a.eq(b) compiler looks at PartialEq::eq(&self, other: &Rhs) signature, sees &self reference and adds it to a.
When you write a == b it desugars to PartialEq::eq(a, b) where a: i32 b: &i32 in your case, hence the error no implementation for `{integer} == &{integer}` .
But why it does not do the same in operators? See
Tracking issue: Allow autoderef and autoref in operators (experiment) #44762
Related information:
What are Rust's exact auto-dereferencing rules?