Document 'sigils' in std docs?


#1

Cross-posted from Reddit and slightly expanded:

I love the new Keywords section that has been added to the docs. It makes it easy to reference or discover the most common constructs you might find in someone’s code.

In addition to these keywords there are also a number of symbols / sigils specific to Rust (its more than ‘sigils’, but ‘symbols’ might have other connotations). Since they are often “less speaking” and harder to google, I was wondering if it made sense to document the most common ones as well in the same place.

Here is what I was thinking:

I did a quick search in the Rust issue tracker but didn’t find anything related to that (except the issue about adding Keywords).

Some questions:

  • Has this been discussed, and do people feel there is a need for that?
  • Is this the right forum to have a discussion for that?
  • If I’m willing to put time into this, what would be the next thing to do?

Edit: To clarify the last point a bit more, here are the steps I think need to happen:

  • There needs to be general buy-in that this should be part of the docs
  • a #[doc(sigil = "...")] (or similar) attribute needs to be created
  • The scope and list of items will have to be discussed
  • Actual writing and QA

However, my question is rather, what is the right order? Is this an RFC thing? Start with an actual outline of items intended and bounce them? “Just do it”?


#2

Many of those exist: check out std::ops

The docs on those could probably be rearranged so that it starts by specifying the relevant syntax in the first line.

This would make std::ops’s traits section browsable as an index for many/most of those.


#3

Many of those exist: check out std::ops

In my mind sigils are everything that’s not std::ops. I wouldn’t want to discuss how Rust handles x = a + b, but I might be interested in documenting what T: R + S means.


#4

|x|... is arguably Fn/FnOnce/FnMut. The docs don’t currently specify that |x|... is the usual way to make an Fn*.

Also note that ? is in std::ops.

So I guess the only sigils left are 'labels and 'lifetimes and generics?


#5

Thanks, I didn’t see that was std::ops already.

When it comes to |x|, those a different from FnX to me and should be documented separately. |x| happens to implement one or more of them, but there is much more to say about the construct itself.

So I guess the only sigils left are 'labels and 'lifetimes and generics?

I haven’t done a comprehensive list, but my gut feeling is there might be a few more.


#6

I do believe they should be specified in std::ops, personally.


#7

Replying to my own post; now that I think about it, there might actually be value in documenting “non-obvious” operations even though they might be std::ops, since the latter defines what semantically happens for a given ops, while the former should document how the op interacts with the language (e.g., ? might be such a candidate).


#8

While it’s sort of an interesting problem to try and pin down what “sigil” means, I don’t think “I want to learn all the sigils in Rust” is a thing novices ever say to themselves, or a use case that official documentation should be trying to solve. The best way to learn most (all?) of the sigils is simply to read the Rust Book, and if you haven’t read The Book you probably don’t have enough background information to make sense out of the docs anyway.

Instead, let’s think about cases where someone had read The Book, then stumbled across syntax they didn’t understand, and tried to search for it but failed to find an explanation. In my personal experience, this is never a problem with a single sigil in isolation. Rather, it’s always a problem with a combination of sigils, typically mixed up with a bunch of other stuff so the user is overwhelmed by the unfamiliarity. For example, if we search the users forum for “syntax confused”, the first result is a question about HRTB. HRTB syntax is pretty intimidating at first, but it clearly wouldn’t fall under any single “sigil”.

tl;dr Special documentation for “sigils” feels like a solution in search of a problem, so I’d like to back up and brainstorm more about what the problem we’re trying to solve is.


#9

It’s good for reference, say if you haven’t done rust for 2 years and you suddenly find yourself trying to debug a large codebase. (this has happened to me a few times, altho with other languages)


#10

Could you elaborate on this some more? Which “sigils” would you expect someone to forget the meaning of after a few years, without also forgetting so much of Rust they’d have to just reread The Book anyway? (personally, I think I would forget semantics of the trait system much sooner than any of the sigils)


#11

Out of curiosity, why are keywords then documented they way they are now?

I don’t think “I want to learn all the sigils in Rust” is a thing novices ever say to themselves

