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

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.

Related

Abstract function receiving and returning Rc

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

Impl trait with generic associated type in return position causes lifetime error

I need to store a fn(I) -> O (where I & O can be references) in a 'static struct. O needs to be a trait with an 'static generic associated type, that associated type is also stored in the struct. Neither I nor O itself get stored inside of the struct, so their lifetime shouldn't matter. But the compiler is still complaining about I not living long enough.
trait IntoState {
type State: 'static;
fn into_state(self) -> Self::State;
}
impl IntoState for &str {
type State = String;
fn into_state(self) -> Self::State {
self.to_string()
}
}
struct Container<F, S> {
func: F,
state: S,
}
impl<I, O> Container<fn(I) -> O, O::State>
where
O: IntoState,
{
fn new(input: I, func: fn(I) -> O) -> Self {
// I & O lives only in the next line of code. O gets converted into
// a `'static` (`String`), that is stored in `Container`.
let state = func(input).into_state();
Container { func, state }
}
}
fn map(i: &str) -> impl '_ + IntoState {
i
}
fn main() {
let _ = {
// create a temporary value
let s = "foo".to_string();
// the temporary actually only needs to live in `new`. It is
// never stored in `Container`.
Container::new(s.as_str(), map)
// ERR: ^ borrowed value does not live long enough
};
// ERR: `s` dropped here while still borrowed
}
playground
As far as I can tell, the compiler's error message is misleading, what it actually requires is an explicitly defined associated type:
fn map(i: &str) -> impl '_ + IntoState<State = String> {
i
}
The excellent answer given to the quesion: Why does the compiler not infer the concrete type of an associated type of an impl trait return value? provides enough information on why this is actually needed.
See also Rust issue #42940 - impl-trait return type is bounded by all input type parameters, even when unnecessary
You can use a generic type parameter instead of returning an impl in which case you don't have to specify the associated type:
fn map<T: IntoState>(i: T) -> T {
i
}
Apparently there is still some confusion about what exactly is going on here, so I'll try to destil my comments into a short answer.
The problem here is the prototype of the function map():
fn map(i: &str) -> impl '_ + IntoState
This specifies that the return type of map() is some type implementing IntoState, with an unspecified associated type State. The return type has a lifetime parameter with the lifetime of the argument i; let's call that lifetime 'a, and the full return type T<'a>. The associated type State of this return type now is <T<'a> as IntoState>::State, which is parametrized by 'a. The compiler is currently not able to eliminate this lifetime parameter from the assoicated type, in spite of the 'static declaration in the trait definition. By explicitly specifying the associated type as String, the compiler will simply use the explicitly specified type String instead of <T<'a> as IntoState>::State, so the lifetime parameter is gone, and we don't get an error anymore.
This compiler shortcoming is discussed in this Github issue.

How to match against a &'static str in Rust

I am a Rust beginner and I can't solve this type problem. I have tried replacing &name with name, but the error "pattern &_ not covered" occurred.
fn get_project(name: &'static str) {
match &name {
"hi" => {},
}
}
fn main() {
let project = get_project("hi");
}
Compiler error:
error[E0308]: mismatched types
--> <anon>:3:9
|
3 | "hi" => {},
| ^^^^ expected &str, found str
|
= note: expected type `&&str`
= note: found type `&'static str`
String literals – like "hi" – have the type &'static str. So if you already have a &str, you don't need to add the &:
fn get_project(name: &str) {
match name {
"hi" => {},
_ => {}, // matches have to be exhaustive
}
}
I also added a default case, because matches in Rust need to be exhaustive: they need to cover all possible cases.
Maybe you noticed, that I also removed the 'static from the argument list. If you want to read about some lifetime stuff, go ahead. Else, stop reading here, because it's possibly confusing and not that important in this case.
In this function there is no need to restrict the lifetime of the given argument to 'static. Maybe you also want to pass in string slices that are borrowed from a String:
let user_input = read_user_input(); // type `String`
get_project(&input);
The code above only works when you remove the 'static from the argument. Once removed, the function is equivalent to:
fn get_project<'a>(name: &'a str) { ... }
This means that the function is generic over a lifetime 'a. The function says: given any lifetime 'a, you can give me a string with said lifetime and I am able to do my thing. Which is true. If the function wouldn't be able to do it for any lifetime, the compiler would complain ;-)
In your example, name doesn't need to have a static lifetime. Because you only use name inside your function, name doesn't need to have an extended lifetime. Check out the strings chapter of The Rust Programming Language. To match a &str with a &'static str you don't need &, just the variable itself is enough.
pub fn get_project(name: &str) {
match name {
"hi" => println!("I found hi!"),
_ => println!("Nothing match"),
}
}
fn main() {
get_project("hi");
get_project("42");
}

