Go & Rust Code Review: 11 Battle-Tested AI Prompts to Catch Data Races, Memory Leaks, and Security Flaws Before They Hit Production

Why Your Code Review Needs an AI Second Opinion

You've written the perfect Go routine or Rust struct. It compiles. Tests pass. But then — boom — a data race in production, a memory leak that slowly eats your server, or a vulnerability that a security researcher finds before you do. The problem? Human reviewers miss things. Studies show that even experienced reviewers catch only 30-50% of defects in code reviews (source: IEEE Transactions on Software Engineering, 2017).

AI won't replace your human reviewers, but it can act as a tireless first pass — scanning for concurrency hazards, unsafe memory patterns, and security anti-patterns that are easy to overlook. The key is knowing how to ask. Generic prompts like "review my code for bugs" produce generic, often useless, feedback. But with carefully crafted prompts, you can turn ChatGPT or Claude into a specialized reviewer for Go and Rust.

This article gives you 11 specific, copy-paste-ready prompts — each targeting a different class of bug. You'll see exactly what to paste, what to expect, and how to apply the results to your codebase. No fluff, just working prompts based on real language specs and official documentation (Go Memory Model, Rust Reference, OWASP Top 10).

How to Use These Prompts Effectively

Before diving in, understand the workflow:

  1. Provide context: Always include the relevant code snippet, function signature, or file. The more context, the better.
  2. Specify the language version: Go 1.21+, Rust 2021 edition, etc. AI models know syntax but version-specific nuances matter.
  3. Ask for concrete output: Require the AI to list issues with severity, line numbers, and fix suggestions.
  4. Iterate: Use the AI's output to refine your questions. The first pass might miss something; ask follow-ups.
  5. Verify: AI suggestions are not gospel. Test fixes and run static analysis tools like go vet, golangci-lint, cargo clippy, and cargo miri to confirm.

11 Prompts for Go & Rust Code Review

1. Data Race Detector (Go)

What it does: Identifies potential data races in concurrent Go code by analyzing memory access patterns.

Prompt:

