RFC: rust_toolchains submodule
diff --git a/MODULE.bazel b/MODULE.bazel
index 3dae74c..7f4d992 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -12,11 +12,18 @@
 bazel_dep(name = "bazel_features", version = "1.32.0")
 bazel_dep(name = "bazel_skylib", version = "1.8.2")
 bazel_dep(name = "platforms", version = "1.0.0")
+bazel_dep(name = "rust_toolchains", version = "0.68.1")
 bazel_dep(name = "rules_cc", version = "0.2.4")
 bazel_dep(name = "rules_license", version = "1.0.0")
 bazel_dep(name = "rules_shell", version = "0.6.1")
 bazel_dep(name = "apple_support", version = "1.24.1", repo_name = "build_bazel_apple_support")
 
+bazel_dep(name = "rust_toolchains", version = "0.0.1")
+local_path_override(
+    module_name = "rust_toolchains",
+    path = "rust_toolchains",
+)
+
 internal_deps = use_extension("//rust/private:internal_extensions.bzl", "i")
 use_repo(
     internal_deps,
@@ -45,10 +52,10 @@
 
 rust = use_extension("//rust:extensions.bzl", "rust")
 rust.toolchain(edition = "2021")
-use_repo(rust, "rust_toolchains")
+use_repo(rust, rules_rust_toolchains = "rust_toolchains")
 
 register_toolchains(
-    "@rust_toolchains//:all",
+    "@rules_rust_toolchains//:all",
 )
 
 rust_host_tools = use_extension("//rust:extensions.bzl", "rust_host_tools")
diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl
index ae79b7f..6726332 100644
--- a/cargo/private/cargo_build_script.bzl
+++ b/cargo/private/cargo_build_script.bzl
@@ -21,6 +21,7 @@
     "deduplicate",
     "expand_dict_value_locations",
     "find_cc_toolchain",
+    "find_optional_toolchain",
     "find_toolchain",
     _name_to_crate_name = "name_to_crate_name",
 )