Higher Ranked Trait Bound and boxed closures lifetime issue

I am trying to write a function returning a boxed closure that can work on references to types with any
lifetime. When writing a specific instance, everything works fine. But when writing a generic
version, I run into lifetime problems.
struct Parameter<'a> {
s: &'a str,
}
fn main() {
let closure = generate_closure_gen();
let string = String::from("Hello World!");
let parameter = Parameter { s: &string }; // Error: string does not live long enough
closure(&parameter);
}
// This one works fine
// Desugared version for Box<Fn(&Parameter)>
fn generate_closure() -> Box<for <'a, 'r> Fn(&'r Parameter<'a>)> {
Box::new(|c: &Parameter| {})
}
// This one gives lifetime errors
fn generate_closure_gen<C>() -> Box<Fn(&C)> {
Box::new(|c: &C| {})
}
I don't see why the closure needs the type parameter to live longer than it (there is no storage or anything ...). And it works for the non-generic version with HRTB, it just feels like it should be possible to make it work with the generic version.
Also, if I try to write the specific version using the generic version, I get a type error
// Desugared version for Box<Fn(&Parameter)>
fn generate_closure_2() -> Box<for <'a, 'r> Fn(&'r Parameter<'a>)> {
generate_closure_gen()
}
src/main.rs:22:5: 22:27 error: mismatched types:
expected `Box<for<'r, 'r> core::ops::Fn(&'r Parameter<'r>) + 'static>`,
found `Box<for<'r> core::ops::Fn(&'r _) + 'static>`
(expected concrete lifetime,
found bound lifetime parameter ) [E0308]
src/main.rs:22 generate_closure_gen()
^~~~~~~~~~~~~~~~~~~~~~
src/main.rs:22:5: 22:27 help: run `rustc --explain E0308` to see a detailed explanation
Any idea on how to make this work?
(playpen link)
Type parameters have a lifetime bound. That lifetime bound is the shortest of all of the implementor's lifetime parameters. You omitted it on generate_closure_gen, so the compiler inferred it, but if we explicitly wrote it out, the function definition would look like this:
fn generate_closure_gen<'a, C: 'a>() -> Box<Fn(&C)> {
Box::new(|c: &C| {})
}
Making this change doesn't solve our problem, though.
To understand why, we need to figure out what C is inferred to be. You call the closure with a &'y Parameter<'x>, and the closure accepts for<'b> &'b C, so C is Parameter<'x>. Parameter<'x> has a lifetime parameter, which will have an influence on the lifetime bound on C.
Lifetime parameters in generic functions must be substituted with lifetimes that start before the function call. In this case, this means that the lifetime of any C we pass to the closure must be valid before the call to generate_closure_gen. That's because C is bound to a specific lifetime, not to any lifetime; i.e. when C is Parameter<'x>, the 'x must be known in advance; we can't have a different 'x each time we call the closure. In other words, what you'd like to have is something like this:
fn generate_closure_gen<C: for<'a> 'a>() -> Box<Fn(&C)> {
Box::new(|c| {})
}
But unfortunately, that isn't legal as of Rust 1.7.

Static method in trait dynamic dispatch

