Inconsistency in trying to be brief

let foo = if x { y } else { z };//We don't want to use return here. Quite understandably good argument

Why I have to have that self everywere:

pub struct SplStr
{
   a_vec: Vec<i64>,
   cur_inx: usize,
}

impl Iterator for SplStr
{
type Item = i64;

fn next(&mut self)->Option<Self::Item>
{
    if self.cur_inx < self.a_vec.len()
    {
    let sm = Some(self.a_vec[self.cur_inx]);
    self.cur_inx = self.cur_inx + 1;
    sm
    }
    else
    {
        None
    }
}
}

let Self { a_vec, cur_inx } = self;

Then you can just use a_vec and cur_inc without self..

3 Likes

Thanks, I appreciate. It would be actually nice that I wouldn't have to do it myself but the compiler could do it for me.
But, this as a workaround will do. Thanks.

The difference is a fundamental one with respect to safety-oriented language design.

Implicit self. would allow introducing errors by inadvertently shadowing local variables, as well as hiding potential side effects in Deref. (And I'm pretty sure these are not its only downsides.)

However, implicit return isn't even really an implicit return. It does not alter control flow – it's merely the case that in Rust, almost everything is an expression, if and blocks are no exception. So the last value of a block/if expression/function body is just the value of the block/expression/function, respectively, ans there's no additional distraction that would risk introducing bugs.

2 Likes

Nahhh...
It just like in C++ implicit 'this'.

Note that I can still shadow local variables. Nothing stops me.

I do realize that it would be like implicit this in C++. That's exactly what I was reacting to.

Yes, but 0. there are warnings and Clippy lints against that already, and 1. you actively need to request it by declaring a let binding with the same name and 2. when you do that, you are looking at the function body, where every potentially-shadowed variable name is in plain sight. In contrast, member fields might be far away in the source code, making the shadowing much more non-obvious.

In general, Rust tries to avoid non-locality syntactically as well as at the type level – implicit self. wouldn't fit into this scheme.

5 Likes

Yes, and that's why in C++ you often see naming conventions where all member variables start with m_. People realise that implicit this can cause confusion when wondering where something comes from, so they came up with a manual procedure that workarounds around it.

Now just replace every m_ with self. and you've got the same thing, except in Rust the compiler will ensure you can't just forget a self. or have it shadow another variable.

6 Likes

This. (heh)

2 Likes

I totally agree with all of the sentiment against and implicit this/self. I think it's a really good thing that Rust doesn't do that, but @piter76, even though I highly recommend against it, it would probably be trivial to create a proc macro that would allow you to put an #[implicit_self] annotation above your function and it would automatically insert that code that @Phlopsi posted at the top of your function.

I don't suggest it by any stretch, but it's cool to realize how customizable Rust is.

4 Likes

I think the main problem is that you cannot do pattern matching on self arguments in the arguments list. For a non-self argument, one can do this kind of syntax:

struct SplStr {
    a_vec: Vec<i64>,
    cur_inx: usize,
}
impl SplStr {
    // no `self` parameter, so can only be called as `SplitStr::next_fn(...)
    fn next_fn(Self { a_vec, cur_inx }: &mut Self) -> Option<i64> {
        if *cur_inx < a_vec.len() {
            let sm = Some(a_vec[*cur_inx]);
            *cur_inx = *cur_inx + 1;
            sm
        } else {
            None
        }
        
    }
}
impl Iterator for SplStr {
    type Item = i64;

    fn next(&mut self) -> Option<Self::Item> {
        Self::next_fn(self)
    }
}

In the case of non-mutable reference you will even be able to obtain both a reference to the whole thing as well as references to the field with this syntax, once this feature is stabilized.

#![feature(bindings_after_at)]
struct SplStr {
    a_vec: Vec<i64>,
    cur_inx: usize,
}
impl SplStr {
    fn blah(this@Self { a_vec, cur_inx }: &Self) { } 
}

Writing self@... is unfortunately not allowed syntactically. In the case of mutable reference arguments or by-value args, this something@Type { fields } syntax can’t ever work. I think this also constitutes a strong reason for having self.field be explicit, at least around anything but immutable references. Otherwise, accessing field could borrow (and hence block) or even destructure the "variable" self without even mentioning it.

4 Likes