@@ -315,14 +316,15 @@
     """
     script = ctx.executable.script
     script_info = ctx.attr.script[CargoBuildScriptRunfilesInfo]
-    toolchain = find_toolchain(ctx)
+    rustc_toolchain = find_toolchain(ctx)
+    cargo_toolchain = find_optional_toolchain(ctx, "@rust_toolchains//cargo:toolchain_type") or rustc_toolchain
     out_dir = ctx.actions.declare_directory(ctx.label.name + ".out_dir")
     env_out = ctx.actions.declare_file(ctx.label.name + ".env")
     dep_env_out = ctx.actions.declare_file(ctx.label.name + ".depenv")
     flags_out = ctx.actions.declare_file(ctx.label.name + ".flags")
     link_flags = ctx.actions.declare_file(ctx.label.name + ".linkflags")
     link_search_paths = ctx.actions.declare_file(ctx.label.name + ".linksearchpaths")  # rustc-link-search, propagated from transitive dependencies
-    compilation_mode_opt_level = get_compilation_mode_opts(ctx, toolchain).opt_level
+    compilation_mode_opt_level = get_compilation_mode_opts(ctx, rustc_toolchain).opt_level
 
     script_tools = []
     script_data = []
@@ -364,7 +366,7 @@
     if pkg_name == "":
         pkg_name = name_to_pkg_name(ctx.label.name)
 
-    toolchain_tools = [toolchain.all_files]
+    toolchain_tools = [rustc_toolchain.all_files]
 
     env = {}
 
@@ -380,19 +382,19 @@
     if use_default_shell_env:
         env.update(ctx.configuration.default_shell_env)
 
-    if toolchain.cargo:
-        env["CARGO"] = "${pwd}/%s" % toolchain.cargo.path
+    if cargo_toolchain.cargo:
+        env["CARGO"] = "${pwd}/%s" % cargo_toolchain.cargo.path
 
     env.update({
         "CARGO_CRATE_NAME": name_to_crate_name(pkg_name),
         "CARGO_MANIFEST_DIR": manifest_dir,
         "CARGO_PKG_NAME": pkg_name,
-        "HOST": toolchain.exec_triple.str,
+        "HOST": rustc_toolchain.exec_triple.str,
         "NUM_JOBS": "1",
         "OPT_LEVEL": compilation_mode_opt_level,
-        "RUSTC": toolchain.rustc.path,
-        "RUSTDOC": toolchain.rust_doc.path,
-        "TARGET": toolchain.target_flag_value,
+        "RUSTC": rustc_toolchain.rustc.path,
+        "RUSTDOC": rustc_toolchain.rust_doc.path,
+        "TARGET": rustc_toolchain.target_flag_value,
         # OUT_DIR is set by the runner itself, rather than on the action.
     })
 
@@ -414,7 +416,7 @@
     # Pull in env vars which may be required for the cc_toolchain to work (e.g. on OSX, the SDK version).
     # We hope that the linker env is sufficient for the whole cc_toolchain.
     cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
-    linker, _, link_args, linker_env = get_linker_and_args(ctx, "bin", toolchain, cc_toolchain, feature_configuration, None)
+    linker, _, link_args, linker_env = get_linker_and_args(ctx, "bin", rustc_toolchain, cc_toolchain, feature_configuration, None)
     env.update(**linker_env)
     env["LD"] = linker
     env["LDFLAGS"] = " ".join(_pwd_flags(link_args))
@@ -477,7 +479,7 @@
     # https://github.com/rust-lang/cargo/issues/9600
     env["CARGO_ENCODED_RUSTFLAGS"] = "\\x1f".join([
         # Allow build scripts to locate the generated sysroot
-        "--sysroot=${{pwd}}/{}".format(toolchain.sysroot),
+        "--sysroot=${{pwd}}/{}".format(rustc_toolchain.sysroot),
     ] + ctx.attr.rustc_flags)
 
     for f in ctx.attr.crate_features:
@@ -488,7 +490,7 @@
         env["CARGO_MANIFEST_LINKS"] = links
 
     # Add environment variables from the Rust toolchain.
-    env.update(toolchain.env)
+    env.update(rustc_toolchain.env)
 
     known_variables = {}
 
@@ -526,7 +528,7 @@
         direct = [
             script,
             ctx.executable._cargo_build_script_runner,
-        ] + fallback_tools + ([toolchain.target_json] if toolchain.target_json else []),
+        ] + fallback_tools + ([rustc_toolchain.target_json] if rustc_toolchain.target_json else []),
         transitive = script_data + script_tools + toolchain_tools,
     )
 
@@ -772,7 +774,8 @@
     },
     fragments = ["cpp"],
     toolchains = [
-        str(Label("//rust:toolchain_type")),
+        config_common.toolchain_type("@rust_toolchains//cargo:toolchain_type", mandatory = False),
+        str(Label("@rust_toolchains//rustc:toolchain_type")),
         config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False),
     ],
 )
diff --git a/rust/BUILD.bazel b/rust/BUILD.bazel
index f4edbd8..1103998 100644
--- a/rust/BUILD.bazel
+++ b/rust/BUILD.bazel
@@ -9,14 +9,17 @@
     "toolchain.bzl",
 ])
 
-toolchain_type(
+alias(
     name = "toolchain_type",
+    actual = "@rust_toolchains//rustc:toolchain_type",
+    deprecation = "instead use `@rust_toolchains//rustc:toolchain_type`",
+    tags = ["manual"],
 )
 
 alias(
     name = "toolchain",
     actual = "toolchain_type",
-    deprecation = "instead use `@rules_rust//rust:toolchain_type`",
+    deprecation = "instead use `@rust_toolchains//rustc:toolchain_type`",
     tags = ["manual"],
 )
 
diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl
index 318c05a..efb67ca 100644
--- a/rust/private/clippy.bzl
+++ b/rust/private/clippy.bzl
@@ -34,6 +34,7 @@
     "//rust/private:utils.bzl",
     "determine_output_hash",
     "find_cc_toolchain",
+    "find_optional_toolchain",
     "find_toolchain",
 )
 load("//rust/settings:incompatible.bzl", "IncompatibleFlagInfo")
@@ -120,7 +121,16 @@
     Returns:
         None
     """
