Crabrix blog

Rust 1.100: what changes for 32-bit Windows builds

Prepare for the announced Rust 1.100 i686 Windows host-tool changes. Separate build hosts, target libraries, linking and real 32-bit execution tests.

The announced change is about where Rust tools run

On 2 October 2026, the Rust Compiler Team announced a change scheduled for Rust 1.100.0: i686-pc-windows-msvc will move from Tier 1 with host tools to Tier 1 without host tools, and i686-pc-windows-gnu from Tier 2 with host tools to Tier 2 without host tools. Distributed standard libraries remain available for both targets. This 22 October guide discusses preparation for that announced transition, not a claim that Rust 1.100 is already the current stable release.

The central practical consequence is that producing a 32-bit Windows application and running the compiler on a 32-bit Windows host become separate support questions. Projects can continue targeting those application platforms by cross-compiling from a supported host. The announcement does not remove all 32-bit Rust targets or say that every existing 32-bit application must immediately become a 64-bit application.

A large ivory 64-bit workbench produces a compact jade application artifact that travels to a smaller separate receiver.
The machine running the compiler and the machine running the produced application are different roles.

Sources: Compiler Team: demoting i686 Windows targets to std-only, 2 October 2026

Read host tools and target support as different promises

Host tools are programs such as the compiler and Cargo that execute during development. A target standard library supports compiling a program for a particular output platform. A support tier describes additional availability and testing commitments, and the words “with host tools” add another dimension. Removing that qualifier is materially different from removing the target itself.

Use the platform-support documentation for the compiler release you are evaluating. A current stable table can still describe the pre-transition situation while an announcement describes a future release. Keep the release number attached to your notes so that a colleague reading them later can distinguish an upcoming policy from the behavior of the compiler installed on a particular workstation today.

Sources: rustc Book: platform support and tiers

Inventory the build host before changing the application

Start by identifying where release builds actually run. Is the compiler process itself 32-bit? Is a 64-bit Windows machine using a 32-bit toolchain? Does a packaging script invoke a tool from an old installation path? These are inventory questions, not reasons to rewrite the application's data model before understanding the deployment path.

Record the host reported by rustc -Vv, the active toolchain, the intended output target, and the linker prerequisites. Also identify the machine or environment used for execution testing. A team may discover that it already builds 32-bit outputs on a supported 64-bit host, making the transition mostly a matter of documenting and verifying its existing arrangement rather than replacing the product.

rustc -Vv
rustup show active-toolchain
rustup target list --installed
cargo -V

A target standard library is not a complete native SDK

Adding a Rust target installs its Rust components; it does not automatically establish every native prerequisite needed to link a Windows executable. The selected target also implies an ABI and native toolchain expectations. An MSVC target and a GNU target are not interchangeable spellings for the same complete environment, especially when the application links native libraries.

Before writing a migration command into release instructions, verify the corresponding linker and SDK setup on the supported host. Keep this prerequisite visible next to the Rust command. A failure to find the appropriate linker should produce a concrete environment diagnosis, rather than leading someone to switch target triples randomly until a build seems to pass with a different ABI than the product expects.

The transition diagram keeps the two target families separate

The diagram summarizes the announcement's target-specific tier changes while showing a supported host producing a 32-bit application. Its arrows represent a build relationship, not a guarantee that any host can link any target without configuration. The native toolchain and execution environment still need to match the selected output.

For the MSVC target, the announced Tier 1 status continues without distributed host tools; the GNU target retains Tier 2 status without those tools. Keep those labels separate in support documentation. Saying simply “Windows was demoted” discards the distinction users need to understand which guarantee changed and which application build remains possible.

An upcoming Rust 1.100 diagram separates a supported host running rustc and Cargo from i686 Windows targets retaining standard libraries without distributed host tools.
Announced for Rust 1.100: target standard libraries remain. A supported build host and the correct native linking setup are still required.

Make the output target explicit in the build command

On a suitably configured supported Windows host, the commands below illustrate selecting the MSVC output target. They are a workflow example, not evidence that this article's macOS verification environment linked or ran a Windows binary. Use the equivalent target only after choosing the ABI your application and native dependencies actually support.

Keep the target explicit in repeatable release commands instead of relying on a developer's implicit default. The resulting artifacts live in a target-specific directory, which helps packaging scripts avoid picking up an older native-host build by mistake. Verify that directory as part of packaging; a successful command is not enough if the installer later copies a different executable from a familiar location.

rustup target add i686-pc-windows-msvc
cargo build --release --target i686-pc-windows-msvc

Separate compilation, linking, packaging and execution

