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

Document const functions #440

Merged
merged 13 commits into from
Oct 13, 2018
Merged

Document const functions #440

merged 13 commits into from
Oct 13, 2018

Conversation

oli-obk
Copy link
Contributor

@oli-obk oli-obk commented Oct 5, 2018

tracking issue: rust-lang/rust#53555


Exhaustive list of permitted structures in `const fn`:

1. type parameters where the parameters have any of the following as part of their bounds (either on `where` or directly on the parameters):
Copy link
Contributor

Choose a reason for hiding this comment

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

only have

1. lifetimes
2. `Sized`

This means that `<T: 'a + ?Sized>` and `<T: 'b + Sized>` + `<T>` are all permitted.
Copy link
Contributor

Choose a reason for hiding this comment

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

What is "<T: 'b + Sized> + <T>" supposed to mean?

Copy link
Contributor

Choose a reason for hiding this comment

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

The generic parameters on functions, impls, etc.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I guess I wasn't clear there. I really mean, '+' should not appear in prose.

This rule also applies to type parameters of items that contain `const fn`s.

2. arithmetic operators on integers
3. boolean operators (except for `&&` and `||` which are banned since they are short-circuiting).
Copy link
Contributor

Choose a reason for hiding this comment

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

And comparison operators for integers, bool and char.

7. field accesses on structs and tuples
8. reading from constants (but not statics, not even taking a reference to a static)
9. `&` and `*` (only dereferencing of references, not raw pointers)
10. casts except for raw pointer to `usize` casts
Copy link
Contributor

Choose a reason for hiding this comment

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

any integer type.

@@ -160,6 +160,38 @@ attributes], [`must_use`], [the procedural macro attributes], [the testing
attributes], and [the optimization hint
attributes].

## Const functions

Functions can be `const`, meaning they can be called from within array length expressions and the initializer of constants, statics and enum discriminants. When called from such a so-called "const context", the function is interpreted by the compiler at compile time. The interpretation happens in the environment of the compilation target and not the host. So `usize` is `32` bits if you are compiling against a `32` bit system, irrelevant of whether you are building on a `64` bit or a `32` bit system.
Copy link
Contributor

@Havvy Havvy Oct 8, 2018

Choose a reason for hiding this comment

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

Let's actually give a name and definition with this. You informally call it `const fn` in future paragraphs, but the name should be prose, not code, so I think "const function" or "constant function" (or both?) works. We should also be more explicit about how they are defined. I propose the following, but feel to tweak it:

*Constant functions* are functions that can be called as a [constant expression]. They
are defined by putting the `const` keyword before the `fn` keyword.

The name should then be used in other parts of the text as appropriate.

Copy link
Contributor

Choose a reason for hiding this comment

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

I want to avoid calling these "constant functions" because it gives the impression that the functions are somehow constant; they aren't. They are pure functions (at least currently and besides some weird business with floating points). They also can't always be called in const ITEM = the_const_fn(...); contexts if given runtime arguments and they can always be called in runtime contexts.

If you must have a name in prose (which strikes me as less formal) then I would call it "const functions".

Copy link
Contributor

Choose a reason for hiding this comment

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

They also can't always be called in const ITEM = the_const_fn(...); contexts if given runtime arguments and they can always be called in runtime contexts.

The constant expression section currently states "The following expressions are constant expressions, so long as any operands are also constant expressions and do not cause any Drop::drop calls to be ran.". It's not the most precise wording, but it's supposed to handle that.

they can always be called in runtime contexts

All constant expressions can be.

If you must have a name in prose (which strikes me as less formal)

We want to have the name for other documentation none-the-less. And we want the name so that we can then discuss it in the prose further on.

This comment was marked as outdated.


Functions can be `const`, meaning they can be called from within array length expressions and the initializer of constants, statics and enum discriminants. When called from such a so-called "const context", the function is interpreted by the compiler at compile time. The interpretation happens in the environment of the compilation target and not the host. So `usize` is `32` bits if you are compiling against a `32` bit system, irrelevant of whether you are building on a `64` bit or a `32` bit system.

If a `const fn` is called outside a "const context", it is indistinguishable from any other function. You can freely do anything with a `const fn` that you can do with a regular fn.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Last "fn" -> "function".

@@ -160,6 +160,38 @@ attributes], [`must_use`], [the procedural macro attributes], [the testing
attributes], and [the optimization hint
attributes].

## Const functions

Functions can be `const`, meaning they can be called from within array length expressions and the initializer of constants, statics and enum discriminants. When called from such a so-called "const context", the function is interpreted by the compiler at compile time. The interpretation happens in the environment of the compilation target and not the host. So `usize` is `32` bits if you are compiling against a `32` bit system, irrelevant of whether you are building on a `64` bit or a `32` bit system.
Copy link
Contributor