-    toolchain = find_toolchain(ctx)
+    rustc_toolchain = find_toolchain(ctx)
+    rustc_toolchain_type = "@rust_toolchains//rustc:toolchain_type"
+
+    clippy_toolchain = find_optional_toolchain(ctx, "@rust_toolchains//clippy:toolchain_type")
+    if clippy_toolchain:
+        clippy_toolchain_type = "@rust_toolchains//clippy:toolchain_type"
+    else:
+        clippy_toolchain_type = rustc_toolchain_type
+        clippy_toolchain = rustc_toolchain
+
     cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
 
     dep_info, build_info, _ = collect_deps(
@@ -146,7 +156,7 @@
         ctx.rule.files,
         # Clippy doesn't need to invoke transitive linking, therefore doesn't need linkstamps.
         depset([]),
-        toolchain,
+        rustc_toolchain,
         cc_toolchain,
         feature_configuration,
         crate_info,
@@ -164,7 +174,7 @@
         ctx = ctx,
         attr = ctx.rule.attr,
         file = ctx.file,
-        toolchain = toolchain,
+        toolchain = rustc_toolchain,
         tool_path = clippy_executable.path,
         cc_toolchain = cc_toolchain,
         feature_configuration = feature_configuration,
@@ -232,7 +242,7 @@
         arguments = args.all,
         mnemonic = "Clippy",
         progress_message = "Clippy %{label}",
-        toolchain = "@rules_rust//rust:toolchain_type",
+        toolchain = clippy_toolchain_type,
     )
 
 def _clippy_aspect_impl(target, ctx):
@@ -246,7 +256,8 @@
     if not crate_info:
         return [ClippyInfo(output = depset([]))]
 
-    toolchain = find_toolchain(ctx)
+    clippy_toolchain = find_optional_toolchain(ctx, "@rust_toolchains//clippy:toolchain_type") or find_toolchain(ctx)
+    toolchain = clippy_toolchain or find_toolchain(ctx)
 
     # For remote execution purposes, the clippy_out file must be a sibling of crate_info.output
     # or rustc may fail to create intermediate output files because the directory does not exist.
@@ -359,7 +370,8 @@
         [rust_common.test_crate_info],
     ],
     toolchains = [
-        str(Label("//rust:toolchain_type")),
+        config_common.toolchain_type("@rust_toolchains//clippy:toolchain_type", mandatory = False),
+        str(Label("@rust_toolchains//rustc:toolchain_type")),
         config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False),
     ],
     implementation = _clippy_aspect_impl,
diff --git a/rust/private/toolchain_utils.bzl b/rust/private/toolchain_utils.bzl
index 078e6f3..c7f653e 100644
--- a/rust/private/toolchain_utils.bzl
+++ b/rust/private/toolchain_utils.bzl
@@ -1,7 +1,21 @@
-"""A module defining toolchain utilities"""
+"""A module defining toolchain utilities."""
+
+def _find_rustc_toolchain(ctx):
+    return ctx.toolchains[str(Label("@rust_toolchains//rustc:toolchain_type"))]
 
 def _toolchain_files_impl(ctx):
-    toolchain = ctx.toolchains[str(Label("//rust:toolchain_type"))]
+    rustc_toolchain = _find_rustc_toolchain(ctx)
+    clippy_toolchain_label = str(Label("@rust_toolchains//clippy:toolchain_type"))
+    clippy_toolchain = ctx.toolchains[clippy_toolchain_label] if clippy_toolchain_label in ctx.toolchains else None
+    cargo_toolchain_label = str(Label("@rust_toolchains//cargo:toolchain_type"))
+    cargo_toolchain = ctx.toolchains[cargo_toolchain_label] if cargo_toolchain_label in ctx.toolchains else None
+
+    if ctx.attr.tool == "clippy" or ctx.attr.tool == "cargo-clippy":
+        toolchain = clippy_toolchain or rustc_toolchain
+    elif ctx.attr.tool == "cargo":
+        toolchain = cargo_toolchain or rustc_toolchain
+    else:
+        toolchain = rustc_toolchain
 
     runfiles = None
     if ctx.attr.tool == "cargo":
@@ -84,12 +98,14 @@
         ),
     },
     toolchains = [
-        str(Label("//rust:toolchain_type")),
+        str(Label("@rust_toolchains//rustc:toolchain_type")),
+        config_common.toolchain_type("@rust_toolchains//clippy:toolchain_type", mandatory = False),
+        config_common.toolchain_type("@rust_toolchains//cargo:toolchain_type", mandatory = False),
     ],
 )
 
 def _current_rust_toolchain_impl(ctx):
