Abstract function receiving and returning Rc - rust

I'm being unable to compile the following:
pub trait Symbol : ToString {
fn callee(self: &Rc<Self>) -> Option<Rc<dyn Symbol>> {
None
}
}
Taking Rc parameter causes the error...
E0038) the trait symbols::Symbol cannot be made into an object
everywhere Symbol is used, including in that function declaration.
It does work if I take &self instead of self: &Rc<Self>, however I've some functions that really need to take this Rc. Any idea of what to do?

Notice this error message:
error[E0307]: invalid `self` parameter type: Rc<(dyn Symbol + 'static)>
--> src/lib.rs:3:21
|
3 | fn callee(self: Rc<dyn Symbol>) -> Option<Rc<dyn Symbol>> {
| ^^^^^^^^^^^^^^
|
= note: type of `self` must be `Self` or a type that dereferences to it
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
It should be a Rc<Self>, it makes not much sense of using &Rc since Rc is cheap to clone.
use std::rc::Rc;
pub trait Symbol : ToString {
fn callee(self: Rc<Self>) -> Option<Rc<dyn Symbol>> {
None
}
}
Playground

Related

Can you specify the lifetime of a moved self parameter?

I have a newtype struct:
pub struct Branch<'repo>(Git2Branch<'repo>);
And I am trying to wrap a method call:
impl <'repo> Branch<'_> {
pub fn into_reference(self) -> Git2Reference<'repo> {
self.0.into_reference()
}
}
(Git2Branch and Git2Reference here are aliases for types of the same name from the git2 crate.)
This fails to compile with
error: lifetime may not live long enough
--> git_wrapper/src/branch.rs:38:9
|
6 | impl <'repo> Branch<'_> {
| ----- lifetime `'repo` defined here
...
37 | pub fn into_reference(self) -> Git2Reference<'repo> {
| ---- has type `branch::Branch<'1>`
38 | self.0.into_reference()
| ^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'repo` but it is returning data with lifetime `'1`
Lifetimes '1 and 'repo should be the same, but I can't figure out how (or if it's even possible) to specify the lifetime of self in this case.
impl <'repo> Branch<'_> {
Is the same as:
impl <'repo, 'anonymous> Branch<'anonymous> {
So the lifetimes 'repo and the anonymous lifetime of the field are unrelated - which causes an issue when you try to return the Git2Reference<'anonymous>.
You only need to talk about one lifetime here, so say that:
impl <'repo> Branch<'repo> {

Rust: Why isn't "self" a reference when "self: &Self"?

According to Rust Lang Programming Book, we know that:
The &self is actually short for self: &Self
However, from the example below (fn get_name), we wrote self: &Self, but self is not a reference but the object itself according to the error message. Why isn't self a reference in this case? What is the ampersand for in &Self then?
Example: a simple struct with method that returns a person's name
struct Person {
name: String
}
impl Person {
fn new(name: &str) -> Person {
Person {
name: name.to_string()
}
}
fn get_name(self: &Self) -> &String {
self.name // Error: expected `&std::string::String`, found struct `std::string::String`
}
}
Because of automatic dereferencing.
C has the . and -> operators, where . is applied to values and -> to pointers. In C, x->y is exactly equivalent to (*x).y.
Rust has . but not ->, so to make the syntax friendly, . will usually automatically dereference when the compiler cannot find the member requested. In this case, &Person doesn't have a name attribute (since references can't have attributes), so the compiler tries again with a dereference ((*self).name) and it finds the attribute this time, on Person.
Since *self has type Person, (*self).name has type String. To fix the problem, you take a reference to the string by doing &self.name, which means the same thing as &((*self).name) (extra parens added for clarity).
To directly address your question:
... self is not a reference but the object itself according to the error message. Why isn't self a reference in this case?
The error message only refers to the String attribute and doesn't really give you any information about self at all. self is indeed a reference. If you throw this in your impl Person:
fn test(&self) { self }
Then the compiler will tell you what self is (because it doesn't match the implied () return type):
error[E0308]: mismatched types
--> src/lib.rs:12:18
|
12 | fn test(&self) { self }
| - ^^^^ expected `()`, found `&Person`
| |
| expected `()` because of default return type
Note that you will see the same compiler error if you say fn get_name(&self). This error isn't caused by the self: &Self syntax; it is indeed equivalent to &self.

Expected bound lifetime parameter, found concrete lifetime when trying to pass an Option<FnOnce>

In the code below, I'm trying to pass an Option<FnOnce(&mut Thing)> to the higher order function invoke_me_maybe(). The function being passed will be invoked if it is present, and not invoked otherwise.
The Option<FnOnce(&mut Thing)> are constructed using as_some() from additional trait methods on booleans, copied the boolinator crate.
struct Thing{}
fn invoke_me_maybe<F: FnOnce(&mut Thing)>(t: &mut Thing, opt_f: Option<F>) {
if let Some(f) = opt_f {
f(t);
}
}
trait BoolOption {
fn as_some<T>(self, some: T) -> Option<T>;
}
impl BoolOption for bool {
fn as_some<T>(self, some: T) -> Option<T> {
if self { Some(some) } else { None }
}
}
pub fn main() {
let mut thing = Thing{};
invoke_me_maybe(&mut thing, true.as_some(|t| {}));
}
The invoke_me_maybe() function does not keep opt_f beyond the end of the function, so we should not need to wrap the function in a Box or anything like that.
The error produced is as follows:
error[E0631]: type mismatch in closure arguments
--> src/main.rs:21:33
|
3 | fn invoke_me_maybe<F: FnOnce(&mut Thing)>(t: &mut Thing, opt_f: Option<F>) {
| --------------- ------------------ required by this bound in `invoke_me_maybe`
...
21 | invoke_me_maybe(&mut thing, true.as_some(|t| {}));
| ^^^^^^^^^^^^^---^^^^
| | |
| | found signature of `fn(_) -> _`
| expected signature of `for<'r> fn(&'r mut Thing) -> _`
error[E0271]: type mismatch resolving `for<'r> <[closure#src/main.rs:21:46: 21:52] as std::ops::FnOnce<(&'r mut Thing,)>>::Output == ()`
--> src/main.rs:21:5
|
3 | fn invoke_me_maybe<F: FnOnce(&mut Thing)>(t: &mut Thing, opt_f: Option<F>) {
| --------------- ------------------ required by this bound in `invoke_me_maybe`
...
21 | invoke_me_maybe(&mut thing, true.as_some(|t| {}));
| ^^^^^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0631.
For more information about an error, try `rustc --explain E0271`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
I'm probably missing some explicit lifetime parameters or something like that, but I can't figure it out. Doesn't fn(_) -> _ already match with for<'r> fn(&'r mut Thing) -> _?
Using closures as higher order functions is tricky. I've noticed that explicitly writing the type of the arguments usually helps. In your case it does the trick:
invoke_me_maybe(&mut thing, true.as_some(|t: &mut Thing| {}));
The problem seems to be that invoke_me_maybe takes a generic argument with a lot of possibilities, while |t| {} may mean anything, and the compiler is not able to match both. Adding the type annotation helps in this case.
Personally I consider this a compiler bug, but I've been wrong before...

Why does a lazy-static value claim to not implement a trait that it clearly implements?

With the following code (an attempt to make an HTTP request using the reqwest crate), the compiler says that my value SID_URI does not implement the trait PolyfillTryInto. What's going on here? reqwest::Url clearly implements the private trait reqwest::into_url::PolyfillTryInto.
#[macro_use]
extern crate lazy_static;
extern crate reqwest;
static R_EMAIL: &str = "example#example.com";
static R_PASS: &str = "password";
static API_PUBKEY: &str = "99754106633f94d350db34d548d6091a";
static API_URI: &str = "https://example.com";
static AUTH_PATH: &str = "/api/v1";
lazy_static! {
static ref SID_URI: reqwest::Url = reqwest::Url::parse(&(API_URI.to_owned() + AUTH_PATH)).unwrap();
}
fn get_sid() -> Result<reqwest::Response, reqwest::Error> {
let client = reqwest::Client::new();
let params = [("ID", R_EMAIL), ("PW", R_PASS), ("KY", API_PUBKEY)];
let q = client.post(SID_URI).form(&params).send()?;
Ok(q)
}
fn main() {
assert!(get_sid().is_ok());
}
error[E0277]: the trait bound `SID_URI: reqwest::into_url::PolyfillTryInto` is not satisfied
--> src/main.rs:19:20
|
19 | let q = client.post(SID_URI).form(&params).send()?;
| ^^^^ the trait `reqwest::into_url::PolyfillTryInto` is not implemented for `SID_URI`
|
= note: required because of the requirements on the impl of `reqwest::IntoUrl` for `SID_URI`
The compiler isn't lying to you, you are just skipping over a relevant detail of the error message. Here's a self-contained example:
#[macro_use]
extern crate lazy_static;
struct Example;
trait ExampleTrait {}
impl ExampleTrait for Example {}
lazy_static! {
static ref EXAMPLE: Example = Example;
}
fn must_have_trait<T>(_: T)
where
T: ExampleTrait,
{
}
fn main() {
must_have_trait(EXAMPLE);
must_have_trait(42i32);
}
error[E0277]: the trait bound `EXAMPLE: ExampleTrait` is not satisfied
--> src/main.rs:19:5
|
19 | must_have_trait(EXAMPLE);
| ^^^^^^^^^^^^^^^ the trait `ExampleTrait` is not implemented for `EXAMPLE`
|
= note: required by `must_have_trait`
error[E0277]: the trait bound `i32: ExampleTrait` is not satisfied
--> src/main.rs:20:9
|
20 | must_have_trait(42i32);
| ^^^^^^^^^^^^^^^ the trait `ExampleTrait` is not implemented for `i32`
|
= note: required by `must_have_trait`
Compare the two error messages:
the trait bound `EXAMPLE: ExampleTrait` is not satisfied
the trait bound `i32: ExampleTrait` is not satisfied
The second error message doesn't say that 42 does not implement ExampleTrait, it says that i32 lacks the implementation. This error message shows the type that fails, not the name of the value! That means that EXAMPLE in the same context is referring to a type.
Lazy-static works by creating one-off types that wrap your value and provide thread-safe single initialization guarantees:
For a given static ref NAME: TYPE = EXPR;, the macro generates a unique type that implements Deref<TYPE> and stores it in a static with name NAME.
This wrapper type does not implement your trait, only the wrapped type does. You will need to invoke Deref and then probably re-reference it to get to a &Url, assuming that a reference to a Url implements your trait:
must_have_trait(&*EXAMPLE);
Additionally, using the bare static variable would attempt to move it out of the static location (which would be a Very Bad Thing), so you always need to use it by reference.

Closure in the return type for a Rust function [duplicate]

This question already has answers here:
Returning a closure from a function
(4 answers)
Closed 5 years ago.
I wrote the following Rust program to print out only command-line arguments that are integers. It works perfectly:
use std::env;
fn main() {
for i in env::args().filter_map(|arg| arg.parse::<i32>().ok()) {
println!("{}", i);
}
}
I then attempted to re-write the program to abstract the filter into a function. This version does not compile.
use std::env::Args;
use std::env;
use std::iter::FilterMap;
// Version 2
fn main() {
for i in nums(&env::args()) {
println!("{}", i);
}
}
fn nums<F: Fn(String) -> Option<i32>>(args: &Args) -> FilterMap<Args,F> {
args.filter_map(|arg| arg.parse::<i32>().ok())
}
It produces the following compilation errors:
Compiling iterator_return_type v0.1.0 (file:///Users/gabriel/AllProjects/SentimentAnalysis/iterator_return_type)
error[E0282]: type annotations needed
--> src/main.rs:16:9
|
16 | for i in nums(&env::args()) {
| ^ cannot infer type for `_`
error: the type of this value must be known in this context
--> src/main.rs:22:27
|
22 | args.filter_map(|arg| arg.parse::<i32>().ok())
| ^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> src/main.rs:22:21
|
22 | args.filter_map(|arg| arg.parse::<i32>().ok())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found closure
|
= note: expected type `F`
found type `[closure#src/main.rs:22:21: 22:50]`
error: aborting due to previous error(s)
error: Could not compile `iterator_return_type`.
What I find particularly confusing is the final compilation error. I do not understand how else I might specify a closure type.
Thanks!
impl Trait and Box<Trait> solution can be applied to both iteratorsand closures, both of them are just traits! The difference is you have to use them in the closure case.
If you want to use impl Trait, then your code will look like this (note that Args should be passed by value):
#![feature(conservative_impl_trait)]
use std::env::Args;
use std::env;
use std::iter::FilterMap;
fn main() {
for i in nums(env::args()) {
println!("{}", i);
}
}
fn nums(args: Args) -> FilterMap<Args, impl FnMut(String) -> Option<i32>> {
args.filter_map(|arg| arg.parse::<i32>().ok())
}
However, you usually need not expose the detail of the iterator type; therefore you can do it like this way:
fn nums(args: Args) -> impl Iterator<Item = i32> {
args.filter_map(|arg| arg.parse::<i32>().ok())
}
What if you want to use stable Rust? Unfortunately you'll have to use boxing for now.
fn nums(args: Args) -> Box<Iterator<Item = i32>> {
Box::new(args.filter_map(|arg| arg.parse::<i32>().ok()))
}
Why can't you describe a full type of closures, despite that you can describe an iterator like Zip<Drain<'a, i32>, IntoIter<&'b str>>? There are two reasons:
Closure types are anonymous by nature; you'll have to anonymize (impl Fn()) or box (Box<Fn()>) them if you want to return them.
The interface for closure traits is unstable; you can't implement them (impl Fn() for YourType { .. }) stably.
Then why doesn't your code work? The reason is:
If you want to pass closures to a function, the caller decides its type. In this case you can write fn foo<T: Fn()>() { .. }.
If you want to pass closures from a function, the callee decides its type. In this case you'll have to use impl Trait.
RFC 1951 will change this distinction. You will be able to use impl Trait in both cases.

Resources