You are a Go concurrency expert. Analyze the following Go code for data races. A data race occurs when two goroutines access the same variable concurrently, and at least one of the accesses is a write. Use the Go Memory Model (https://go.dev/ref/mem) as the authority. List every unsafe access, explain the race, and suggest fixes using channels or the sync package.

```go
package main

import (
    "fmt"
    "sync"
)

type Counter struct {
    mu sync.Mutex
    n  int
}

func (c *Counter) Inc() { c.mu.Lock(); c.n++; c.mu.Unlock() }
func (c *Counter) Value() int { c.mu.Lock(); defer c.mu.Unlock(); return c.n }

func main() {
    c := &Counter{}
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            c.Inc()
        }()
    }
    wg.Wait()
    fmt.Println(c.Value())
}
**Example output**: "No data race found in this code. The mutex properly synchronizes access. However, note that if you had used `c.n++` directly in a goroutine, it would race."

### 2. Memory Leak Hunter (Go)

**What it does**: Finds goroutine leaks and memory leaks caused by improper channel usage or missing context cancellation.

**Prompt**:

Act as a Go performance expert. Examine this code for memory or goroutine leaks. A goroutine leak happens when a goroutine blocks forever on a channel receive or send that never completes. Use the official Go documentation on channels and context (https://go.dev/blog/pipelines, https://pkg.go.dev/context) to support your analysis. List any leaked goroutines, the root cause, and how to fix them.

func worker(ch <-chan int) {
    for v := range ch {
        process(v)
    }
}

func main() {
    ch := make(chan int)
    go worker(ch)
    // ... send values to ch ...
    // forgot to close(ch)
}
**Example output**: "The worker goroutine will block forever because the channel is never closed. Add `defer close(ch)` in the sender, or use a context for cancellation."

### 3. Unsafe Code Auditor (Rust)

**What it does**: Reviews Rust `unsafe` blocks for undefined behavior and safety violations.

**Prompt**:

You are a Rust safety expert. Analyze the following Rust code, focusing on the unsafe blocks. Check for violations of Rust's safety rules: null pointers, dangling references, buffer overflows, incorrect use of std::mem::transmute, and improper Send/Sync implementations. Reference the Rust Reference (https://doc.rust-lang.org/reference/unsafe.html) and the Rustonomicon (https://doc.rust-lang.org/nomicon/) for each issue. Explain the danger and provide safe alternatives.

unsafe fn dangerous(ptr: *const u8, len: usize) -> u8 {
    let slice = std::slice::from_raw_parts(ptr, len);
    *slice.get_unchecked(len - 1)
}
**Example output**: "This function is unsafe because it creates a slice of `len` bytes without checking that the pointer is valid for that length. If `len` is larger than the allocated buffer, it reads out of bounds. Use `std::slice::from_raw_parts` only after validating the pointer and length."

### 4. Concurrency Logic Checker (Rust)

**What it does**: Pinpoints deadlocks, race conditions, and incorrect use of `Mutex`, `RwLock`, and channels.

**Prompt**:

You are a Rust concurrency expert. Review this code for concurrency issues such as deadlocks, race conditions, or misuse of synchronization primitives. Use the Rust standard library docs (https://doc.rust-lang.org/std/sync/) as a reference. For each issue, describe a scenario that triggers it, and suggest a fix.

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let data = Arc::new(Mutex::new(vec![1, 2, 3]));
    let mut handles = vec![];

    for _ in 0..10 {
        let data = Arc::clone(&data);
        handles.push(thread::spawn(move || {
            let mut guard = data.lock().unwrap();
            guard.push(1);
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }
}
**Example output**: "The code looks correct: each thread locks the mutex, pushes to the vector, and releases. No deadlock because only one lock is held. If you had two mutexes and locked them in different orders, you'd risk deadlock."

### 5. Web Vulnerability Scanner (Go)

**What it does**: Checks Go web handlers for common security flaws: SQL injection, XSS, CSRF, path traversal.

**Prompt**:

You are a security expert specializing in Go web applications. Analyze this HTTP handler for vulnerabilities. Look for SQL injection, cross-site scripting (XSS), cross-site request forgery (CSRF), and path traversal. Use OWASP Top 10 (https://owasp.org/www-project-top-ten/) and Go's database/sql docs (https://pkg.go.dev/database/sql) as references. For each vulnerability, show how to exploit it and fix it.

func handler(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")
    query := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name)
    db.Query(query) // assume db is a *sql.DB
    fmt.Fprintf(w, "Hello, %s", name)
}
**Example output**: "SQL injection: `name` is unsanitized. An attacker can submit `' OR '1'='1` to dump the table. Use parameterized queries: `db.Query("SELECT * FROM users WHERE name = ?", name)`. XSS: the response reflects `name` without escaping. Use `html.EscapeString`."

### 6. Error Handling Reviewer (Go)

**What it does**: Finds unhandled errors, ignored return values, and improper error wrapping.

**Prompt**:

You are a Go code quality coach. Review this code for error handling issues. Check for ignored errors (e.g., missing if err != nil), improper use of log.Fatal vs log.Print, and failure to wrap errors with fmt.Errorf and %w. Reference the Go blog on error handling (https://go.dev/blog/error-handling-and-go) and the errors package docs (https://pkg.go.dev/errors). List each issue with severity and fix.

func readFile(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err // Should wrap: fmt.Errorf("open %s: %w", path, err)
    }
    defer f.Close() // error ignored
    var data []byte
    _, err = f.Read(data) // err ignored
    return nil
}
**Example output**: "The error from `f.Close()` is ignored; it can return an error that may matter (e.g., on read-only filesystems). The `Read` error is ignored; you must check it. The `Open` error should be wrapped to add context."

### 7. Type Safety & Trait Bounds Checker (Rust)

**What it does**: Reviews type usage, trait bounds, and lifetime annotations for correctness and efficiency.

**Prompt**:

You are a Rust type system expert. Analyze this code for type safety issues, overly broad trait bounds, and lifetime errors. Use the Rust Book (https://doc.rust-lang.org/book/ch10-00-generics.html) and the Rust Reference on lifetimes (https://doc.rust-lang.org/reference/lifetime-elision.html) as references. Suggest improvements for clarity and performance.

fn process<T>(data: &[T]) -> Vec<T> where T: Clone {
    let mut result = Vec::new();
    for item in data {
        result.push(item.clone());
    }
    result
}
**Example output**: "The bound `T: Clone` is too restrictive if you only need to read; consider returning `Vec<&T>` instead. If you need to own, that's fine. Also, consider using `iter().cloned().collect()` for efficiency."

### 8. Performance Bottleneck Finder (Go)

**What it does**: Identifies inefficient allocations, excessive copying, and poor algorithm choices.

**Prompt**:

You are a Go performance engineer. Review this function for performance issues: excessive allocations, O(n^2) patterns, unnecessary conversions, and misuse of slices. Use Go's pprof documentation (https://go.dev/blog/pprof) as a reference for common performance patterns. Provide benchmarks before/after if possible.

func Process(items []string) []string {
    result := make([]string, 0, len(items))
    for _, s := range items {
        if strings.Contains(s, "a") {
            result = append(result, strings.ToUpper(s))
        }
    }
    return result
}
**Example output**: "The code is already efficient: preallocates result with capacity, uses range, and avoids string concatenation. To improve, consider using `strings.Builder` if building strings, but here it's fine."

### 9. Memory Leak via Reference Cycles (Rust)

**What it does**: Detects memory leaks caused by reference cycles with `Rc` and `RefCell`.

**Prompt**:

You are a Rust memory management expert. Examine this code for memory leaks due to reference cycles. Reference cycles occur when Rc pointers form a cycle, preventing deallocation. Use the Rust Book's chapter on reference cycles (https://doc.rust-lang.org/book/ch15-06-reference-cycles.html) to explain why it leaks and suggest fixes like Weak.

use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct Node {
    value: i32,
    next: Option<Rc<RefCell<Node>>>,
}

fn main() {
    let a = Rc::new(RefCell::new(Node { value: 1, next: None }));
    let b = Rc::new(RefCell::new(Node { value: 2, next: Some(a.clone()) }));
    if let Some(ref mut a_ref) = a.borrow_mut().next {
        *a_ref = b.clone(); // creates cycle
    }
}
**Example output**: "This creates a cycle: a -> b -> a. Both Rc reference counts never drop to zero, leaking memory. Use `Weak` for one of the pointers to break the cycle."

### 10. Security Vulnerability in Unsafe Rust (Rust)

**What it does**: Searches for security vulnerabilities in Rust FFI and unsafe code, such as buffer overflows and integer overflows.

**Prompt**:

You are a security auditor for Rust code. Analyze this FFI code for vulnerabilities: buffer overflows, integer overflows, and improper handling of C strings. Use the Rust FFI guide (https://doc.rust-lang.org/nomicon/ffi.html) and the Rust Security Advisory database (https://rustsec.org/) as references. For each concern, provide a fix.

use std::os::raw::c_char;

extern "C" {
    fn strlen(s: *const c_char) -> usize;
}

fn safe_strlen(s: &str) -> usize {
    let c_str = std::ffi::CString::new(s).unwrap();
    unsafe { strlen(c_str.as_ptr()) }
}
**Example output**: "The `CString::new` will panic if the string contains interior null bytes, which could be exploited. If the input comes from an untrusted source, handle the error gracefully. Also, `strlen` returns a size_t; cast to usize is fine."

### 11. General Code Review Bot (Both)

**What it does**: Provides a comprehensive review of a file or function, catching anything the specialized prompts miss.

**Prompt**:

You are a senior developer performing a thorough code review of the provided code. Focus on correctness, concurrency, memory safety, error handling, and style. Use the language's official style guide (Effective Go: https://go.dev/doc/effective_go, Rust API Guidelines: https://rust-lang.github.io/api-guidelines/). Output a list of issues with severity (critical, warning, suggestion) and fixes.

[Paste your code here]
`` **Example output**: "Critical: data race on line 42. Warning: error handling missing on line 18. Suggestion: userange` instead of index loop."

Putting It All Together: A Real-World Workflow

Let's walk through a practical scenario. You have a Go service that handles concurrent HTTP requests and a Rust library that does heavy computation. You want to review both before release.

  1. Start with the general review bot to get an overview. Paste your Go server code. The AI flags a potential race condition on a shared counter.
  2. Run the data race detector prompt on the specific function. It confirms the race and suggests using atomic operations.
  3. For the Rust library, run the unsafe code auditor prompt on any unsafe blocks. It finds a missing bounds check.
  4. Run the memory leak hunter on your Go goroutines. It finds a goroutine that never exits because a channel isn't closed.
  5. Apply fixes and then run go vet -race and cargo clippy to verify.

This workflow saves hours of manual review and catches bugs that might otherwise slip through.

Limitations and Caveats

AI code review is not a substitute for human review or static analysis tools. It can produce false positives and miss context-specific issues. Always combine AI prompts with:

  • Official documentation: Use the exact language specs to validate AI claims.
  • Static analysis tools: go vet, golangci-lint, cargo clippy, cargo miri for Rust.
  • Human review: A second pair of eyes is irreplaceable.

Also, be careful with AI suggesting fixes that introduce new bugs. Test thoroughly.

Conclusion

AI prompts can transform LLMs into specialized code reviewers that catch data races, memory leaks, and security flaws in Go and Rust. The 11 prompts above are designed to be used as a starting point — adapt them to your codebase and combine them with traditional tools. The result: fewer production incidents, cleaner code, and a more efficient review process.

Start using these prompts today, and you'll see the difference. Remember, the AI is your assistant — not your oracle. Verify every finding. Happy reviewing!


For more insights on AI-powered development, explore our blog at asibiont.com/blog.

← All posts

Comments