-    toolchain = ctx.toolchains[str(Label("@rules_rust//rust:toolchain_type"))]
+    toolchain = _find_rustc_toolchain(ctx)
 
     return [
         toolchain,
@@ -103,7 +119,7 @@
     doc = "A rule for exposing the current registered `rust_toolchain`.",
     implementation = _current_rust_toolchain_impl,
     toolchains = [
-        str(Label("@rules_rust//rust:toolchain_type")),
+        str(Label("@rust_toolchains//rustc:toolchain_type")),
     ],
 )
 
diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl
index 8a82126..f92aa3b 100644
--- a/rust/private/utils.bzl
+++ b/rust/private/utils.bzl
@@ -60,15 +60,42 @@
     return env_vars
 
 def find_toolchain(ctx):
-    """Finds the first rust toolchain that is configured.
+    """Finds the first rustc toolchain that is configured.
 
     Args:
         ctx (ctx): The ctx object for the current target.
 
     Returns:
-        rust_toolchain: A Rust toolchain context.
+        rust_toolchain: A Rustc toolchain context.
     """
-    return ctx.toolchains[Label("//rust:toolchain_type")]
+    rustc_toolchain_type = str(Label("@rust_toolchains//rustc:toolchain_type"))
+    if rustc_toolchain_type in ctx.toolchains:
+        return ctx.toolchains[rustc_toolchain_type]
+
+    # Keep compatibility for rules that still request the legacy type label.
+    legacy_toolchain_type = str(Label("//rust:toolchain_type"))
+    if legacy_toolchain_type in ctx.toolchains:
+        return ctx.toolchains[legacy_toolchain_type]
+
+    fail(
+        "No Rust toolchain configured. Expected @rust_toolchains//rustc:toolchain_type " +
+        "or //rust:toolchain_type.",
+    )
+
+def find_optional_toolchain(ctx, toolchain_type):
+    """Finds the configured toolchain for a given type if it exists.
+
+    Args:
+        ctx (ctx): The ctx object for the current target.
+        toolchain_type (str): The toolchain type label.
+
+    Returns:
+        rust_toolchain or None: The toolchain context or None if unavailable.
+    """
+    toolchain_label = str(Label(toolchain_type))
+    if toolchain_label in ctx.toolchains:
+        return ctx.toolchains[toolchain_label]
+    return None
 
 # A global kill switch to test without a cc toolchain present.
 _FORCE_DISABLE_CC_TOOLCHAIN = False
diff --git a/rust/rust_analyzer/BUILD.bazel b/rust/rust_analyzer/BUILD.bazel
index 04e6c60..e84944f 100644
--- a/rust/rust_analyzer/BUILD.bazel
+++ b/rust/rust_analyzer/BUILD.bazel
@@ -1,5 +1,8 @@
 package(default_visibility = ["//visibility:public"])
 
-toolchain_type(
+alias(
     name = "toolchain_type",
+    actual = "@rust_toolchains//rust_analyzer:toolchain_type",
+    deprecation = "instead use `@rust_toolchains//rust_analyzer:toolchain_type`",
+    tags = ["manual"],
 )
diff --git a/rust/rustfmt/BUILD.bazel b/rust/rustfmt/BUILD.bazel
index 04e6c60..122b72e 100644
--- a/rust/rustfmt/BUILD.bazel
+++ b/rust/rustfmt/BUILD.bazel
@@ -1,5 +1,8 @@
 package(default_visibility = ["//visibility:public"])
 
-toolchain_type(
+alias(
     name = "toolchain_type",
+    actual = "@rust_toolchains//rustfmt:toolchain_type",
+    deprecation = "instead use `@rust_toolchains//rustfmt:toolchain_type`",
+    tags = ["manual"],
 )
diff --git a/rust_toolchains/BUILD.bazel b/rust_toolchains/BUILD.bazel
new file mode 100644
index 0000000..ffd0fb0
--- /dev/null
+++ b/rust_toolchains/BUILD.bazel
@@ -0,0 +1 @@
+package(default_visibility = ["//visibility:public"])
diff --git a/rust_toolchains/MODULE.bazel b/rust_toolchains/MODULE.bazel
new file mode 100644
index 0000000..9078118
--- /dev/null
+++ b/rust_toolchains/MODULE.bazel
@@ -0,0 +1,4 @@
+module(
+    name = "rust_toolchains",
+    version = "0.0.1",
+)
diff --git a/rust_toolchains/cargo/BUILD.bazel b/rust_toolchains/cargo/BUILD.bazel
new file mode 100644
index 0000000..ad29cb3
--- /dev/null
+++ b/rust_toolchains/cargo/BUILD.bazel
@@ -0,0 +1,6 @@
+package(default_visibility = ["//visibility:public"])
+
+# The toolchain for the exec platform.
+toolchain_type(
+    name = "toolchain_type",
+)
diff --git a/rust_toolchains/clippy/BUILD.bazel b/rust_toolchains/clippy/BUILD.bazel
new file mode 100644
index 0000000..ad29cb3
--- /dev/null
+++ b/rust_toolchains/clippy/BUILD.bazel
@@ -0,0 +1,6 @@
+package(default_visibility = ["//visibility:public"])
+
+# The toolchain for the exec platform.
+toolchain_type(
+    name = "toolchain_type",
+)
diff --git a/rust_toolchains/rust_analyzer/BUILD.bazel b/rust_toolchains/rust_analyzer/BUILD.bazel
new file mode 100644
index 0000000..ad29cb3
--- /dev/null
+++ b/rust_toolchains/rust_analyzer/BUILD.bazel
@@ -0,0 +1,6 @@
+package(default_visibility = ["//visibility:public"])
+
+# The toolchain for the exec platform.
+toolchain_type(
+    name = "toolchain_type",
+)
diff --git a/rust_toolchains/rustc/BUILD.bazel b/rust_toolchains/rustc/BUILD.bazel
new file mode 100644
index 0000000..ad29cb3
--- /dev/null
+++ b/rust_toolchains/rustc/BUILD.bazel
@@ -0,0 +1,6 @@
+package(default_visibility = ["//visibility:public"])
+
+# The toolchain for the exec platform.
+toolchain_type(
+    name = "toolchain_type",
+)
diff --git a/rust_toolchains/rustfmt/BUILD.bazel b/rust_toolchains/rustfmt/BUILD.bazel
new file mode 100644
index 0000000..ad29cb3
--- /dev/null
+++ b/rust_toolchains/rustfmt/BUILD.bazel
@@ -0,0 +1,6 @@
+package(default_visibility = ["//visibility:public"])
+
+# The toolchain for the exec platform.
+toolchain_type(
+    name = "toolchain_type",
+)
diff --git a/rust_toolchains/toolchain/BUILD.bazel b/rust_toolchains/toolchain/BUILD.bazel
new file mode 100644
index 0000000..dc8b969
--- /dev/null
+++ b/rust_toolchains/toolchain/BUILD.bazel
@@ -0,0 +1,74 @@
+load(
+    "@rules_rust//rust:toolchain.bzl",
+    "current_rust_analyzer_toolchain",
+    "current_rustfmt_toolchain",
+)
+load(
+    "@rules_rust//rust/private:toolchain_utils.bzl",
+    "current_rust_toolchain",
+    "toolchain_files",
+    "toolchain_files_for_target",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+toolchain_files(
+    name = "current_cargo_files",
+    tool = "cargo",
+)
+
+toolchain_files(
+    name = "current_clippy_files",
+    tool = "clippy",
+)
+
+toolchain_files(
+    name = "current_cargo_clippy_files",
+    tool = "cargo-clippy",
+)
+
+toolchain_files(
+    name = "current_rustc_files",
+    tool = "rustc",
+)
+
+toolchain_files(
+    name = "current_rustdoc_files",
+    tool = "rustdoc",
+)
+
+toolchain_files(
+    name = "current_rustc_lib_files",
+    tool = "rustc_lib",
+)
+
+toolchain_files(
+    name = "current_rust_stdlib_files",
+    tool = "rust_stdlib",
+)
+
+current_rust_toolchain(
+    name = "current_rust_toolchain",
+)
+
+current_rustfmt_toolchain(
+    name = "current_rustfmt_toolchain",
+)
+
+current_rust_analyzer_toolchain(
+    name = "current_rust_analyzer_toolchain",
+    tags = ["manual"],
+)
+
+toolchain_files_for_target(
+    name = "current_rustfmt_toolchain_for_target",
+    toolchain_files = ":current_rustfmt_toolchain",
+    visibility = ["//:__subpackages__"],
+)
+
+alias(
+    name = "current_rustfmt_files",
+    actual = ":current_rustfmt_toolchain",
+    deprecation = "instead use `@rules_rust//rust/toolchain:current_rustfmt_toolchain`",
+    tags = ["manual"],
+)