Как уже выше сказали - cargo не увидел, что src поменялись и не стал собирать заново.
Определяет он, что файл изменился по дате изменения файла. Если дата изменения файла меньше либо равна дате компиляции, то повторной компиляции не будет.
Чтобы обновить дату изменения файла - достаточно сделать touch main.rs после первой сборки.
В docker появился docker init, который сам делает корректный dockerfile - так что в принципе уже и не нужно руками dockerfile писать.
Генерирует он примерно такое для компиляции (зачем так - описано в комментариях):
ARG RUST_VERSION=1.71.0
ARG APP_NAME=project
FROM rust:${RUST_VERSION}-slim-bullseye AS build
ARG APP_NAME
WORKDIR /app
# Build the application.
# Leverage a cache mount to /usr/local/cargo/registry/
# for downloaded dependencies and a cache mount to /app/target/ for
# compiled dependencies which will speed up subsequent builds.
# Leverage a bind mount to the src directory to avoid having to copy the
# source code into the container. Once built, copy the executable to an
# output directory before the cache mounted /app/target is unmounted.
RUN --mount=type=bind,source=src,target=src \
--mount=type=bind,source=Cargo.toml,target=Cargo.toml \
--mount=type=bind,source=Cargo.lock,target=Cargo.lock \
--mount=type=cache,target=/app/target/ \
--mount=type=cache,target=/usr/local/cargo/registry/ \
<<EOF
set -e
cargo build --locked --release
cp ./target/release/$APP_NAME /bin/server
EOF
Ещё можно попробовать написать так (этим пользовался я раньше):
# FROM, Установка rust и прочих зависимостей
# ...
RUN cargo new /app
COPY app/Cargo.toml /app/
# This step compiles only our dependencies and saves them in a layer. This is the most impactful time savings
# Note the use of --mount=type=cache. On subsequent runs, we'll have the crates already downloaded
WORKDIR /app
RUN --mount=type=cache,target=/usr/local/cargo/registry cargo build --release
COPY ./app /app
# touch нужен, чтобы изменить дату изменения файла
RUN --mount=type=cache,target=/usr/local/cargo/registry \
set -e; \
touch /app/src/main.rs; \
cargo build --release;
Оба варианта позволяют активно использовать фичи кэширования в Docker, чтобы не выкачивать все зависимости и не перекомпилировать их при каждой сборке образа.