Trying to get dynamic dispatch working in a trait static method but get a type must be known error.
I'm trying to achieve something like
F#
https://github.com/Thorium/SimpleCQRS-FSharp/blob/master/CommandSide/Domain.fs
C#
https://github.com/gregoryyoung/m-r/blob/master/SimpleCQRS/Domain.cs..
Is the only way to make the trait generic?
pub struct Aggregate<T: AggregateRoot>
{
pub id: Uuid,
agg: T,
changes: Vec<Box<Any>>
}
impl <T :AggregateRoot > Aggregate<T>
{
fn GetUncomittedChanges(&self) -> Vec<Box<Any>> { self.changes}
fn MarkChangesAsCommitted(&self) { self.changes.drain(..);}
}
trait AggregateRoot
{
fn new2() -> Self; //should be private
fn new(id: Uuid) -> Self;
fn LoadsFromHistory(changes : Vec<Box<Any>> ) -> Self
where Self: Sized
{
let newAgg = AggregateRoot::new2 ();
changes.iter().map( |e| newAgg.Apply(e) );
newAgg.MarkChangesAsCommitted();
newAgg
}
fn Apply<U: Any>(&self, arg: U) ;
fn GetId(&self) -> Uuid;
}
currently trying but gives 2 params expected 1 supplied.
Let's start with issues in how you asked the question, in the hopes that you will be able to ask better questions in the future. The complete error you are getting is:
<anon>:27:37: 27:52 error: the type of this value must be known in this context
<anon>:27 changes.iter().map( |e| newAgg.Apply(e) );
^~~~~~~~~~~~~~~
Note that the compiler error message shows you exactly which bit of code is at fault. It's useful to include that error when asking a question.
You've also included extraneous detail. For example, GetUncomittedChanges, id and GetId are all unused in your example. When solving a problem, you should produce an MCVE. This helps you understand the problem better and also allows people helping you to look at less code which usually results in faster turnaround.
Your code has a number of problems, but let's start at the first error:
let newAgg = AggregateRoot::new2 ();
This says "for any possible AggregateRoot, create a new one". Many concrete types can implement a trait (which is the point of traits), but the compiler needs to know how much space to allocate for a given instance. There might be a struct that takes 1 byte or 200 bytes; how much space needs to be allocated on the stack in this case?
To progress, you can use Self::new2 instead. That means to create a new instance of the current implementor.
The next error is
<anon>:20:16: 20:40 error: no method named `MarkChangesAsCommitted` found for type `Self` in the current scope
<anon>:20 newAgg.MarkChangesAsCommitted();
^~~~~~~~~~~~~~~~~~~~~~~~
You are calling a method on a concrete type from a trait implementation; this simply doesn't make any sense. What would happen if a bool implements this trait? It doesn't have a MarkChangesAsCommitted method. I don't know what you intended in this case, so I'll just delete it.
Now you get this error:
<anon>:19:9: 19:16 error: `changes` does not live long enough
<anon>:19 changes.iter().map( |e| newAgg.Apply(e) );
^~~~~~~
note: reference must be valid for the static lifetime...
<anon>:17:5: 21:6 note: ...but borrowed value is only valid for the scope of parameters for function at 17:4
That's because your method Apply expects to be given a type that implements Any. However, you are passing a &Box<Any>. Any has a lifetime bound of 'static, and that reference is not static. A straightforward change is to accept a reference to a type that implements Any:
fn Apply<U: Any>(&self, arg: &U);
Now that the code compiles, there's a number of stylistic issues to fix:
no space before :
no space after >
no space before (
no space inside ()
map should not be used for side effects
function and variable names are camel_case
most of the time, accept a &[T] instead of a Vec<T> as a function argument.
use "Egyptian" braces, except when you are using a where clause.
All together, your code looks like:
use std::any::Any;
struct Aggregate<T: AggregateRoot> {
agg: T,
changes: Vec<Box<Any>>
}
impl<T: AggregateRoot> Aggregate<T> {
fn mark_changes_as_committed(&self) { }
}
trait AggregateRoot {
fn new() -> Self;
fn load_from_history(changes: &[Box<Any>]) -> Self
where Self: Sized
{
let new_agg = Self::new();
for change in changes { new_agg.apply(change) }
new_agg
}
fn apply<U: Any>(&self, arg: &U);
}
fn main() {}
Is there a way to constrain the concrete types of the AggregateRoot to Aggregates so mark_changes can be called?
Not that I'm aware of. It sounds like you want to move mark_changes to the trait and force all implementors of the trait to implement it:
trait AggregateRoot {
fn load_from_history(changes: &[Box<Any>]) -> Self
where Self: Sized
{
let new_agg = Self::new();
for change in changes { new_agg.apply(change) }
new_agg.mark_changes_as_committed();
new_agg
}
fn mark_changes_as_committed(&self);
// ...
}

Resources