A source file can pass type checking and still fail at native linking. A linked program can still fail to start because a required runtime component is missing. A program that starts on a development machine can still be packaged incorrectly. Treat these as separate stages with evidence appropriate to each one, rather than interpreting a green compile as a universal compatibility result.

For an existing Windows product, preserve at least one realistic 32-bit execution check through the transition. Exercise the installed application, not only the loose executable in a build directory. File access, native library loading, configuration discovery, and update behavior often depend on packaging details that a small console demonstration cannot cover.

Build-time programs still execute on the host

A project may compile more than its final application. Build scripts and procedural macros participate in the build as host-side programs, while target code is produced for the selected application platform. Native tools launched by a script also need to be available where the build runs. Confusing these roles can produce failures that look like application incompatibility even when the target source itself is fine.

Audit any build-time platform detection during the migration. A script that assumes its own architecture describes the output program may generate the wrong constants or locate an incompatible native library. Keep host discovery and target discovery separate, and test the ordinary build sequence after moving the host environment. The related build-script guide develops that boundary with a complete generated-source example.

Pointer-sized integers are a portability decision

The width of usize follows the compilation target. That is useful for indexing in-memory collections, but it is a poor accidental choice for a persistent file format that must move between 32-bit and 64-bit applications. A host migration is a good time to identify whether an application has quietly serialized native sizes or assumed a pointer fits in a particular fixed-width integer.

Do not fix such problems by scattering unchecked casts through the code. Decide what the stored value means, choose a defined representation, and validate conversion at the boundary where the value becomes an in-memory size. That reasoning remains valuable even if the application continues to ship only one architecture today, because files and protocols often outlive the machine that first produced them.

A fixed-width record that does not depend on the host

This complete example encodes two u32 fields into an eight-byte little-endian record. The format is defined by the field widths and byte order, not by the size of a native struct or pointer. The decoder rejects an input with the wrong length and the assertions check both the known byte representation and round-trip behavior.

It is intentionally a small format, not a general serialization library. Adding fields would require a versioning and compatibility policy, and treating the count as an allocation size would require another checked conversion and resource limit. The example demonstrates one useful architectural habit: make the external representation explicit so moving the compiler host does not silently redefine a document's bytes.

#[derive(Debug, PartialEq, Eq)]
struct Record { count: u32, flags: u32 }

fn encode(record: &Record) -> [u8; 8] {
    let mut bytes = [0; 8];
    bytes[..4].copy_from_slice(&record.count.to_le_bytes());
    bytes[4..].copy_from_slice(&record.flags.to_le_bytes());
    bytes
}

fn decode(bytes: &[u8]) -> Option<Record> {
    let bytes: &[u8; 8] = bytes.try_into().ok()?;
    Some(Record {
        count: u32::from_le_bytes(bytes[..4].try_into().ok()?),
        flags: u32::from_le_bytes(bytes[4..].try_into().ok()?),
    })
}

fn main() {
    let record = Record { count: 513, flags: 7 };
    let bytes = encode(&record);
    assert_eq!(bytes, [1, 2, 0, 0, 7, 0, 0, 0]);
    assert_eq!(decode(&bytes), Some(record));
    assert_eq!(decode(&bytes[..7]), None);
    println!("Fixed-width eight-byte record verified");
}

Sources: Standard library: u32 byte-order conversions

Review FFI at the actual native boundary

A Rust application calling native code must match the foreign library's architecture, ABI, and data representation. A successful pure Rust build does not prove that an existing native library is appropriate for the target. Locate the source of each linked binary and record whether a matching 32-bit version is available under the product's distribution terms.

Avoid assuming that a C type has the same width merely because its Rust counterpart has a familiar name. Prefer the types and declarations appropriate to the platform interface, and keep ownership and lifetime rules documented near the wrapper. A host-tool transition is not an excuse to weaken those contracts; it is an opportunity to verify that previous assumptions were explicit rather than inherited accidentally from one machine.

Test representative data sizes without promising impossible capacity

A 32-bit output has a different address-space context from a 64-bit host. Tests performed only with tiny inputs on the host can miss allocation failures or overflow at size boundaries relevant to the target. Identify realistic upper bounds for documents, buffers, indexes, and dependency behavior, then test the application's response when a request exceeds those limits.

The correct behavior may be a clear refusal rather than an attempt to allocate until the process fails. Keep error messages tied to the operation the user requested, such as importing an oversized file, instead of exposing a confusing architecture detail without a repair path. A portable program is not one that pretends every platform has unlimited resources; it is one that handles its supported limits deliberately.

