Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Expressions rewrite #13

Merged
merged 45 commits into from
Jun 1, 2017

Conversation

matthewjasper
Copy link
Contributor

@matthewjasper matthewjasper commented Mar 15, 2017

So this is a WIP attempt to improve the expressions section. It documents more features, fixes inaccuracies and hopefully makes things clearer. All feedback is welcome.

Changes

  • General
    • Always wrap at 80 characters
    • Make more things links
  • Structs
    • State that struct expressions can't be used in control flow expressions
  • Methods
    • Add examples for methods
    • Explained auto-deref
      • UFCS (or whatever it's called now) isn't in the reference!!! It has a PR now.
  • Fields
    • Show the syntax for calling a struct field
  • Calls
    • Move before operators
    • Explain the function call syntax
    • Mention Fn traits
  • Lambdas
    • Move lambda section
    • Lambdas have unique types
    • move is part of the lambda expression according to libsyntax, so I'm going with that
    • Explain that move can extend the lifetime of the closure
    • Use closure, except when followed by 'expression'
    • Closure traits depend on how captures are used, not on how they are captured
  • Arrays
    • Explain what arrays expressions do
    • statics aren't constant expressions
    • Explain Copy requirement
  • Index
    • Specify how overloading is done
    • Indices must be of type usize
  • Unary operators
    • Explained std::ops::Neg
    • Hopefully made it clearer how Deref works
    • Explained std::ops::Not
    • Say that &mut can't be applied to everything
  • Binary operators
    • Merge with unary operators
    • Built in operators don't use traits
    • Show all of the operators in examples
    • Put all of this into a table, to avoid having to repeat so many words.
  • Compound assignments
    • Explain overloading of these operators
  • Precedence
    • Add unary operators
    • Show the correct associativity
  • Grouped expressions
    • Better example
  • Loops and control flow.
    • Use 'then' instead of and for sequenced operations in while loops
    • State that all loops return () (Except loop but that's in another PR)
    • Add some more examples
    • Don't use 'identical' for things that aren't (would also need to look at if let semantically equivalence to if is wrong w.r.t. scope #47 in this respect)
    • Make the definition of if let and while let clearer

Update:

  • Further Changes
    • Mention array to slice coercions happening in method calls.
    • Use 'mutable lvalue (context)' to describe some things. (I have no idea if this is how it should be described, but this was the easiest way to describe the behaviour that I tested).
    • Explain when lvalues can be moved out of. (or, how not to do a move semantics tutorial)
    • Explain that most things are actually in libcore, in only one place.
    • Explicitly say that blocks are rvalues.
    • Say exactly what operations are overflow.
    • Say some things about implicit borrows.
    • Add examples to show what is meant by method 'receiver'.
    • Explain when fields can be viewed as separate entities for borrows and moves.
    • Note that methods don't use lifetimes, mutability or safety to help with resolution.
    • Say that paths to static mut require unsafe in this chapter.
    • Enums variants can also be used in 'struct expressions'
    • Mention tuple indexing.
    • Mention that generic traits and methods are considered to be one trait for method resolution
    • Show some paths with :: in path expressions section.
    • Document const expr (including RFC 71)
    • Document casts, sort of.
    • RFC 736 - Privacy respecting Functional Record Update (FRU)
    • RFC 968 - Closure return type syntax
    • RFC 1229 - Compile time asserts
    • RFC 1535 - Stable overflow checks

I'll probably split this up into pieces that can more easily be reviewed and avoid blocking everything on the lang team explaining how lvalues actually work.

* Add examples for methods
* Show the syntax for calling a struct field
* Explain the function call syntax
* Mention `Fn` traits
* Move lambda section
* Lambdas have unique types
* `move` is part of the lambda expression according to libsyntax
* Explain that `move` can extend the lifetime of the closure
* Use closure, except when followed by 'expression'
* Closure traits depend on how captures are used, not on how they are captured
* Parameters are patterns, not idents.
* Make it clear that inference can be done on just some of the parameters
* Explain what arrays expressions do.
* `static`s aren't constant expressions.
* Explain `Copy` requirement.
* Specify how overloading is done.
* Indices must be of type `usize`.
* Explained `std::ops::Neg`.
* Hopefully made it clearer how `Deref` works
* Explained `std::ops::Not`.
* Say that `&mut` can't be applied to everything.
* Built in operators don't use traits.
* Explain that the type of right shift depends on the type.
* Explain overloading
* Add unary operators
* Show the correct associativity
* Use 'then' instead of and.
* State that all loops return `()`.
* Add some more examples.
* Don't use 'identical' for things that aren't.
* Make the definition of `if let` and `while let` clearer.
* State that struct expressions can't be used in control flow expressions.
Add operator examples
Mention `if let` and `while let` as more places where struct literals can't be used.
Copy link
Contributor Author

@matthewjasper matthewjasper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions I had while writing this. (Outdated comments are still sometimes relevant)

is typically the innermost enclosing statement; the tail expression of
a block is considered part of the statement that encloses the block.
When an rvalue is used in an lvalue context, a temporary un-named lvalue is
created and used instead. The lifetime of temporary values is typically the
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't true for the LHS of =.

1. Trait methods with receiver of type `&A`.
1. Trait methods with receiver of type `&mut A`.
1. If it's possible, Rust will then repeat steps 1-5 with
`<A as std::ops::Deref>::Target`, and insert a dereference operator.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this clear and correct? Is it what is intended?

Copy link
Contributor Author

@matthewjasper matthewjasper Mar 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some other explanations to look at
typeck - suggests there is also 'unsizing' happening.
stack overflow

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might not be perfect, but it's also not incorrect; we may want to elaborate later, but I don't think it needs to block this PR

## Lambda expressions

A _lambda expression_ (sometimes called an "anonymous function expression")
defines a closure and denotes it as a value, in a single expression. A lambda
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I using 'lambda expression' and 'closure' correctly? Is there even a (single) correct choice?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally would always refer to these as "closures" everywhere. Rust only has "closures" and "functions", it does not have anonymous functions nor named closures. "lambda" adds another term for no real gain, IMO.

Operators are defined for built in types by the Rust language. Operators will
panic when they overflow when compiled in debug mode. Many of the following
operators can be overloaded using traits in `std::ops` or `std::cmp`. Note that
the traits in `std::cmp` are used more generally to show how a type may be
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, and other places, should I link to std documentation?
Should I mention that these are also in core?

assigned to. Dereferencing a raw pointer requires `unsafe`. On non-pointer
types `*x` is equivalent to `*std::ops::Deref::deref(&x)` or
`*std::ops::Deref::deref_mut(&mut x)` depending on whether the rquired
lvalue has to be mutable.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should deref coercions be explained here, or in the Deref chapter, or elsewhere?

and written in postfix notation.
: Propagating errors if applied to `Err(_)` and unwrapping if applied to
`Ok(_)`. Only works on the `Result<T, E>` type, and written in postfix
notation.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestions on how to improve this are welcome.

| `<<` | Left Shift | | | `std::ops::Shl` |
| `>>` | Right Shift* | | | `std::ops::Shr` |

\* Arithmetic right shift on signed integers, Logical right shift on unsigned integers.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tables for operators, 👍/👎?

the borrow expires. If the `&` or `&mut` operators are applied to an
rvalue, a temporary value is created; the lifetime of this temporary value
is defined by [syntactic rules](#temporary-lifetimes). `&mut` may only be
applied to `lvalues` that can be mutated. These operators cannot be
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to write 'mutable lvalue' so much, but is that the proper way to say what I mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly relevant part of typeck.

```rust
let x = false || true; // true
let y = false && panic!(); // false, doesn't evaluate `panic!()`
```

### Type cast expressions
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I copy the (old) book explanation for all of the as casts here, or somewhere else?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here seems good

"Smaller"
};
assert_eq!(y, "Bigger");
```

## `match` expressions
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can patterns be moved to their own section?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems good to me

@matthewjasper
Copy link
Contributor Author

cc #9 (RFC 558, maybe 560 also), #10, #20, #23.

@chriskrycho chriskrycho mentioned this pull request May 13, 2017
48 tasks
a block is considered part of the statement that encloses the block.
When an lvalue is evaluated in an _rvalue context_, it denotes the value held
_in_ that memory location. If value is of a type that implements `Copy`, then
the value will be copied. In other situations if the type of the value is
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few places I'm using 'other' to mean 'all other' or 'otherwise'. Any suggestions for an alternative wording would also be appreciated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you don't like 'other' here? Seems fine to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that could be interpreted as 'some other'.

@steveklabnik
Copy link
Member

Hey @matthewjasper

Sorry this has taken me so long to look at, big PRs are tough. I will try to do so this week.

@matthewjasper
Copy link
Contributor Author

I understand that, feel free to only review some of this or ask me for things to make this easier to review.

Copy link
Member

@steveklabnik steveklabnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whew! thank you so much for all of this, and sorry it took me so long.

I'm broadly in favor of merging this, but there's some formatting nits, and the lambda thing.

a block is considered part of the statement that encloses the block.
When an lvalue is evaluated in an _rvalue context_, it denotes the value held
_in_ that memory location. If value is of a type that implements `Copy`, then
the value will be copied. In other situations if the type of the value is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you don't like 'other' here? Seems fine to me.

the value will be copied. In other situations if the type of the value is
[`Sized`](the-sized-trait.html) it may be possible to move the value. Only the
following lvalues may be moved out of:
* [Variables](#variables.html) which are not currently borrowed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a newline above this? I like to let it breathe a bit 😄

```

Implicit borrows may be taken in the following expressions:
* Left operand in [method-call expressions](#method-call-expressions).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here


When resolving method calls on an expression of type `A`, Rust will use the
following order:
1. Inherent methods, with receiver of type `A`, `&A`, `&mut A`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here


Note: that in steps 1-4 the receiver is used, not the type of `Self`, which may
not be the same as `A`. For example
```rust
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

* `e` is a function pointer type and `U` is an integer; *fptr-addr-cast*
* Same concerns as with pointer to address casts

TODO: Explain the semantics of these.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing so doesn't block this PR, IMHO; the reference is still incomplete.

present, then labelled `break` and `continue` expressions nested within this
loop may exit out of this loop or return control to its head. See [break
expressions](#break-expressions) and [continue
expressions](#continue-expressions). Any `loop` expression has value `()`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not true anymore now that that is being stabilized

conditional expression evaluates to `false`, the `while` expression completes.

An example:
An example:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this space?

"Smaller"
};
assert_eq!(y, "Bigger");
```

## `match` expressions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems good to me

pattern, an `=` and an expression. If the value of the expression on the right
hand side of the `=` matches the pattern, the loop body block executes then
control returns to the pattern matching statement. Otherwise, the while
expression completes. Like `while` loops, `while let` loops evaluate to `()`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same issue here wrt new features

@matthewjasper matthewjasper force-pushed the expressions-rewrite branch 2 times, most recently from 2e8f7a4 to d6ee031 Compare May 26, 2017 21:48
@steveklabnik steveklabnik merged commit 986aea4 into rust-lang:master Jun 1, 2017
@steveklabnik
Copy link
Member

Thanks so much! I'm happy to land this even with the WIP bits.

@steveklabnik
Copy link
Member

Looks like this ended up breaking some links

[00:46:48] reference/variables.html:73: broken link fragment `#lvalues-rvalues-and-temporaries` pointing to `reference/expressions.html`
[00:46:48] reference/tokens.html:291: broken link fragment `#unary-operator-expressions` pointing to `reference/expressions.html`
[00:46:48] reference/tokens.html:343: broken link fragment `#unary-operator-expressions` pointing to `reference/expressions.html`
[00:46:48] reference/tokens.html:343: broken link fragment `#binary-operator-expressions` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:115: broken link fragment `#the-drop-trait` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:182: broken link fragment `#array-and-slice-types` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:213: broken link - reference/let-statements
[00:46:48] reference/expressions.html:222: broken link fragment `#paths` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:247: broken link fragment `#dereference-operator` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:273: broken link fragment `#lvalues-rvalues-and-temporaries` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:274: broken link fragment `#unsafe-block` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:478: broken link fragment `#lvalues-rvalues-and-temporaries` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:486: broken link fragment `#the-drop-trait` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:675: broken link fragment `#lvalues-rvalues-and-temporaries` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:766: broken link fragment `#lvalues-rvalues-and-temporaries` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:933: broken link - reference/type.html
[00:46:48] reference/expressions.html:992: broken link fragment `#lvalues-rvalues-and-temporaries` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:994: broken link fragment `#lvalues-rvalues-and-temporaries` pointing to `reference/expressions.html`
[00:46:48] reference/expressions.html:1226: broken link fragment `#lvalues-rvalues-and-temporaries` pointing to `reference/expressions.html`
[00:46:48] reference/types.html:305: broken link fragment `#lambda-expressions` pointing to `reference/expressions.html`
[00:46:48] reference/string-table-productions.html:72: broken link fragment `#unary-operator-expressions` pointing to `reference/expressions.html`
[00:46:48] reference/string-table-productions.html:73: broken link fragment `#binary-operator-expressions` pointing to `reference/expressions.html`

I don't know why CI didn't catch this...

@matthewjasper
Copy link
Contributor Author

Should I submit a new PR to fix them?

@steveklabnik
Copy link
Member

@matthewjasper that'd be great if you have the chance; I just pushed a commit fixing some of them If you have the chance to take the rest, that'd be great! I'd like to get this done today since tomorrow is the cutoff for beta; rust-lang/rust#42353 is the actual PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants