Troubleshooting
Common errors, platform-specific gotchas, and PyO3 pitfalls.
Installation and Toolchain
cargo not found after installation
source "$HOME/.cargo/env" # Linux / macOS
# Or restart your terminal (Windows)
linker 'cc' not found on Linux
sudo apt-get install build-essential # Ubuntu / Debian
sudo dnf groupinstall "Development Tools" # Fedora / RHEL
sudo pacman -S base-devel # Arch
linker 'link.exe' not found on Windows
Install Visual Studio Build Tools with the "Desktop development with C++" workload. Or switch to the GNU toolchain:
rustup toolchain install stable-x86_64-pc-windows-gnu
rustup default stable-x86_64-pc-windows-gnu
rustup update fails or hangs
# Force re-download of the toolchain
rustup self update # update rustup itself first
rustup update stable
Compile Errors
cannot assign twice to immutable variable
You forgot mut:
// ❌
let x = 10;
x = 20; // error
// ✅
let mut x = 10;
x = 20;
use of moved value
The value was moved into a function or another variable. Either clone it or borrow it:
// ❌
let a = vec![1, 2, 3];
let b = a; // a is moved
println!("{:?}", a); // error
// ✅ — borrow
let b = &a;
println!("{:?}", a); // still valid
// ✅ — clone
let b = a.clone();
println!("{:?}", a); // still valid
mismatched types when mixing String and &str
// ❌
fn greet(name: String) { ... }
greet("alice"); // "alice" is &str, not String
// ✅
fn greet(name: &str) { ... } // accept &str — more flexible
greet("alice");
greet(&my_string); // &String coerces to &str
borrowed value does not live long enough
A reference outlives the value it points to. The classic fix is to move the value into a binding that lives longer:
// ❌
let result;
{
let s = String::from("hello");
result = &s; // s is dropped at end of block
}
println!("{result}"); // error: s is gone
// ✅
let s = String::from("hello"); // s lives in the outer scope
let result = &s;
println!("{result}");
the trait bound T: Send is not satisfied
You are trying to share a non-Send type across threads. Common culprits:
Rc<T>— useArc<T>insteadRefCell<T>— useMutex<T>orRwLock<T>instead- Raw pointers — wrap in a struct that implements
Sendmanually (advanced)
PyO3 / Maturin Errors
extension-module feature missing — segfault or import error
# ✅ Required in Cargo.toml
[dependencies]
pyo3 = { version = "0.20", features = ["extension-module"] }
Forgetting extension-module causes a double-link of libpython and a segfault or import error on some platforms.
cargo test fails with linker errors on a cdylib-only crate
# ✅ Required — add rlib so cargo test can build a test harness
[lib]
crate-type = ["cdylib", "rlib"]
#[pymodule] function name does not match crate name
# Cargo.toml
[lib]
name = "logferry" # the Python module name
// lib.rs — function name must match [lib] name
#[pymodule]
fn logferry(_py: Python<'_>, m: &PyModule) -> PyResult<()> { ... }
// ^^^^^^^^ must match Cargo.toml [lib] name
Mismatch causes ImportError: cannot import name 'logferry'.
maturin develop fails with python not found
Make sure your virtual environment is activated before running maturin:
source .venv/bin/activate # Linux / macOS
.venv\Scripts\activate # Windows PowerShell
maturin develop --release
unwrap() panics the Python interpreter
Never use unwrap() in production PyO3 code. Use ? and return PyResult:
// ❌ — panics the interpreter on error
let n: i64 = s.parse().unwrap();
// ✅ — converts to a Python ValueError
let n: i64 = s.parse().map_err(|e| PyValueError::new_err(format!("{e}")))?;
Returning &str from a function that borrows a local variable
// ❌ — borrow checker rejects this
#[pyfunction]
fn greet(name: &str) -> &str {
let result = format!("Hello, {name}!");
&result // result is a local — it will be dropped
}
// ✅ — return an owned String; PyO3 converts it to Python str
#[pyfunction]
fn greet(name: &str) -> String {
format!("Hello, {name}!")
}
PyErr construction inside py.allow_threads
Some PyErr::new_err(...) calls need the GIL. Construct errors before the allow_threads block or use lazy construction:
// ❌ — may panic inside allow_threads
let result = py.allow_threads(|| {
if bad { return Err(PyValueError::new_err("bad")); } // GIL not held
Ok(42)
});
// ✅ — signal the error with a Rust type, convert after re-acquiring GIL
let result: Result<i64, String> = py.allow_threads(|| {
if bad { return Err("bad".to_string()); }
Ok(42)
});
result.map_err(|e| PyValueError::new_err(e))?;
Performance
Slow compile times
# .cargo/config.toml
[profile.dev]
opt-level = 0
debug = 1 # less debug info = faster link
On Linux, the mold linker dramatically reduces link times:
sudo apt-get install mold
# .cargo/config.toml
[build]
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
rust-analyzer not working in VS Code
rustup component add rust-analyzer- Reload the window:
Ctrl+Shift+P→ "Developer: Reload Window" - Check output:
Ctrl+Shift+P→ "rust-analyzer: Show RA server logs"
See Also
- Installation — setup guide with platform-specific steps
- Daily Workflow — recommended inner loop
- Cargo Cheatsheet — all cargo and maturin commands