Do not let artifact names hide architecture mistakes

A packaging script that always copies target/release/app.exe can accidentally select a host build when the intended application was produced under a target-specific directory. Give release artifacts names or metadata that identify the target, and verify their origin before signing or distributing them. Preserve that identity when transferring files between build and packaging machines.

Also check native plugins, helper executables, and installer actions. The main executable can have the correct architecture while a bundled helper does not. Treat the product as the complete installed bundle. An installation test on a clean representative environment catches dependencies that were present on a developer's workstation but omitted from the distribution.

Use a migration matrix with named responsibilities

A practical matrix can list the old host, proposed host, selected Rust toolchain, application target, native linker setup, packaging method, and execution-test environment. Give each row an owner and a concrete expected result. This is more useful than a broad task called “support Rust 1.100,” because it reveals which change actually requires work.

For a small project, the matrix may contain only one release path. For a product shipping both MSVC and GNU variants, keep separate rows rather than assuming one successful build covers the other. Reuse common source tests, but preserve the target-specific native and packaging checks that make those variants distinct products from the operating system's point of view.

Keep the compiler upgrade separate from source modernization

When evaluating the announced transition, first reproduce the current product with its existing compiler and target on the proposed supported host. Then evaluate the newer compiler as another controlled change. Combining a host move, compiler upgrade, dependency refresh, and large source cleanup makes a failure harder to localize and can hide which action was actually required.

Once the build path is stable, modernize source in focused changes backed by relevant tests. New language features may be valuable, but they are not prerequisites merely because the project moved its build host. Preserve the oldest compiler version the project promises until that compatibility policy changes deliberately and is communicated to contributors and downstream users.

Retain old toolchains as evidence, not a permanent strategy

A known working old build can help compare output and diagnose a transition. Preserve its source revision, compiler identity, target, and packaging prerequisites so the comparison is meaningful. Keeping a mysterious workstation untouched without recording those details is fragile evidence: a disk failure or account change can erase the only explanation of how the product was built.

Do not treat an unsupported host installation as an indefinite maintenance plan. Identify a supported build path and test it while there is time to resolve native dependency or packaging issues. If a temporary delay is necessary, give it a specific owner and exit condition. The objective is a repeatable product build rather than a collection of machines nobody dares to change.

Explain the change accurately to application users

Most application users care whether their supported installation continues to work, not where the compiler ran. Release communication should describe any actual product compatibility change separately from this upstream tooling announcement. If your application still supports its existing 32-bit users after changing build hosts, say that clearly without implying a platform removal that did not occur.

If testing reveals that a particular native dependency or operating-system version is no longer supported by your own product, document that as a product decision with its own reason. Do not attribute every local limitation to the Rust announcement. Accurate communication helps users distinguish an upstream tooling policy from the support commitments your application actually makes.

Crabrix and WASI are a different target boundary

Crabrix's bundled local execution environment is not a Windows cross-linker or a replacement for a Windows SDK. Sharing a Rust source file with the app does not establish that its native Windows integration can run there. Keep the portable calculation core separate from platform adapters so that useful logic can be explored across environments without confusing their capabilities.

The fixed-width record program is an example of such a portable core. It needs no Windows API to establish its byte format. Testing that core locally is valuable, while verifying native linking and installed Windows behavior remains work for the corresponding desktop environment. Each check contributes evidence at its own boundary rather than standing in for all the others.

A short example of a successful transition plan

Consider a utility whose source is portable Rust but whose installer includes a native compression library. Its team can first move the compiler to a supported 64-bit Windows host, install the matching 32-bit linker prerequisites, and build the existing i686 target. It then verifies the compression library's architecture, packages the target-specific executable, and runs installation and file-processing checks in the supported user environment.

Only after that path is repeatable does the team evaluate the announced compiler release. If a test fails, the evidence distinguishes host setup, native library selection, packaging, and source behavior. The plan is modest, but it avoids both unnecessary application rewrites and a false sense of safety from a single successful type-checking command.

Prepare for the precise change that was announced

The useful response is to separate roles: supported build host, chosen compiler, intended application target, compatible native linker, and realistic execution tests. Record each one, then migrate the pieces that actually need to change. There is no need to infer that every 32-bit Rust application has lost its target or that every Windows source project must be redesigned.

Recheck the release-specific platform documentation when Rust 1.100 becomes the version you intend to use. Announcements describe a planned transition; a maintained product needs evidence from its actual toolchain and distribution path. That combination of precise upstream reading and concrete local verification is what turns a potentially alarming headline into an understandable engineering task.

Explore the free Crabrix lessons →