I think the same is true for “I want to learn all the functions String provides”. I believe there is value in being able to a) quickly find or reference something you might have seen in code and b) get an executive overview / reminder how a certain feature works.

I agree however that the documentation might have to focus on more than the mere sigil in question, but would also have to take into account / back-link to more advanced use cases (e.g., into the book)


#12

One more thing I could have made clearer in the original post:

I would like this documentation to target mid-stage developers. As such it should have less text than the book, but have a much higher information density.

Sample use case: "I want to write a generic function and I know it was something with <T>". The documentation there might then in bullet points list:

  • that these get monomorphized or become template methods depending on impl or dyn
  • you can’t use types but only traits for Tk
  • when T is Sized or not, and what that means
  • that bounds can be specified with T : X , where

The point would be having a reference-like documentation for language features like these.

Edit: Also, the name sigil might be misleading and I totally don’t mind changing it. What I just want is something more than keywords that explains the most common language constructs (expressed by non-keyword symbols) in a concise way.


#13

I’m not on the docs team or anything so it’s not like I can officially answer this, but I imagine it makes more sense for keywords because:

  1. Whether something is or is not a “keyword” is pretty much an objective fact. “Sigil” is significantly fuzzier.
  2. Keywords are much easier to search for than sigils, so the odds of someone ever looking for and then actually finding the docs for them is much higher
  3. We’re far more likely to use keywords than sigils for niche, special-purpose features where dedicated documentation makes sense (e.g. become for guaranteed TCO).
  4. There are just more keywords than there are sigils, and that’s likely to become more true in the future.
  5. Sigils are also a lot broader and fuzzier in scope, so it’s harder to tell what would even go on a sigil’s doc page. For instance, the if keyword page obviously should say something about if expressions, and that’s about it. The & sigil page should clearly mention references, but should it also talk about borrow checking? Move semantics? Deref coercion?

Obviously none of this is an argument that sigil documentation is a non-starter, just that keyword docs are a lot more straightforward and obviously useful.

What page would this be though? The < sigil page? The <T> page? Or a “generic functions syntax” page?

What searches would we want to lead people to these pages? i.e., should https://doc.rust-lang.org/std/?search=<T> send me to a “generic functions syntax” page? What about searching fn foo<T>() -> T {}?

I’m not necessarily opposed to any of these ideas, it’s just very unclear to me what pages there would be, what would be on them, how users would find them, and what use cases it would actually solve, which to me is a strong sign that “sigils” might not be the right thing to focus on.

And this is exactly the kind of thing I was really trying to get at. In other words, instead of going after individual “sigils” maybe we’d get far more benefit out of documenting larger syntactic constructs like “trait bounds syntax” or “generic functions syntax” or “closure syntax”. Those are things I can actually imagine people searching for successfully and have relatively clear scope.

Which then gets us to questions like should the std docs be documenting syntax, or is that the Reference’s job? This might also overlap with whatever the Grammar Working Group is doing.

Again, I’m not trying to argue against doing anything, but for being much clearer about what the goal is supposed to be. For instance, my first thought now is to let the Reference be in charge of exhaustively and comprehensively documenting the formal grammar, but to also add std docs pages for things like “generic function syntax” that links to the relevant part of the Reference, then walks through a few toy examples of the most interesting and useful parts of the syntax like a basic type parameter, then a trait bound, then an impl trait, etc, and finally ends on a bullet point list of links to further reading such as a chapter in The Book on HRTBs. That seems way more likely to be discoverable and useful for novices searching “how do I write generic functions in Rust?” than, say, a doc page for the < sigil.


#14

And this is exactly the kind of thing I was really trying to get at. In other words, instead of going after individual “sigils” maybe we’d get far more benefit out of documenting larger syntactic constructs like “trait bounds syntax” or “generic functions syntax” or “closure syntax”. Those are things I can actually imagine people searching for successfully and have relatively clear scope.

Which then gets us to questions like should the std docs be documenting syntax, or is that the Reference’s job ? This might also overlap with whatever the Grammar Working Group is doing.

You might have a point that the Reference could be a better place of documenting the underlying constructs.

In that case I think:

  • the Keywords section from std should actually move / be merged with the reference
  • the Reference needs a simple “visual recognition guide” that is a mix of the current std Keywords and the proposed “Sigils” (one reason I want this is having the ability to look up a construct I saw in code which the current reference does not provide)
  • The std documentation should maybe have a “Keywords & Sigils” section at the end (replacing keywords) that just prominently links to that “visual recognition guide” to make sure there is an easy path of discoverability.

I could get used to both, std or reference. I just think wherever keywords go there should be a comparable “non-keyword construct” section with easy discoverability and mid-level explanation.

What page would this be though? The < sigil page? The <T> page? Or a “generic functions syntax” page?

In my (naive) understanding < does not do anything interesting on its own (see below), and <T> is the canonical way to express a type parameter; so I would put it there. That page could be have a description of “generic type syntax”.

What searches would we want to lead people to these pages? i.e., should https://doc.rust-lang.org/std/?search=<T> send me to a “generic functions syntax” page? What about searching fn foo<T>() -> T {} ?

I have not thought about searching.

For the other symbols present in fn foo<T>() -> T {} I would only document <T>.

Regarding the other tokens, fn is a keyword, and (), ->, {} are “not exciting enough”, as they do not behave significantly differently than in other languages on this level. I would also not document < on its own (as a comparison operator), by the same logic.


#15

Nobody mentioned yet that there is a table of operators and symbols in “The Rust Programming Language” (in the older versions it was called syntax index). Does this come close to what you want?


#16

Just pasting my answer from the Reddit thread to the same question:


I think that section is nice from a purely syntactic perspective, but I am missing a “middle ground” between the book’s tutorial-style, and this rather “syntactic” appendix.

The Keyword section looks like it’s delivering that, which is why I’m so happy about it.

I would love them (and “Sigils” / “constructs”), target mid-stage developers who know the basics (possibly coming from another language), and need a concise refresher how in Rust these particular concepts work out.

For example the Section |x| should target a developer saying “I know what a closure is in C++ / Python, but can you explain how it works in Rust in two pages or less with mostly bullet points?”


Does this come close to what you want?

I think my proposal comes from wanting a particular level of style, and being confused about the direction or content of the std docs.

  • The std docs have so far been a reference for types, methods and other “pre-assembled” content
  • At the same time, the writing style is succinctly “hands on”, without reiterating the obvious, but listing all important things one needs to know

Now with Keywords added they enter “language construct” territory; async, crate, dyn, … being in a different class than u32 and Deref.

Personally I don’t mind them being included, as I always wanted a place to look these things up, and std docs had the perfect style for my taste (with the book being too tutorial-ish, and the reference often being like “before we explain how a struct works, here are 2 pages of grammar”).

However, with them being included, I was just also hoping for “the rest” to be documented in a similar style to the rest of std docs. A “hands on” explanation for all things that are neither pre-assembled, nor keywords, but all the sigilic constructs you might find in someone else’s code, or might want to use yourself.


#17

“A list of things that Googling can’t find” sounds like a great idea for documentation.


#18

Here is my first draft.

  • I tried to find a balance excluding “obvious” sigils (better name welcome), and including overloaded, Rust specific, or ones that might have a few things to say about them.
  • Some should maybe split or merged. For this draft I tried to present them with minimal context to make them recognizable. I assume people will get that, say, !Send is the same as !Sync, and ! in this context is the free-standing symbol.

Feedback welcome!


#19
  • &x – the reference is to a memory location (“place expression”), not just a variable binding. e.g. &foo.bar.
  • 'a – a lifetime parameter
  • -> R – Here, R is the return type (but if you only say "-> designates the return type that is unclear.)
  • => – If you want to explain => well, then I think the form pattern => expression is clearer.
  • |x| on its own is not a closure, you need the expression to the right of it.
  • v? also works for Option<T>.
  • m! should include the parenthesis after it, e.g. m!(...)
  • _ can also be a type, i.e. “type placeholder / infer the type”
  • a::<T> – if you are not familiar with “turbofish” then this is not very meaning full. I’d call this “applying a type to a type parameter of a function” instead.
  • () – also a unit expression and pattern.

#20

*x - dereference of a reference

This can also be the access of a raw pointer, depending on the type of x