Rust Lang Team
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Help
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- title: "Design meeting 2024-06-05: Precise capturing: `impl use<..> Trait` vs `use<..> impl Trait`" tags: ["T-lang", "design-meeting", "minutes"] date: 2024-06-05 discussion: https://rust-lang.zulipchat.com/#narrow/stream/410673-t-lang.2Fmeetings/topic/Design.20meeting.202024-06-05 url: https://hackmd.io/uvTw8ss4STSaSWs2PA1bQw --- # Introduction We've decided to add syntax for the precise capturing of generic parameters in RPIT-like `impl Trait` opaque types. This solves the problem of overcapturing and will allow us to fully stabilize the Lifetime Capture Rules 2024 for RPIT in Rust 2024. To make this work, we need to stabilize this syntax ahead of or along with the edition. To best support migration efforts, it would be better to stabilize this sooner rather than later. To do that, we need to finalize the exact syntax. Based on our earlier discussions leading to the acceptance of RFC 3617, the key choice here is between: 1. `impl use<..> Trait` 2. `use<..> impl Trait` The goal of this meeting is to pick which one of these we want. To lay my own cards on the table, as the author, having now written out the arguments every which way, I find myself deeply sympathetic to the good arguments for both choices. As discussed below, in favor of `impl use<..> Trait` is that it follows the natural ordering when applying generics as arguments to a type. In favor of `use<..> impl Trait` is an appealing kind of syntactic *principledness*, in that it keeps `use<..>` away from the bounds (which it is not a part of) in the same way that preferring `super let pat = expr` to `let super pat = expr` keeps `super` away from the pattern (which it is not a part of), and is why the former was preferred by the draft RFC for `super let`. Mostly I think we're going to be happy either way. We'll just have to pick one. Niko has been using this feature in nightly and recently reported his experience: > I thought I didn't care but within a few minutes of using it I found that `use<> impl` just felt _much_ simpler to me. The best explanation I can give is that it feels less "stacked" -- i.e., I can look at the `use` and process it as a kind of "prefix" on the `impl Trait` type, rather than being an inner part that I have to think carefully about. That combined with the different scoping relative to `for` feels like a very strong argument to me. The remainder of the document is a detailed comparative analysis of these options drawn from the relevant alternatives section from RFC 3617. # Alternatives ## Syntax ### `use<..> impl Trait` Putting the `use<..>` specifier *before* the `impl` keyword is potentially appealing as `use<..>` applies to the entire `impl Trait` opaque type rather than to just one of the bounds, and this ordering might better suggest that. Let's discuss some arguments for this, some arguments against it, and then discuss the fundamental tension here. #### The case for `use<..>` before `impl` We've been referring to the syntax for RPIT-like opaque types as `impl Trait`, as is commonly done. But this is a bit imprecise. The syntax is really `impl $bounds`. We might say, e.g.: ```rust fn foo() -> impl 'static + Unpin + for<'a> FnMut(&'a ()) { |_| () } ``` Each *bound*, separated by `+`, may be a *lifetime* or a *trait bound*. Each trait bound may include a higher ranked `for<..>` *binder*. The lifetimes introduced in such a binder are in scope only for the bound in which that binder appears. This could create confusion with `use<..>` after `impl`. If we say, e.g.: ```rust fn foo<'a>( _: &'a (), ) -> impl use<'a> for<'b> FnMut(&'b ()) + for<'c> Trait<'c> { // ^^^^^^^ ^^^^^^^ ^^^^^^^ // | | ^ Applies to one bound. // | ^ Applies to one bound. // ^ Applies to the whole type. |_| () } ``` ...then it may feel like `use<..>` should apply to only the first bound, just as the `for<..>` binder right next to it does. Putting `use<..>` *before* `impl` might avoid this issue. E.g.: ```rust fn foo<'a>( _: &'a (), ) -> use<'a> impl for<'b> FnMut(&'b ()) + for<'c> Trait<'c> { |_| () } ``` This would make it clear that `use<..>` applies to the entire type. This seems the strongest argument for putting `use<..>` before `impl`, and it's a *good* one. #### The case for and against `use<..>` before `impl` There are some other known arguments for this ordering that may or may not resonate with the reader; we'll present these, along with the standard arguments that might be made in response, as an imagined conversation between Alice and Bob: > **Bob**: We call the base feature here "`impl Trait`". Anything that we put between the `impl` and the `Trait` could make this less recognizable to people. > > **Alice**: Maybe, but users don't literally write the words `impl Trait`; they write `impl` and then a set of bounds. They could even write `impl 'static + Fn()`, e.g. The fact that there can be multiple traits and that a lifetime or a `for<..>` binder could come between the `impl` and the first trait doesn't seem to be a problem here, so maybe adding `use<..>` won't be either. > > **Bob**: But what about the orthography? In English, we might say "using 'x, we implement the trait". We'd probably try to avoid saying "we implement, using 'x, the trait". Putting `use<..>` first better lines up with this. > > **Alice**: Is that true? Would we always prefer the first version? To my ears, "using 'x, we implement the trait" sounds a bit like something Yoda would say. I'd probably say the second version, if I had to choose. Really, of course, I'd mostly try to say instead that "we implement the trait using 'x", but there are probably good reasons to not use that ordering here in Rust. > > **Bob**: The RFC talks about maybe later extending the `use<..>` syntax to closure-like blocks, e.g. `use<> |x| x`. If it makes sense to put the `use<..>` first here, shouldn't we put it first in `use<..> impl Trait`? > > **Alice**: That's interesting to think about. In the case of closure-like blocks, we'd probably want to put the `use<..>` in the same position as `move` as it could be extended to serve a similar purpose. For closures, that would mean putting it before the arguments, e.g. `use<> |x| x`, just as we do with `move`. But this would also imply that `use<..>` should appear *after* certain keywords, e.g. for `async` blocks we currently write `async move {}`, so maybe here we would write `async use<> {}`. > > **Alice**: There is a key difference to keep in mind here. Closure-like blocks are *expressions* but `impl Trait` is syntax for a *type*. We often have different conventions between type position and expression position in Rust. Maybe (or maybe not) this is a place where that distinction could matter. #### The case against `use<..>` before `impl` The `use<..>` specifier syntax *applies* the listed generic *parameters* as generic *arguments* to the opaque type. It's analogous, e.g., with the generic arguments here: ```rust impl Trait for () { type Opaque<'t, T> = Concrete<'t, T> // ^^^^^^^^ ^^^^^ // ^ Type ^ Generic arguments where Self: 'static; // ^^^^^^^^^^^^^ // ^ Bounds } ``` Just as the above *applies* `<'t, T>` to `Concrete`, `use<..>` applies its arguments to the opaque type. In the above example and throughout Rust, we observe the following order: *type*, *generic arguments* (applied to the type), *bounds*. In `impl Trait` syntax, the `impl` keyword is the stand-in for the opaque type itself. The `use<..>` specifier lists the generic arguments to be applied to that type. Then the bounds follow. Putting `use<..>` after `impl` is consistent with this rule, but the other way would be inconsistent. This observation, that we're applying generic *arguments* to the opaque type and that the `impl` keyword is the stand-in for that type, is also a strong argument in favor of `impl<..> Trait` syntax. It's conceivable that we'll later, with more experience and consistently with [Stroustrup's Rule][], decide that we want to be more concise and adopt the `impl<..> Trait` syntax after all. One of the advantages of placing `use<..>` after `impl` is that there would be less visual and conceptual churn in later making that change. Finally, there's one other practical advantage to placing `impl` before `use<..>`. If we were to do it the other way and place `use<..>` before `impl`, we would need to make a backward incompatible change to the `ty` macro matcher fragment specifier. This would require us to migrate this specifier according to our policy in [RFC 3531][]. This is something we could do, but it is a cost on us and on our users, even if only a modest one. [RFC 3531]: https://github.com/rust-lang/rfcs/blob/master/text/3531-macro-fragment-policy.md [Stroustrup's Rule]: https://www.thefeedbackloop.xyz/stroustrups-rule-and-layering-over-time/ #### The fundamental tension on `impl use<..>` vs. `use<..> impl` Throughout this RFC, we've given two intuitions for the semantics of `use<..>`: - **Intuition #1**: `use<..>` *applies* generic arguments to the opaque type. - **Intuition #2**: `use<..>` brings generic parameters *into scope* for the hidden type. These are *both* true and are both valid *intuitions*, but there's some tension between these for making this syntax choice. It's often helpful to think of `impl Trait` in terms of generic associated types (GATs), and let's make that analogy here. Consider: ```rust impl Trait for () { type Opaque<'t, T> = Concrete<'t, T>; // ^^^^^^ ^^^^^ ^^^^^^^^ ^^^^^ // | | | ^ Generic arguments applied // | | ^ Concrete type // | ^ Generic parameters introduced into scope // ^ Alias type (similar to an opaque type) fn foo<T>(&self) -> Self::Opaque<'_, T> { todo!() } // ^^^^^^^^^^^^ ^^^^^ // ^ Alias type ^ Generic arguments applied } ``` The question is, are the generics in `use<..>` more like the generic *parameters* or more like the generic *arguments* above? If these generics are more like the generic *arguments* above (*Intuition #1*), then `impl<..> Trait` and `impl use<..> Trait` make a lot of sense as we're *applying* these arguments to the type. In Rust, when we're applying generic arguments to a type, the generic arguments appear *after* the type, and `impl` is the stand-in for the type here. However, if these generics are more like the generic *parameters* above (*Intuition #2*), then `use<..> impl Trait` makes more sense. In Rust, when we're putting generic parameters into scope, they appear before the type. Since both intuitions are valid, but each argues for a different syntax choice, picking one is tough. The authors are sympathetic to both choices. --- # Discussion ## Attendance - People: TC, scottmcm, tmandry, pnkfelix, nikomatsakis, eholk, Josh, Xiang ## Meeting roles - Minutes, driver: TC ## Previous discussions The 2024-04-24 design meeting: https://hackmd.io/PohGwog9SLK-XCg0ZWTpuQ The 2024-04-03 planning meeting with the first bikeshed: https://hackmd.io/c_-Fm49QRASsH7m-0VmtZw ## Remind me where this is actually usable? scottmcm: this mentions the ty matcher, but can this really be used in every `ty` position? What would it mean to have `fn foo<...>(x: use<...> impl Trait)`, for example? Can I do `impl Foo<...> for Bar<...> { type MyType = use<...> impl Trait; }`? (I'm wondering if there's a more meaningful distinction here than just `ty_2021` vs `ty_2024`, like we have `pat_param` vs `pat`. Is there a `ty_return` vs normal `ty`, say?) eholk: I don't immediately see why we couldn't, but I'd need to look more closely at the grammar. NM: There are two questions. On is what we would parse. The other is what we accept semantically. We'd want to reject this in some places certainly. eholk: As with other places, the macro matcher would probably accept it generally, but if it expands to a place where it can't be used, that would be an error. We have some precedent with how `_` is matched by `expr`. scottmcm: There's also the precedent here of `pat_param` vs `pat`. If this can never show up in an argument and only in a return type... it makes me wonder if there's a possibility here to leave `ty` alone and add `ty_return`. tmandry: Actually, I'm wondering why this would never be useful in argument position. eholk: I like this idea of writing a `ty_return` or whatnot. There are ergonomics considerations here. There are places we could end up with really precise matchers that aren't generally useful. But on the other hand, it'd be good to have confidence that expanding something matched would be legal in the intended place of use. pnkfelix: These could also be used in type alias position. That might raise questions for that name. pnkfelix: I'm confused by how this would be used in argument position. This seems deeply tied to opaque types. NM: That's correct. I can maybe imagine future words in which it could have a meaning, but I'm not sure we'd go there. What this says in the abstract is that, "as long as this type is valid, these other types must also be valid, as far as the borrows go." But for argument generics, the types have to be valid throughout the function anyway. ```rust fn foo<T, U>(arg: impl Debug) { // becomes `fn foo<T, U, V>() where V: Debug` } fn foo<T, U>(arg: use<T> impl Debug) { // ...also becomes `fn foo<T, U, V>() where V: Debug` ...? } ``` NM: Proposal: * Parser accepts `use` everywhere; semantically it is rejected * Matcher in 2024: * ty = may start with `use` * ty_2021 = may not * Matcher in 2021 * (not possible) = may start with `use` * ty = ty_2021 = may not * Questions * Name: ty_2021 = typaram -- similar to patparam? * Edition rewrite: is it worth rewriting `ty` to `ty_2021` ## `impl use<..> + $bounds` Josh: When we considered syntax options before, did we consider: `impl use<'a, B> + Trait<C> + Other + ...` ? That could make the scope clearer, since it'd no longer look like `use` applied only to the first term. "I return something that uses these lifetimes/parameters and implements this trait and ..." TC: We did not previously discuss this. Josh: Then, if I'm understanding correctly how this is used, I'd like to propose this as being clearer. Effectively, rather than `use<'a, B>` being a thing attached specifically to the `impl` at the beginning of the construct (whether before or after it), it's one *term* in the `impl X + Y + Z` construct. (Also, ideally that term could be placed anywhere in the list.) NM: Similar to "the `Captures` hack". tmandry: This sort of works in the opposite direction from the other bounds. It's a weakening rather than a strengthening. I don't like that about it. The 2024 capture rules were trying to align the language with the correct intuition here, and this would seem to push against it. NM: * `fn foo<T, U>() -> impl PartialEq` // could capture everything (default: `use<T, U>`) * `fn foo<T, U>() -> impl use<> + PartialEq` // captures nothing * `fn foo<T, U>() -> impl use<> + PartialEq<T>` // captures `T` * `fn foo<T, U>() -> impl use<U> + PartialEq<T>` // captures `T`, `U` * `fn foo<T, U>() -> impl use<T, U> + PartialEq<T>` // captures `T`, `U` NM: `use` here acts lke any other bound -- adding `use` means fewer types meet those bounds, but you can do more with it (it "outlasts" other things) tmandry: For me, the bounds seem to restrict what you can do with a type... NM: The bounds restricts the set of types but expands what you can do with it. NM: Most bounds are about what you can call on it (e.g. the methods of a trait). There are bounds like `'static`. NM: Where I went wrong here was saying that `use<T> + use<U>` would be `use<T, U>`. That's not true. NM: If you have "at most one use", it works well. JT: Yes, because use is a "subtraction from all" in some sense. *Intuition:* `use<B...>` is a trait that is implemented for all types that reference types in `B...`. TC: While I agree with what NM is saying, perhaps we can more precisely frame the intuition that tmandry has. As a list of bounds gets longer, it offers more to the caller. But as the list of arguments to `use<..>` gets longer, it offers less to the caller. So the `use<..>` construct is *contravariant* in its length as compared with the bounds. NM: Yes, +1; it's *contravariant*. That's a great framing. NM: My preference: * Restrict to at most one `use` in the list right now and require that it includes all the identifiers from other bounds * If we ever wanted more than one: * I would interpret it as "disjoint from the complement of the identifiers in the list" * and then say that the "legal names to be included" are the union of all identifiers that appear * but I'd want to work from needs and examples NM: There are places where I want to do something similar to this, such as with `dyn`. So I think it's not bad that it scales. It may be comparable to the prefix `use<..>` in that way. scottmcm: It'd be interesting to be able to use these on GATs. NM: Yes, it's interesting to think about these in a where clause. Josh: E.g., `where T: Debug + use<U>`. NM: Yes, we'd make this part of the bounds list, it's just not a bound that could be used everywhere right now. NM: GATs and opaque types are very related. NM: We'd start with this where we need it right now, then we'd think about expanding it to e.g. where clauses on GATs. ```rust fn foo<'a, T: 'a + Foo>(t: T) -> dyn Foo + 'a { // Niko has always found this indirect and non-obvious... Box::new(t) } fn foo<T>(t: T) -> dyn Foo + use<T> { // ...I'd rather write this, though it could be `use<T> dyn Foo` just as well Box::new(t) } ``` NM: This also lets us put it at the end, which is nice. I like it at either the beginning or the end, just not where we had it at the beginning. Josh: Yes, this isn't the most important thing to know about the return type, it's an added detail. The most important thing to know is usually the primary trait implemented. "This is a Future", not "Hey, before I tell you anything else, know that this uses `'a`". NM: ```rust fn foo<A, B, C>( a: A, b: B, ) -> impl Iterator + use<A, B> { } ``` scottmcm: I've long wondered how we'd ever be able to do something like: ```rust trait Iterator = StreamingIterator<Item<'a>: use<>>; ``` as a way to say it doesn't depend on the `'a`. pnkfelix: Is there a current way to specify any kind of bounds on a closure expression directly, beyond the "return type" itself, e.g.: ```rust fn foo<T>() -> impl Fn() { // ~~~~~~~~~ this is the aforementioned "return type" /* want to impose bounds on underlying type of the closure expression below (presumably *not* by annotating the return type above) */ || { ... } } ``` NM: It's sort of not surprising that we don't, as closures as expressions. tmandry: we can still just do this... ```rust fn foo() -> impl Fn() + use<> { use<> || { .. } } ``` tmandry: If we can use this for GATs, that's a strong argument. scottmcm: If this can help us clean up `dyn`, that would be really nice. +1 to that. scottmcm: to Niko's point earlier, I agree that I'd love to rethink how we do bounds on trait objects. See "Consider re-tuning the lifetime elision rules for trait objects" <https://github.com/rust-lang/rust/issues/91302>. In particular, I think `impl dyn Foo { … }` is too often a mistake, because people want `impl dyn Foo + '_ { … }`. NM: What are the disadvantages of putting it in the bounds list rather than putting it in front? tmandry: The main one is the contravariance, using TC's framing there. NM: My hunch is that this will not cause undue confusion. If you don't think too hard about it, it's OK. TC: To pnkfelix's question earlier, note that this does work: ```rust #![feature(precise_capturing)] #![allow(incomplete_features)] //@ edition: 2024 fn outlives<'o, T: 'o>(_: T) {} fn foo<'a>(_: &'a ()) -> impl use<> Fn() { // ^^^^^ // We would get an error below without this. || () } fn main() { let x = (); outlives::<'static>(foo(&x)); } ``` NM: It seems OK for things to be a bit complicated as long as the compiler will point people in the right direction. others: Agreed. Josh: We could have rustfmt move `use<..>` to the end. scottmcm: The thing that comes to mind for me is `Fn()` where it may be clearer to not have anything after it. Otherwise, I agree that at the end is probably fine most of the time. ```rust fn foo() -> impl Fn(A) -> B + use<…> // ^^^^^^^^^^ unclear to human ``` Exact proposal: * bound lists can contain `use<Parameter..>`. * semantically, we enforce that: * `use<Parameter..>` appears at most once in the bound list. * it only appears in "opaque type" impl trait (i.e., RPIT, ATPIT, etc). * error if the bounds reference other parameters that don't appear in the `use` list (with machine-applicable suggestion to add those parameters to the use list). * Question mark: RPITIT -- either support it now in trait definitions or later (once we support in GATs) * The `use` bound defines the set of parameters to appear on the hidden type * if not listed, default is all parameters in scope * expected additions in future: * use to control capture in GATs, dyns * similar syntax for closure expressions (can't be same, not a bounds list) Pros: * Can appear at end of the list (and rustfmt can move it) * Can be used for GATs (in the future) to avoid capturing parameters from the trait * Can be used for dyns (in the future) as part of a larger change * Clearly covers hidden type in the same way as other bounds * No macro changes required Cons: * Contravariant in the length of identifiers inside the `use` which is a bit odd * but having at most one in the list (and one that covers all parameters that appear elsewhere) makes this less relevant for now; we can decide if/how to permit multiple and what it should mean *Consensus*: We're agreed on the exact proposal above. We're interested in expanding this in the future to other places, such as the where clauses of GATs and to `dyn`. (This section of the meeting ended here.) --- ## Role of the `use` keyword tmandry: The framing of bringing parameters into scope vs applying arguments is helpful. I see it as bringing parameters into scope, and part of this is the fact that we are reusing the `use` keyword. The whole purpose of `use` directives is to bring an outside name into scope, and if we used it for closure captures I would frame its role in the same way. --- # Short planning meeting Availability for planning: Dates: - 5th: Today! - 12th: tmandry out all week. - 19th: Open. - 26th: Open. Availability: * Josh: No conflicts. * Niko: June 12 -- "maybe" but otherwise available. * Tyler: Out June 12. * Scott: should be available all. Topics: * Project goals revue * x2 ? ergonomics initiative topics * https://rust-lang.github.io/rust-project-goals/2024h2/ergonomics-initiative.html * especially auto-clone * maybe together with profiles + AFIDT * Return type notation (RTN) * a note: implementable trait aliases would be nice * Match ergonomics * AsyncIterator and async trait implementation strategy (`async fn next()` vs `fn poll_next()` vs ...) (note: libs-api inclined to punt to lang here) * +1 to libs-api punting --niko * eholk would be logical person to prepare doc on this I think --niko * AFIDT * Does supertrait item shadowing https://github.com/rust-lang/rfcs/pull/3624 need a design meeting, or can we check boxes async? We need tmandry for match ergonomics. Let's schedule a special meeting: - Friday 1630 UTC / 1230 EDT / 0930 PDT

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully