Syntax survey for a potential RFC (please participate, especially if you don't know Rust too well :) )

Consider the following piece of code:

struct Tree;
struct Bush {twigs: i32}

struct Forest {
    tree: Tree,
    bush: Bush,
}

impl Default for Bush {
    fn default() -> Self {
        Self {
            twigs: 2
        }
    }
}

impl Default for Forest {
    fn default() -> Self {
        Self {
            tree: Tree,
            bush: Bush {twigs: 1},
        }
    }
}

fn main() {
    let forest = Forest {
        tree: Tree,
        ..Default::default()
    };
    println!("{}", forest.bush.twigs);
}

Without checking any documentation, but just by looking at the code, what do you think the output will be?

Additionally, please answer if you are a person that usually helps other people in this forum, or if you rather come here and learn. There is no clear border, just answer what you rather identify with.

  • 1 (and I frequently help people in this forum)
  • 2 (and I frequently help people in this forum)
  • 1 (and I rather learn from this forum)
  • 2 (and I rather learn from this forum)

0 voters

And if you want to answer, please don't check what other people write below first :slight_smile:

(some empty space just in case people post answers below)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

4 Likes

I both help people and learn others on this forum, so not sure what I should categorize myself as ... Now to check if my answer was correct or not ... (it was not).

Not gonna lie, making Tree a unit struct really threw me off because it looked like the Forrest { tree: Tree, ... } bit was using a type as a value.

I'm also not sure what new syntax you are proposing because that snippet is valid Rust.

2 Likes

Yes it is valid Rust, this is rather about changing syntax to avoid misunderstandings.

Maybe this is beside the point, but I think the syntax "Default::default" is a bit too popular. Implementing the Default trait gives the type a method called default. So I'd prefer to always specify the type when possible. In this case you'd write Forest::default() and it would be a lot less mysterious.

I think this is just down to our docs and common tutorials. The type can be inferred in Default::default but why repeat the word default twice anyway, it's not easy to read. Often, we can even shorten it to T::default - shorter and clearer - in generic context.

31 Likes

Yes, that would indeed make it obvius that it means "copy the rest of the fields from a default forest" and not "initialize the rest of the fields with their defaults".

6 Likes

Making surveys is hard, if you want to show that people are "surprised" by what happens you should maybe have a question like "on a scale of 1 to 10 how weird does this snippet seem to you ?" (I don't claim this is a good example of a good question).

I was confused, but just reading it like I would a PR at work cleared it up.

1 Like

Not surprised. That is to say that having never written an "empty" type like struct Tree; or used default and rarely the .. thing I managed to think it through and get the right answer. Although maybe not with 100% certainty.

Perhaps clippy could suggest writing ..Forest::default() instead of ..Default::default() for increased clarity.

I consider myself to be a "meta helper". I ask daft questions and the excellent answers that come back help others who were as confused as myself :slight_smile:

9 Likes

Seems like purely a question of knowing what FRU syntax does. I am not sure what kind of syntactic change you are proposing, either.

2 Likes

Can you explain your thesis @isibboi? I guessed correctly and I am far from a Rust expert (in fact, I haven't written any Rust code in about 2 years, though I would love to). I'm not sure why it would/should be the other option.

here you can read about it

The idea is that the struct update syntax is misleading in certain cases to a reader who does not know about it.

The syntax means that the remaining fields are filled with default values, however in my opinion the source of those is not clear just by the syntax itself. It could be the default constructed types of the fields themselves, or the field values of the default constructed surrounding type.

1 Like

Given the results of the poll it does not seem that the current syntax is that confusing. There are plenty of other things in Rust that are not so obvious if you are not familiar with them. Which is true of pretty much all languages.

In short, you can't escape having to learn the language you are using.

I would vote against adding more syntax like but assign for this. At first sight that is even less obvious.

5 Likes

Anything can be confusing to anyone who doesn't know about it.

Logically, if we assume a tiny bit of coherence in the language, then it simply couldn't be. Here's why.

Default::default() is an expression, just like any other expression. If one were to replace the return value of Default::default() with any other value, i.e. something that is not the default, then what would the FRU even mean? Since Foo { field1: expr1, ..any_other_expr } compiles just as well, it necessarily implies that the rest of the fields comes from the value itself, and Default::default() is not a magic marker.

6 Likes

Let's keep discussion about the actual topic in the other topic.

2 Likes

I'm relatively new to Rust with a long background in C# (if that makes a difference).

At first glance, I would expect to see the result as 2. As in, for the rest of the properties, use that property's type default.

The more I look at it, the more I see the nuance.

For example, if the above is true, how do I apply the Forest's defaults instead of the Bush's defaults? The reverse seems easy

let forest = Forest {
        tree: Tree,
        bush: Bush::default()
    };

PS: Bush::default() might not be the correct syntax, but I think you get it

  • 22% of all responders guessed wrong. That is very high!

  • 34% of the respondents who use this forum for learning guessed wrong. That is very high!

Note that this is not a random guess. It is a guess based on knowledge and intuition, and language features are supposed to be intuitive – so you can guess the correct answer even if you don't know the exact details of how it works. Clearly this language feature is not very intuitive.

6 Likes

I think 76% of respondents getting it right is pretty good.

After two years of using Rust there is a lot of Rust snippets that pass through here everyday that I have no idea about what it does.

That tells me this particular language feature is pretty intuitive after a little Rust learning and use.

I'm sure you could present obscure code in C, C++ or Javascript that would have people puzzling just the same.

Anyway, I think a clippy lint that suggests using Typename:: rather than Default:: might be helpful.

8 Likes

There is at least a lint for this in simple cases: default_trait_access. I agree that using clippy is a great way to address the concern in the short term.

4 Likes

Assessing whether a language feature is good based on users' intuition is not the way to go, specially if users who guessed wrong (like me) come from languages where constructors are magical and guess a lot for you... sometimes a bit too much.

For example, in Scala you have case classes which are a "functional" way of constructing classes and represent the product algebraic type. All is great with case classes until you have to deal with the magic that the language was hiding from you.

Coming to Rust, at the beginning you might feel uncomfortable because the language is not giving you everything (having to implement even simple traits like Debug), but trust me, it's much better when languages put explicitness in front of implicitness. Heck, even Scala has implicit as a keyword and users abuse of it to the extreme (j/k).

But being serious, one has to be careful when a feature's intuitiveness is just a matter of what languages you are used to, and not how useful the feature is.

6 Likes

I don't think first impressions should guide the language. Once you've learned the larger meaning of ..expr in FRU, the greater pictures makes much more sense than a special syntax for defaults. Even then, twice as many learners guessed right than wrong.

Anyway, the larger meaning isn't going away and the meaning of the expression Default::default() can't be changed, so it's primarily a question of learning material and/or clippy lints plus programming norms, IMO.

†: And other things like evaluations not being implicitly lazy, etc. "Unintuitive at first sight" doesn't warrant "special case compared to the rest of the language".

P.s. if you got this wrong but ace dtolney's quiz, say, I want you on my team.

10 Likes