Choose a reason for hiding this comment

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

Your `"const context" is already defined as a constant expression. That said, the bit about interpretation using the target's environment is useful to add there. And the explicit list of where constant expressions must be evaluated at compile-time is 💯.

I like the example as well, but it should probably say what the target's pointer size is before saying the size of usize. E.g. "For example, if you are compiling on a 32-bit OS, then usize has a size of 32 bits irrelevant of ..."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not quite. "const context" requires a constant expression. The definition in the documentation you linked is also incomplete. I'll add an entry and make both documentations point to it.


If a `const fn` is called outside a "const context", it is indistinguishable from any other function. You can freely do anything with a `const fn` that you can do with a regular fn.

`const fn`s have various restrictions to makes sure that you cannot define a `const fn` that can't be evaluated at compile-time. You will, for example, never be able to write a random number generator as a const fn. Calling a const fn at compile-time will always yield the same result as calling it at runtime, even if you call it multiple times. There's one exception to this rule: if you are doing complex floating point operations in extreme situations, then you might get (very slightly) different results. It is adviseable to not make array lengths and enum discriminants depend on floating point computations.
Copy link
Contributor

@Havvy Havvy Oct 8, 2018

Choose a reason for hiding this comment

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

concision nit: "that you cannot define a const fn that can't" -> "that they can".

tone nit: Let's not blame the reader for having the audacity of trying to write an RNG as a constant function. Perhaps they're merely curious if somebody could, as it'd be something important to keep in mind if it was possible. "For example, a random number generator cannot be written as a constant function."

nit: "if you call" -> "when called"

Since floating point is not permitted currently, the floating point section can be removed until it is decided what we do with it. We don't document the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

only floating point math is forbidden. You can define const fn foo(f: f32) -> f32 { f }. Depending on the registers used at runtime, your value might actually change.


`const fn`s have various restrictions to makes sure that you cannot define a `const fn` that can't be evaluated at compile-time. You will, for example, never be able to write a random number generator as a const fn. Calling a const fn at compile-time will always yield the same result as calling it at runtime, even if you call it multiple times. There's one exception to this rule: if you are doing complex floating point operations in extreme situations, then you might get (very slightly) different results. It is adviseable to not make array lengths and enum discriminants depend on floating point computations.

Exhaustive list of permitted structures in `const fn`:

This comment was marked as resolved.


Exhaustive list of permitted structures in `const fn`:

1. type parameters where the parameters only have any of the following as part of their bounds (either on `where` or directly on the parameters):
Copy link
Contributor

Choose a reason for hiding this comment

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

The parenthetical can be replaced by just making "bounds" a link to trait-bounds.html.

2. `Sized`

This means that `<T: 'a + ?Sized>`, `<T: 'b + Sized>` and `<T>` are all permitted.
Note that `?Sized` is the absence of a constraint when bounds have been fully elaborated
Copy link
Contributor

Choose a reason for hiding this comment

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

This note can replaced by putting or [`?Sized`] on the second bullet point in the sublist. Link to trait-bounds.html#sized

which includes adding implicit `Sized` bounds.
This entails that permitting `Sized` + lifetimes allows the above examples.

This rule also applies to type parameters of items that contain `const fn`s.
Copy link
Contributor

Choose a reason for hiding this comment

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

By "contain", I presume you mean implementations and traits. But do you also mean e.g. functions that contain a block expression that contains a constant function? Listing them out would help readability here.

3. all boolean operators except for `&&` and `||` which are banned since they are short-circuiting.
4. any kind of aggregate constructor (array, `struct`, `enum`, tuple, ...)
5. calls to other *safe* `const fn`s (methods and functions)
6. index operations on arrays and slices
Copy link
Contributor

@Havvy Havvy Oct 8, 2018

Choose a reason for hiding this comment

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

Index expressions? Or does operations mean more than just the syntactic array[indexer]?


`const fn`s have various restrictions to makes sure that you cannot define a `const fn` that can't be evaluated at compile-time. You will, for example, never be able to write a random number generator as a const fn. Calling a const fn at compile-time will always yield the same result as calling it at runtime, even if you call it multiple times. There's one exception to this rule: if you are doing complex floating point operations in extreme situations, then you might get (very slightly) different results. It is adviseable to not make array lengths and enum discriminants depend on floating point computations.

Exhaustive list of permitted structures in `const fn`:

This comment was marked as resolved.

2. arithmetic and comparison operators on integers
3. all boolean operators except for `&&` and `||` which are banned since they are short-circuiting.
4. any kind of aggregate constructor (array, `struct`, `enum`, tuple, ...)
5. calls to other *safe* `const fn`s (methods and functions)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: "(methods and functions)" -> "whether by function call or method call

Havvy
Havvy previously requested changes Oct 8, 2018
Copy link
Contributor

@Havvy Havvy left a comment

Choose a reason for hiding this comment

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

I do like a lot of this. The content is structured well. But, it's failing a bunch of not really written down rules, which I'm explicitly writing down here:

  1. Style is 80 chars per line.
  2. Defining terms involves italicizing the term being defined, done in markdown by surrounding with asterisks or underscores.
  3. Linking is better than re-explaining. Having a single point-of-truth makes it easier to keep the reference updated. Especially since I'm particularly bad about remembering and looking for those extra points.
  4. To make linking easier, we like to give names to anything worth talking about.
  5. Concision is useful. People are going to be using the reference to look things up.

(Note to self: This list should really be in the contributing guide)

I've already given a bunch of comments that I thought were going to be part of this review (thanks Github! Twice you're UI has let me make that mistake!)

10. casts except for raw pointer to integer casts
11. `const unsafe fn` is allowed, but the body must consist of safe operations only and you won't be able to call the `const unsafe fn` from within another `const fn` even if you use `unsafe`
> **Note**: this list is more restrictive than what you can write in
regular constants
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing >.

@oli-obk
Copy link
Contributor Author

oli-obk commented Oct 8, 2018

I think I addressed everything

@Havvy
Copy link
Contributor

Havvy commented Oct 8, 2018

Almost. My comment about giving a name to a function with const on it so that the prose can call it that is unaddressed.

Furthermore, new pages need to be added to SUMMARY.md or they will not be built.

@oli-obk
Copy link
Contributor Author

oli-obk commented Oct 8, 2018

My comment about giving a name to a function with const on it so that the prose can call it that is unaddressed.

I felt it was addressed by @Centril . I use const fn as a name for it. I'd be fine with calling them "const functions". I'm probably biased by attrition. I'll accept whatever the bikeshed results in. I'd just rather not have it here.

@@ -160,6 +160,59 @@ attributes], [`must_use`], [the procedural macro attributes], [the testing
attributes], and [the optimization hint
attributes].

## Const functions

Functions can be `const`, meaning they can be called from within
Copy link
Contributor

@Centril Centril Oct 8, 2018

Choose a reason for hiding this comment

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

I would somehow explain here that this applies so long as provided arguments (expressions) to the functions explicit formal parameters (e.g. those within (...) but not those within <...> which are rather implicit formal parameters) are also valid in const contexts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't do that for operators. I don't see how add(x, y) differs from x + y in the "explain constness"-aspect

Copy link
Contributor

Choose a reason for hiding this comment

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

I would say we should do that for operators in that case. This is the reference, so better be as unambiguous as we can be.

* The [dereference operator].
* [Grouped] expressions.
* [Cast] expressions, except pointer to address and
function pointer to address casts.
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing a bullet point for calling const fns.

@Havvy
Copy link
Contributor

Havvy commented Oct 8, 2018

I felt it was addressed by @Centril . I use const fn as a name for it. I'd be fine with calling them "const functions". I'm probably biased by attrition. I'll accept whatever the bikeshed results in. I'd just rather not have it here.

It's less a bikeshed and more that having spoken language names for concepts is important, and that we should introduce them explicitly. I'm perfectly fine with "const function". A lot of our spoken language names are just the keywords, but not in code blocks. E.g. "enums", "structs", "traits". And we can talk about these concepts without putting them in code blocks. It's easier to talk about "const functions" than it is to talk about "const fns" since one is a prose language and the other is not.

@Centril
Copy link
Contributor

Centril commented Oct 8, 2018

@Havvy that fair :) My worry was that by calling it "constant function" we might conflate it with const FOO: fn() = <def>;.

@Havvy Havvy added New Content Missing features or aspects of language not currently documented. RFC Stabilization Docs Documentation required for stabilizing a feature Organization Reorganize existing information in the book. labels Oct 9, 2018
@oli-obk
Copy link
Contributor Author

oli-obk commented Oct 9, 2018

It's easier to talk about "const functions" than it is to talk about "const fns" since one is a prose language and the other is not.

I've been calling them "consteffens" as one word since basically forever. But I have no problem with yielding to "const functions".

@oli-obk oli-obk changed the title Document const fns Document const functions Oct 12, 2018
@Centril
Copy link
Contributor

Centril commented Oct 13, 2018

I think this is good for now; we can always revisit with improvements later on.

@Havvy
Copy link
Contributor

Havvy commented Oct 13, 2018

Yeah, there are changes I want to make (linkify everything) that I don't want to put on somebody who's not familiar with all the pages in the reference, bu what we have here is definitely good enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
New Content Missing features or aspects of language not currently documented. Organization Reorganize existing information in the book. RFC Stabilization Docs Documentation required for stabilizing a feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants