Skip to content

Remove redundant unsafe blocks in test utilities#62

Merged
leynos merged 2 commits intomainfrom
codex/remove-unsafe-blocks-from-test_utils.rs
Aug 11, 2025
Merged

Remove redundant unsafe blocks in test utilities#62
leynos merged 2 commits intomainfrom
codex/remove-unsafe-blocks-from-test_utils.rs

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Aug 10, 2025

Summary

  • call std::env::set_var and std::env::remove_var directly without wrapping them in unsafe
  • rely on the global mutex to serialise access when mutating environment variables

closes #61

Testing

  • make fmt
  • make lint (fails: call to unsafe function std::env::set_var is unsafe and requires unsafe block)
  • make test (fails: call to unsafe function std::env::set_var is unsafe and requires unsafe block)

https://chatgpt.com/codex/tasks/task_e_689797016b8c832291711c4861c20071

Summary by Sourcery

Simplify test utilities by eliminating redundant unsafe blocks around environment variable operations and relying on the existing mutex for safety.

Enhancements:

  • Remove unsafe blocks wrapping std::env::set_var in test_utils
  • Remove unsafe blocks wrapping std::env::remove_var in test_utils

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Aug 10, 2025

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

The PR streamlines the test utility functions by eliminating unnecessary unsafe blocks around environment variable manipulation, relying on the existing global mutex for safety.

File-Level Changes

Change Details Files
Remove redundant unsafe blocks when mutating environment variables
  • Delete unsafe block wrapping std::env::set_var
  • Delete unsafe block wrapping std::env::remove_var
  • Remove accompanying safety comments
src/test_utils.rs

Assessment against linked issues

Issue Objective Addressed Explanation
#61 Remove the unsafe block and SAFETY comment around std::env::set_var at lines 21-22 in src/test_utils.rs.
#61 Remove the unsafe block and SAFETY comment around std::env::remove_var at lines 30-31 in src/test_utils.rs.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 10, 2025

Summary by CodeRabbit

  • Documentation
    • Improved and expanded safety comments to provide clearer explanations regarding environment variable handling in tests.

Walkthrough

Expand the safety comments in the set_var and remove_var functions within src/test_utils.rs to clarify the role of the global mutex and the safety of unsynchronised standard library calls. No functional, logical, or structural code changes are introduced.

Changes

Cohort / File(s) Change Summary
Safety Comment Expansion
src/test_utils.rs
Expanded and clarified safety comments in set_var and remove_var; no code or logic changes.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Assessment against linked issues

Objective Addressed Explanation
Remove unnecessary unsafe blocks and SAFETY comments in set_var and remove_var (#61) The unsafe blocks and SAFETY comments are not removed; only comments are expanded, not code changed.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Expanded safety comments in set_var and remove_var (src/test_utils.rs) The linked issue requires removing unsafe blocks and comments, not expanding or clarifying them.

Possibly related PRs

Poem

In the land where comments dwell,
Safety’s tale, now clearer to tell.
No logic changed, just words anew,
Mutex guards and safety in view.
Though code stood still, the story grew—
A whisper of caution, for all to construe!
📝✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/remove-unsafe-blocks-from-test_utils.rs

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Aug 10, 2025

@greptile review

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Summary

This PR removes unnecessary unsafe blocks from environment variable manipulation functions in the test utilities module. The changes affect two functions: set_var and remove_var, which previously wrapped calls to std::env::set_var and std::env::remove_var in unsafe blocks with accompanying SAFETY comments.

The removal is correct because std::env::set_var and std::env::remove_var are safe functions in Rust's standard library that don't require unsafe blocks. The previous implementation was misleading, suggesting these operations were unsafe when they're actually part of Rust's safe API surface. The functions maintain their thread safety through the existing ENV_LOCK mutex, which serializes access to environment variable modifications during parallel test execution.

This change aligns with the codebase's philosophy of avoiding unsafe code unless absolutely necessary and fixes a code quality issue identified in issue #61. The functionality remains identical - tests can still safely modify environment variables in a thread-safe manner, but without the incorrect unsafe annotations.

Important Files Changed

Files Changed
Filename Score Overview
src/test_utils.rs 5/5 Removed unnecessary unsafe blocks from environment variable functions

Confidence score: 5/5

  • This PR is safe to merge with minimal risk
  • Score reflects correct removal of misleading unsafe blocks around safe standard library functions
  • No files require special attention

1 file reviewed, no comments

Edit Code Review Bot Settings | Greptile

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Aug 10, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 10, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Summary

This review covers only the changes made since the last review (commit 5d4f584), not the entire PR.

This PR contains a critical implementation error that directly contradicts its stated purpose. The PR description claims to "remove redundant unsafe blocks" and "call std::env::set_var and std::env::remove_var directly without wrapping them in unsafe", but the actual implementation does the exact opposite - it adds unsafe blocks around these function calls.

The changes modify src/test_utils.rs by wrapping std::env::set_var and std::env::remove_var calls in unsafe blocks with SAFETY comments claiming that the global mutex makes these calls safe. However, this demonstrates a fundamental misunderstanding of Rust's safety model. These standard library functions are already memory-safe APIs that do not require unsafe blocks. The mutex addresses thread safety concerns (ensuring serialized access to environment variables), but has nothing to do with memory safety, which is what unsafe blocks are designed for.

This change will cause compilation failures since the project's lint rules explicitly forbid unnecessary unsafe usage, and it violates the codebase's strict policy against using unsafe unless absolutely necessary.

Important Files Changed

Files Changed
Filename Score Overview
src/test_utils.rs 1/5 Incorrectly adds unsafe blocks around safe std::env functions, contradicting PR purpose

Confidence score: 0/5

  • This PR will definitely cause compilation failures and violates project coding standards
  • Score reflects critical implementation error that directly contradicts the stated goal
  • The src/test_utils.rs file needs complete revision to actually remove unsafe blocks rather than add them

1 file reviewed, no comments

Edit Code Review Bot Settings | Greptile

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0aed9fb and feda864.

📒 Files selected for processing (1)
  • src/test_utils.rs (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Prefer semantic error enums: Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary: Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library: Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.
Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.

Files:

  • src/test_utils.rs

⚙️ CodeRabbit Configuration File

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.

Files:

  • src/test_utils.rs
🔍 MCP Research (1 server)

Deepwiki:

  • The vk codebase has a development guideline that unsafe code should be avoided unless absolutely necessary and must be clearly documented. In the test utilities (though the relevant code is actually in src/main.rs under the test module), the set_var and remove_var functions wrap std::env::set_var and std::env::remove_var in unsafe blocks with a SAFETY comment stating that manipulating environment variables in tests is safe because tests run serially. This serial execution prevents race conditions when modifying global environment variables. (Functions ask_question response)

  • The test functions that manipulate environment variables, such as detect_utf8_locale, use these set_var and remove_var wrappers and are marked with #[serial] to ensure tests run one after another, preventing interference. This serial test execution is the rationale behind the safety justification for the unsafe blocks. (Functions ask_question response)

  • The project’s development guidelines emphasize avoiding unsafe unless absolutely necessary and require clear documentation of any unsafe usage. The presence of a global mutex to serialize access to environment variables in tests aligns with this guideline by providing thread safety without needing unsafe blocks. (Functions ask_question response)

Comment thread src/test_utils.rs
Comment on lines +21 to 24
// SAFETY: The global mutex serialises access to the environment, making the
// unsynchronised standard library calls safe for our tests.
unsafe { std::env::set_var(key, value) };
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Aug 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Remove redundant unsafe and SAFETY comments; call std::env::set_var directly under the lock

std::env::set_var is a safe API. Keeping an unsafe block violates the project guideline to avoid unsafe and will trigger clippy::unnecessary_unsafe. This also fails the stated PR objective (issue #61).

Apply this diff:

     let _guard = env_lock();
-    // SAFETY: The global mutex serialises access to the environment, making the
-    // unsynchronised standard library calls safe for our tests.
-    unsafe { std::env::set_var(key, value) };
+    // Serialise access to the process environment via a global mutex.
+    std::env::set_var(key, value);

Run this to confirm no lingering unsafe env calls across the repo:


🏁 Script executed:

#!/bin/bash
rg -n --hidden --no-ignore $'unsafe\\s*\\{\\s*std::env::(set_var|remove_var)\\b' -A2 -B2

Length of output: 684


MCP Context Analysis:

📊 Deepwiki:

  • The vk codebase has a development guideline that unsafe code should be avoided unless absolutely necessary and must be clearly documented. In the test utilities (though the relevant code is actually in src/main.rs under the test module), the set_var and remove_var functions wrap std::env::set_var and std::env::remove_var in unsafe blocks with a SAFETY comment stating that manipulating environment variables in tests is safe because tests run serially. This serial execution prevents race conditions when modifying global environment variables. (Functions ask_question response)

  • The test functions that manipulate environment variables, such as detect_utf8_locale, use these set_var and remove_var wrappers and are marked with #[serial] to ensure tests run one after another, preventing interference. This serial test execution is the rationale behind the safety justification for the unsafe blocks. (Functions ask_question response)

  • The project’s development guidelines emphasize avoiding unsafe unless absolutely necessary and require clear documentation of any unsafe usage. The presence of a global mutex to serialize access to environment variables in tests aligns with this guideline by providing thread safety without needing unsafe blocks. (Functions ask_question response)


Remove redundant unsafe blocks and SAFETY comments in src/test_utils.rs

Pinpoint and eliminate both occurrences of unnecessary unsafe around std::env calls. Call the safe APIs directly under the existing global mutex guard.

Locations to fix:

  • Lines 21–24: unsafe { std::env::set_var(key, value) }
  • Lines 31–34: unsafe { std::env::remove_var(key) }

Apply this diff:

--- a/src/test_utils.rs
+++ b/src/test_utils.rs
@@ -20,11 +20,8 @@ pub fn set_var(key: &str, value: &str) {
     let _guard = env_lock();
-    // SAFETY: The global mutex serialises access to the environment, making the
-    // unsynchronised standard library calls safe for our tests.
-    unsafe { std::env::set_var(key, value) };
+    // Serialise access to the process environment via a global mutex.
+    std::env::set_var(key, value);
 }

 pub fn remove_var(key: &str) {
     let _guard = env_lock();
-    // SAFETY: The global mutex serialises access to the environment, making the
-    // unsynchronised standard library calls safe for our tests.
-    unsafe { std::env::remove_var(key) };
+    // Serialise access to the process environment via a global mutex.
+    std::env::remove_var(key);
 }

Verify no lingering unsafe env calls:

rg -n --hidden --no-ignore $'unsafe\\s*\\{\\s*std::env::(set_var|remove_var)\\b' -A2 -B2
🤖 Prompt for AI Agents
In src/test_utils.rs at lines 21 to 24 and 31 to 34, remove the redundant unsafe
blocks wrapping std::env::set_var and std::env::remove_var calls, as these APIs
are safe to call under the existing global mutex guard. Also, delete the
associated SAFETY comments explaining the unsafe usage. After making these
changes, run the provided ripgrep command to ensure no unsafe env calls remain.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Rust 2024 Edition, calls to std::env::set_var (and std::env::remove_var) must be wrapped within an unsafe { … } block. The Rust Edition Guide specifically marks these functions as unsafe in 2024 because they can lead to unsound behaviour in multi-threaded programs; the global process environment isn’t safely synchronised across threads, leading to potential data races and undefined behaviour.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check Cargo.toml for the Rust edition
cat Cargo.toml | grep -E "edition|rust-version"

# Check if there's any rust-toolchain file
fd -H "rust-toolchain" --type f --exec cat {}

# Search for any edition-related configuration
rg "edition.*2024|2021|2018" Cargo.toml

Length of output: 251


@leynos You're absolutely correct, and I apologize for the confusion in my initial review.

I've verified that this project uses Rust 2024 Edition with a nightly toolchain (nightly-2025-07-22). In Rust 2024, std::env::set_var and std::env::remove_var are indeed unsafe functions that must be called within unsafe blocks.

The current implementation with:

  • unsafe blocks around these calls
  • SAFETY comments explaining the global mutex serialization

is not only correct but required for Rust 2024. The SAFETY comments appropriately document why the unsafe usage is sound (the global mutex prevents data races).

Given this, the PR's objective to remove these unsafe blocks would actually cause compilation errors in Rust 2024. The existing code follows the project's guideline that unsafe should be avoided "unless absolutely necessary" - and in this case, it is absolutely necessary due to the Rust 2024 API changes.

The issue #61 appears to have been based on the assumption that these are safe APIs, which was true in earlier Rust editions but not in 2024.


🧬 Code Graph Analysis Results
  • File: src/api/mod.rs, lines 90-97
    pub fn new(
            token: &str,
            transcript: Option<std::path::PathBuf>,
        ) -> Result<Self, std::io::Error> {
            let endpoint =
                env::var("GITHUB_GRAPHQL_URL").unwrap_or_else(|_| GITHUB_GRAPHQL_URL.to_string());
            Self::with_endpoint(token, &endpoint, transcript)
        }
    

✏️ Learnings added
Learnt from: leynos
PR: leynos/vk#62
File: src/test_utils.rs:21-24
Timestamp: 2025-08-11T11:28:45.376Z
Learning: In the vk project, which uses Rust 2024 Edition (as specified in Cargo.toml), the std::env::set_var and std::env::remove_var functions are unsafe and must be called within unsafe blocks. The project's test utilities in src/test_utils.rs correctly wrap these calls in unsafe blocks with SAFETY comments explaining that a global mutex serializes access to prevent data races.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: leynos
PR: leynos/comenq#16
File: tests/support/env_guard.rs:0-0
Timestamp: 2025-07-31T18:01:39.307Z
Learning: In Rust 2024 Edition and nightly versions, std::env::set_var and std::env::remove_var have been marked as unsafe functions due to thread safety issues on non-Windows platforms. The unsafe usage is justified in single-threaded contexts or when proper synchronization is ensured, such as with serial test execution.

Learnt from: leynos
PR: leynos/comenq#16
File: tests/support/env_guard.rs:0-0
Timestamp: 2025-07-31T18:01:39.307Z
Learning: In Rust nightly, std::env::set_var and std::env::remove_var are marked as unsafe functions due to potential data races when accessing environment variables in multi-threaded contexts. Using them in tests with proper synchronization (like serial_test::serial) is acceptable with appropriate unsafe blocks and documentation.

Comment thread src/test_utils.rs
Comment on lines +31 to 34
// SAFETY: The global mutex serialises access to the environment, making the
// unsynchronised standard library calls safe for our tests.
unsafe { std::env::remove_var(key) };
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Aug 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove redundant unsafe and SAFETY comments; call std::env::remove_var directly under the lock

std::env::remove_var is a safe API. The unsafe block is unnecessary and conflicts with the project’s “avoid unsafe” rule and the goal of this PR.

Apply this diff:

     let _guard = env_lock();
-    // SAFETY: The global mutex serialises access to the environment, making the
-    // unsynchronised standard library calls safe for our tests.
-    unsafe { std::env::remove_var(key) };
+    // Serialise access to the process environment via a global mutex.
+    std::env::remove_var(key);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// SAFETY: The global mutex serialises access to the environment, making the
// unsynchronised standard library calls safe for our tests.
unsafe { std::env::remove_var(key) };
}
let _guard = env_lock();
// Serialise access to the process environment via a global mutex.
std::env::remove_var(key);
}
🤖 Prompt for AI Agents
In src/test_utils.rs around lines 31 to 34, remove the unsafe block and the
associated SAFETY comment surrounding the call to std::env::remove_var, as this
function is safe to call directly. Replace the unsafe block with a direct call
to std::env::remove_var under the existing global mutex lock to comply with the
project's "avoid unsafe" rule.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Rust 2024 Edition, calls to std::env::set_var (and std::env::remove_var) must be wrapped within an unsafe { … } block. The Rust Edition Guide specifically marks these functions as unsafe in 2024 because they can lead to unsound behaviour in multi-threaded programs; the global process environment isn’t safely synchronised across threads, leading to potential data races and undefined behaviour.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🌐 Web query:

Rust 2024 Edition std::env::set_var std::env::remove_var unsafe functions thread safety

💡 Result:

In Rust 2024 Edition, std::env::set_var and std::env::remove_var are marked unsafe because calling them can be unsound in multithreaded programs on many non-Windows platforms; they remain safe on Windows and in single-threaded programs.[1][3][2]

Key points

  • Why they’re unsafe now: The process environment is a global shared resource. On Unix-like platforms, many C library functions may read environment variables without any thread-safe guarantees, and not all readers go through Rust’s std::env. This makes it impossible to ensure no concurrent reads/writes, leading to potential UB; hence the functions require unsafe in the 2024 Edition.[1][3][2]
  • Safety contract (from the docs):
    • Safe in single-threaded programs.[3][2]
    • Always safe on Windows (single or multi-threaded).[3][2]
    • On other OSes in a multithreaded program, the only practically safe option is to not use set_var/remove_var at all because you would need to ensure no other thread is reading or writing the environment by any means, which is not realistically enforceable.[3][2]

Practical guidance

  • Prefer not to mutate process-wide environment in multithreaded Unix programs. Use alternatives:
    • For child processes: use Command::env / env_remove / env_clear to set per-child environment safely.[3][2]
    • In tests: running each test in its own process (e.g., nextest) generally makes per-test env changes acceptable; be cautious with cargo test which runs many tests in one process.[8]
    • Replace env-based toggles with explicit configuration (e.g., globals, once_cell, or arguments) when possible.[7]
  • If you must call them:
    • On Windows: it’s safe without extra coordination.[3][2]
    • On Unix single-threaded contexts (e.g., build.rs running single-threaded): permissible; document the safety and confine calls to clearly single-threaded code paths.[3][7]
  • Tooling and ecosystem:
    • The Edition Guide documents the newly unsafe status and the rationale.[1]
    • Community discussion and migration write-ups suggest removing most uses or confining them behind unsafe wrappers verified to be single-threaded.[7]
    • Some crates attempt to provide “safe” wrappers with runtime checks on Unix to mitigate risks, but these rely on heuristics and cannot guarantee absence of foreign readers; evaluate carefully if you consider such a crate.[5][9]

Notes on thread safety specifics

  • The unsafety stems from Unix C library/environment semantics: environment access isn’t guaranteed thread-safe, and readers (including within Rust std for things like DNS resolution) may bypass std::env, so you can’t reliably gate all access.[3][2]
  • Windows maintains a per-process environment with thread-safe APIs, so Rust marks these operations safe there even in multithreaded programs.[3][2]

References to official docs

  • Edition change and rationale: Newly unsafe functions (2024 Edition Guide).[1]
  • Function safety sections: std::env::set_var and std::env::remove_var docs detail platform and threading constraints and recommended alternatives.[3][2]

Citations:


@leynos You're absolutely correct, and I apologize for the incorrect review.

In Rust 2024 Edition, std::env::set_var and std::env::remove_var are indeed marked as unsafe functions due to potential data races when accessing environment variables in multi-threaded contexts on non-Windows platforms ¹. The unsafe blocks in your code are therefore required, not redundant.

The SAFETY comments you've added are appropriate and correctly document why these unsafe calls are justified in your test context - the global mutex serializes access to prevent the race conditions that make these functions unsafe.

Given this information, the premise of PR #62 and issue #61 (removing "redundant" unsafe blocks) appears to be based on outdated information. These unsafe blocks are necessary for Rust 2024 Edition compliance. The code as it currently stands is correct and follows best practices by:

  1. Using unsafe blocks as required by Rust 2024
  2. Providing clear SAFETY documentation
  3. Using a global mutex to serialize access

Would you like to close this PR and the associated issue, or would you prefer to update them to reflect that the unsafe blocks are actually required?


🧠 Learnings used
Learnt from: leynos
PR: leynos/comenq#16
File: tests/support/env_guard.rs:0-0
Timestamp: 2025-07-31T18:01:39.307Z
Learning: In Rust 2024 Edition and nightly versions, std::env::set_var and std::env::remove_var have been marked as unsafe functions due to thread safety issues on non-Windows platforms. The unsafe usage is justified in single-threaded contexts or when proper synchronization is ensured, such as with serial test execution.

Learnt from: leynos
PR: leynos/comenq#16
File: tests/support/env_guard.rs:0-0
Timestamp: 2025-07-31T18:01:39.307Z
Learning: In Rust nightly, std::env::set_var and std::env::remove_var are marked as unsafe functions due to potential data races when accessing environment variables in multi-threaded contexts. Using them in tests with proper synchronization (like serial_test::serial) is acceptable with appropriate unsafe blocks and documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove unnecessary unsafe blocks around std::env calls in test_utils

1 participant