rust.md

  1# Rust
  2
  3Rust support is available natively in Zed.
  4
  5- Tree-sitter: [tree-sitter/tree-sitter-rust](https://github.com/tree-sitter/tree-sitter-rust)
  6- Language Server: [rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer)
  7
  8<!--
  9TBD: Polish Rust Docs. Zed is a good rust editor, good Rust docs make it look like we care about Rust (we do!)
 10TBD: Users may not know what inlayHints, don't start there.
 11TBD: Provide explicit examples not just `....`
 12-->
 13
 14## Inlay Hints
 15
 16The following configuration can be used to change the inlay hint settings for `rust-analyzer` in Rust:
 17
 18```json
 19{
 20  "lsp": {
 21    "rust-analyzer": {
 22      "initialization_options": {
 23        "inlayHints": {
 24          "maxLength": null,
 25          "lifetimeElisionHints": {
 26            "enable": "skip_trivial",
 27            "useParameterNames": true
 28          },
 29          "closureReturnTypeHints": {
 30            "enable": "always"
 31          }
 32        }
 33      }
 34    }
 35  }
 36}
 37```
 38
 39See [Inlay Hints](https://rust-analyzer.github.io/book/features.html#inlay-hints) in the Rust Analyzer Manual for more information.
 40
 41## Target directory
 42
 43The `rust-analyzer` target directory can be set in `initialization_options`:
 44
 45```json
 46{
 47  "lsp": {
 48    "rust-analyzer": {
 49      "initialization_options": {
 50        "rust": {
 51          "analyzerTargetDir": true
 52        }
 53      }
 54    }
 55  }
 56}
 57```
 58
 59A `true` setting will set the target directory to `target/rust-analyzer`. You can set a custom directory with a string like `"target/analyzer"` instead of `true`.
 60
 61## Binary
 62
 63You can configure which `rust-analyzer` binary Zed should use.
 64
 65By default, Zed will try to find a `rust-analyzer` in your `$PATH` and try to use that. If that binary successfully executes `rust-analyzer --help`, it's used. Otherwise, Zed will fall back to installing its own `rust-analyzer` version and using that.
 66
 67If you want to disable Zed looking for a `rust-analyzer` binary, you can set `ignore_system_version` to `true` in your `settings.json`:
 68
 69```json
 70{
 71  "lsp": {
 72    "rust-analyzer": {
 73      "binary": {
 74        "ignore_system_version": true
 75      }
 76    }
 77  }
 78}
 79```
 80
 81If you want to use a binary in a custom location, you can specify a `path` and optional `args`:
 82
 83```json
 84{
 85  "lsp": {
 86    "rust-analyzer": {
 87      "binary": {
 88        "path": "/Users/example/bin/rust-analyzer",
 89        "args": []
 90      }
 91    }
 92  }
 93}
 94```
 95
 96This `"path"` has to be an absolute path.
 97
 98## Alternate Targets
 99
100If want rust-analyzer to provide diagnostics for a target other than you current platform (e.g. for windows when running on macOS) you can use the following Zed lsp settings:
101
102```json
103{
104  "lsp": {
105    "rust-analyzer": {
106      "initialization_options": {
107        "cargo": {
108          "target": "x86_64-pc-windows-msvc"
109        }
110      }
111    }
112  }
113}
114```
115
116If you are using `rustup` and you can find a list of available target triples (`aarch64-apple-darwin`, `x86_64-unknown-linux-gnu`, etc) by running:
117
118```sh
119rustup target list --installed
120```
121
122## More server configuration
123
124<!--
125TBD: Is it possible to specify RUSTFLAGS? https://github.com/zed-industries/zed/issues/14334
126-->
127
128Rust-analyzer [manual](https://rust-analyzer.github.io/book/) describes various features and configuration options for rust-analyzer language server.
129Rust-analyzer in Zed runs with the default parameters.
130
131### Large projects and performance
132
133One of the main caveats that might cause extensive resource usage on large projects, is the combination of the following features:
134
135```
136rust-analyzer.checkOnSave (default: true)
137    Run the check command for diagnostics on save.
138```
139
140```
141rust-analyzer.check.workspace (default: true)
142    Whether --workspace should be passed to cargo check. If false, -p <package> will be passed instead.
143```
144
145```
146rust-analyzer.cargo.allTargets (default: true)
147    Pass --all-targets to cargo invocation
148```
149
150Which would mean that every time Zed saves, a `cargo check --workspace --all-targets` command is run, checking the entire project (workspace), lib, doc, test, bin, bench and [other targets](https://doc.rust-lang.org/cargo/reference/cargo-targets.html).
151
152While that works fine on small projects, it does not scale well.
153
154The alternatives would be to use [tasks](../tasks.md), as Zed already provides a `cargo check --workspace --all-targets` task and the ability to cmd/ctrl-click on the terminal output to navigate to the error, and limit or turn off the check on save feature entirely.
155
156Check on save feature is responsible for returning part of the diagnostics based on cargo check output, so turning it off will limit rust-analyzer with its own [diagnostics](https://rust-analyzer.github.io/book/diagnostics.html).
157
158Consider more `rust-analyzer.cargo.` and `rust-analyzer.check.` and `rust-analyzer.diagnostics.` settings from the manual for more fine-grained configuration.
159Here's a snippet for Zed settings.json (the language server will restart automatically after the `lsp.rust-analyzer` section is edited and saved):
160
161```json
162{
163  "lsp": {
164    "rust-analyzer": {
165      "initialization_options": {
166        // get more cargo-less diagnostics from rust-analyzer,
167        // which might include false-positives (those can be turned off by their names)
168        "diagnostics": {
169          "experimental": {
170            "enable": true
171          }
172        },
173        // To disable the checking entirely
174        // (ignores all cargo and check settings below)
175        "checkOnSave": false,
176        // To check the `lib` target only.
177        "cargo": {
178          "allTargets": false
179        },
180        // Use `-p` instead of `--workspace` for cargo check
181        "check": {
182          "workspace": false
183        }
184      }
185    }
186  }
187}
188```
189
190### Multi-project workspaces
191
192If you want rust-analyzer to analyze multiple Rust projects in the same folder that are not listed in `[members]` in the Cargo workspace,
193you can list them in `linkedProjects` in the local project settings:
194
195```json
196{
197  "lsp": {
198    "rust-analyzer": {
199      "initialization_options": {
200        "linkedProjects": ["./path/to/a/Cargo.toml", "./path/to/b/Cargo.toml"]
201      }
202    }
203  }
204}
205```
206
207### Snippets
208
209There's a way get custom completion items from rust-analyzer, that will transform the code according to the snippet body:
210
211```json
212{
213  "lsp": {
214    "rust-analyzer": {
215      "initialization_options": {
216        "completion": {
217          "snippets": {
218            "custom": {
219              "Arc::new": {
220                "postfix": "arc",
221                "body": ["Arc::new(${receiver})"],
222                "requires": "std::sync::Arc",
223                "scope": "expr"
224              },
225              "Some": {
226                "postfix": "some",
227                "body": ["Some(${receiver})"],
228                "scope": "expr"
229              },
230              "Ok": {
231                "postfix": "ok",
232                "body": ["Ok(${receiver})"],
233                "scope": "expr"
234              },
235              "Rc::new": {
236                "postfix": "rc",
237                "body": ["Rc::new(${receiver})"],
238                "requires": "std::rc::Rc",
239                "scope": "expr"
240              },
241              "Box::pin": {
242                "postfix": "boxpin",
243                "body": ["Box::pin(${receiver})"],
244                "requires": "std::boxed::Box",
245                "scope": "expr"
246              },
247              "vec!": {
248                "postfix": "vec",
249                "body": ["vec![${receiver}]"],
250                "description": "vec![]",
251                "scope": "expr"
252              }
253            }
254          }
255        }
256      }
257    }
258  }
259}
260```