Open
Description
Summary
After running the cargo clippy command it told me there was a bug in the tool and that I should file an issue. It's complaining about this error:
error[E0369]: binary operation `!=` cannot be applied to type `tokio::sync::MutexGuard<'_, std::option::Option<std::net::IpAddr>>`
--> vxlan-sidecar/src/main.rs:152:33
See attached for the log output and file that produces this bug.
[nix-shell:~/Code/carve]$ cargo clippy --fix --all-targets
warning: virtual workspace defaulting to `resolver = "1"` despite one or more workspace members being on edition 2024 which implies `resolver = "3"`
note: to keep the current resolver, specify `workspace.resolver = "1"` in the workspace root's manifest
note: to use the edition 2024 resolver, specify `workspace.resolver = "3"` in the workspace root's manifest
note: for more details see https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions
warning: virtual workspace defaulting to `resolver = "1"` despite one or more workspace members being on edition 2024 which implies `resolver = "3"`
note: to keep the current resolver, specify `workspace.resolver = "1"` in the workspace root's manifest
note: to use the edition 2024 resolver, specify `workspace.resolver = "3"` in the workspace root's manifest
note: for more details see https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions
Checking carve v0.1.0 (/home/justin/Code/carve/carve)
Fixed carve/src/redis_manager.rs (2 fixes)
Checking vtep v0.1.0 (/home/justin/Code/carve/vtep)
Checking vxlan-sidecar v0.1.0 (/home/justin/Code/carve/vxlan-sidecar)
Checking canary v0.1.0 (/home/justin/Code/carve/canary)
Checking qemu-box v0.1.0 (/home/justin/Code/carve/qemu-box)
Checking carve-api v0.1.0 (/home/justin/Code/carve/carve-api)
Checking carve-novnc-nginx v0.1.0 (/home/justin/Code/carve/carve-novnc-nginx)
warning: failed to automatically apply fixes suggested by rustc to crate `vxlan_sidecar`
after fixes were automatically applied the compiler reported errors within these files:
* vxlan-sidecar/src/main.rs
This likely indicates a bug in either rustc or cargo itself,
and we would appreciate a bug report! You're likely to see
a number of compiler warnings after this message which cargo
attempted to fix but failed. If you could open an issue at
https://github.com/rust-lang/rust-clippy/issues
quoting the full output of this command we'd be very appreciative!
Note that you may be able to make some more progress in the near-term
fixing code with the `--broken-code` flag
The following errors were reported:
error[E0369]: binary operation `!=` cannot be applied to type `tokio::sync::MutexGuard<'_, std::option::Option<std::net::IpAddr>>`
--> vxlan-sidecar/src/main.rs:152:33
|
152 | if last != Some(ip) {
| ---- ^^ -------- std::option::Option<std::net::IpAddr>
| |
| tokio::sync::MutexGuard<'_, std::option::Option<std::net::IpAddr>>
|
note: the foreign item type `tokio::sync::MutexGuard<'_, std::option::Option<std::net::IpAddr>>` doesn't implement `std::cmp::PartialEq<std::option::Option<std::net::IpAddr>>`
--> /home/justin/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/sync/mutex.rs:151:1
|
151 | pub struct MutexGuard<'a, T: ?Sized> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not implement `std::cmp::PartialEq<std::option::Option<std::net::IpAddr>>`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0369`.
Original diagnostics will follow.
warning: function call inside of `expect`
--> vxlan-sidecar/src/main.rs:90:10
|
90 | .expect(
| __________^
91 | | format!(
92 | | "Competition {} not found in config",
93 | | competition_name.as_str()
94 | | )
95 | | .as_str(),
96 | | );
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
= note: `#[warn(clippy::expect_fun_call)]` on by default
help: try
|
90 ~ .unwrap_or_else(|| panic!("Competition {} not found in config",
91 ~ competition_name.as_str()));
|
warning: useless use of `format!`
--> vxlan-sidecar/src/main.rs:126:14
|
126 | &format!("{}", competition.cidr.as_ref().unwrap()),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `competition.cidr.as_ref().unwrap().to_string()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
= note: `#[warn(clippy::useless_format)]` on by default
warning: using `print!()` with a format string that ends in a single newline
--> vxlan-sidecar/src/main.rs:147:5
|
147 | / print!(
148 | | "Starting DNS resolution loop for VTEP host: {}\n",
149 | | vtep_host
150 | | );
| |_____^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
= note: `#[warn(clippy::print_with_newline)]` on by default
help: use `println!` instead
|
147 ~ println!(
148 ~ "Starting DNS resolution loop for VTEP host: {}",
|
warning: this `map_or` can be simplified
--> vxlan-sidecar/src/main.rs:157:28
|
157 | if last.map_or(true, |prev| prev != ip) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or
= note: `#[warn(clippy::unnecessary_map_or)]` on by default
help: use a standard comparison instead
|
157 - if last.map_or(true, |prev| prev != ip) {
157 + if last != Some(ip) {
|
warning: `vxlan-sidecar` (bin "vxlan-sidecar" test) generated 4 warnings (run `cargo clippy --fix --bin "vxlan-sidecar" --tests` to apply 4 suggestions)
warning: redundant pattern matching, consider using `is_ok()`
--> qemu-box/src/main.rs:21:16
|
21 | if let Ok(_) = Command::new("kill").arg("-0").arg(pid.trim()).output() {
| -------^^^^^---------------------------------------------------------- help: try: `if Command::new("kill").arg("-0").arg(pid.trim()).output().is_ok()`
|
= note: this will change drop order of the result, as well as all temporaries
= note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
= note: `#[warn(clippy::redundant_pattern_matching)]` on by default
warning: `qemu-box` (bin "qemu-box" test) generated 1 warning
warning: `qemu-box` (bin "qemu-box") generated 1 warning (1 duplicate)
warning: failed to automatically apply fixes suggested by rustc to crate `vxlan_sidecar`
after fixes were automatically applied the compiler reported errors within these files:
* vxlan-sidecar/src/main.rs
This likely indicates a bug in either rustc or cargo itself,
and we would appreciate a bug report! You're likely to see
a number of compiler warnings after this message which cargo
attempted to fix but failed. If you could open an issue at
https://github.com/rust-lang/rust-clippy/issues
quoting the full output of this command we'd be very appreciative!
Note that you may be able to make some more progress in the near-term
fixing code with the `--broken-code` flag
The following errors were reported:
error[E0369]: binary operation `!=` cannot be applied to type `tokio::sync::MutexGuard<'_, std::option::Option<std::net::IpAddr>>`
--> vxlan-sidecar/src/main.rs:152:33
|
152 | if last != Some(ip) {
| ---- ^^ -------- std::option::Option<std::net::IpAddr>
| |
| tokio::sync::MutexGuard<'_, std::option::Option<std::net::IpAddr>>
|
note: the foreign item type `tokio::sync::MutexGuard<'_, std::option::Option<std::net::IpAddr>>` doesn't implement `std::cmp::PartialEq<std::option::Option<std::net::IpAddr>>`
--> /home/justin/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.45.1/src/sync/mutex.rs:151:1
|
151 | pub struct MutexGuard<'a, T: ?Sized> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not implement `std::cmp::PartialEq<std::option::Option<std::net::IpAddr>>`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0369`.
Original diagnostics will follow.
warning: `vxlan-sidecar` (bin "vxlan-sidecar") generated 4 warnings (4 duplicates)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.17s
Reproducer
Following file produced the bug:
use actix_web::{App, HttpServer, Responder, get, web};
use carve::config::AppConfig;
use std::env;
use std::net::IpAddr;
use std::process::Command;
use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::time::{Duration, sleep};
#[derive(Clone)]
enum HealthStatus {
Healthy,
Unhealthy,
}
type SharedHealth = Arc<Mutex<HealthStatus>>;
#[get("/api/health")]
async fn health(health: web::Data<SharedHealth>) -> impl Responder {
let status = health.lock().await;
match *status {
HealthStatus::Healthy => "Healthy",
HealthStatus::Unhealthy => "Unhealthy",
}
}
fn create_vxlan_interface(vxlan_id: &str) -> Result<(), String> {
// Remove vxlan0 if it exists
let _ = Command::new("ip").args(["link", "del", "vxlan0"]).status();
// Create vxlan0 with remote
let status = Command::new("ip")
.args([
"link", "add", "vxlan0", "type", "vxlan", "id", vxlan_id, "dev", "eth0", "dstport",
"4789",
])
.status()
.map_err(|e| format!("Failed to create vxlan0: {}", e))?;
if !status.success() {
return Err("Failed to create vxlan0 interface".into());
}
// Bring up vxlan0
Command::new("ip")
.args(["link", "set", "vxlan0", "up"])
.status()
.map_err(|e| format!("Failed to bring up vxlan0: {}", e))?;
Ok(())
}
fn create_bridge(cidr: &str) -> Result<(), String> {
// Remove br0 if it exists
let _ = Command::new("ip").args(["link", "del", "br0"]).status();
// Create br0
let status = Command::new("ip")
.args(["link", "add", "name", "br0", "type", "bridge"])
.status()
.map_err(|e| format!("Failed to create br0: {}", e))?;
if !status.success() {
return Err("Failed to create br0 interface".into());
}
// Bring up br0
Command::new("ip")
.args(["link", "set", "br0", "up"])
.status()
.map_err(|e| format!("Failed to bring up br0: {}", e))?;
// Add vxlan0 to br0
Command::new("ip")
.args(["link", "set", "vxlan0", "master", "br0"])
.status()
.map_err(|e| format!("Failed to add vxlan0 to br0: {}", e))?;
// Add IP address to br0
// Set vxlan0 address to .254 of the given CIDR
let ip_address = format!("{}/24", cidr.trim_end_matches(".0").to_string() + ".254");
// println!("Setting br0 address to {}", ip_address);
Command::new("ip")
.args(["addr", "add", &ip_address, "dev", "br0"])
.status()
.map_err(|e| format!("Failed to set br0 address: {}", e))?;
Ok(())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let team_name = env::var("TEAM_NAME").expect("TEAM_NAME env var required");
let config = AppConfig::new().expect("Failed to load config");
let competition_name = env::var("COMPETITION_NAME").expect("COMPETITION_NAME env var required");
let competition = config
.competitions
.iter()
.find(|c| c.name == competition_name)
.expect(
format!(
"Competition {} not found in config",
competition_name.as_str()
)
.as_str(),
);
let teams: Vec<String> = competition.teams.iter().map(|t| t.name.clone()).collect();
let team_index = teams
.iter()
.position(|n| n == &team_name)
.expect("TEAM_NAME not found in config");
let vxlan_id = 1338 + team_index as u32;
// get the cidr for the team
let cidr = competition.cidr.as_ref().expect("Competition CIDR missing");
let base = cidr.split('/').next().expect("Invalid CIDR");
let mut octets: Vec<u8> = base.split('.').map(|s| s.parse().unwrap()).collect();
// Set third octet to team_index+1
octets[2] = (team_index + 1) as u8;
octets[3] = 0;
let team_cidr = format!("{}.{}.{}.0", octets[0], octets[1], octets[2]);
// Use vtep_host from the first competition, fallback to localhost if missing
if let Err(e) = create_vxlan_interface(&vxlan_id.to_string()) {
eprintln!("{}", e);
}
if let Err(e) = create_bridge(&team_cidr) {
eprintln!("{}", e);
}
// Add route for the competitions's /16 subnet via .1 (first IP in team /24 subnet) on br0
let route_command = Command::new("ip")
.args([
"route",
"add",
&format!("{}", competition.cidr.as_ref().unwrap()),
"via",
&format!("{}.{}.{}.1", octets[0], octets[1], octets[2]),
"dev",
"br0",
])
.status();
match route_command {
Ok(status) if status.success() => {
println!("Route added successfully for competition subnet");
}
Ok(_) | Err(_) => {
eprintln!("Failed to add route for competition subnet");
}
}
// DNS resolution loop for vtep_host
let remote = competition.vtep_host.as_deref().unwrap_or("127.0.0.1");
let vtep_host = remote.to_string();
let last_ip = Arc::new(Mutex::new(None::<IpAddr>));
let last_ip_clone = last_ip.clone();
print!(
"Starting DNS resolution loop for VTEP host: {}\n",
vtep_host
);
tokio::spawn(async move {
loop {
match tokio::net::lookup_host((vtep_host.as_str(), 0)).await {
Ok(mut addrs) => {
if let Some(ip) = addrs.next().map(|sockaddr| sockaddr.ip()) {
let mut last = last_ip_clone.lock().await;
if last.map_or(true, |prev| prev != ip) {
// flush the FDB entries for vxlan0
let _ = Command::new("bridge")
.args(["fdb", "flush", "dev", "vxlan0"])
.status();
// Only update if IP changed
let status = Command::new("bridge")
.args([
"fdb",
"append",
"00:00:00:00:00:00",
"dst",
&ip.to_string(),
"dev",
"vxlan0",
])
.status();
match status {
Ok(s) if s.success() => {
*last = Some(ip);
println!("Appended FDB entry for vxlan0 remote {}", ip);
}
Ok(_) | Err(_) => {
eprintln!(
"Failed to append FDB entry for vxlan0 remote {}",
ip
);
}
}
}
}
}
Err(e) => {
eprintln!("DNS resolution error for {}: {}", vtep_host, e);
}
}
sleep(Duration::from_secs(10)).await;
}
});
// Health check state
let health_status: SharedHealth = Arc::new(Mutex::new(HealthStatus::Healthy));
let health_status_clone = health_status.clone();
let first_ip = format!("{}.{}.{}.1", octets[0], octets[1], octets[2]);
tokio::spawn(async move {
let mut fail_count = 0;
loop {
let output = Command::new("ping")
.args(["-c", "1", "-W", "2", &first_ip])
.output();
match output {
Ok(out) if out.status.success() => {
if fail_count > 3 {
println!(
"Ping to {} successful after {} failures",
first_ip, fail_count
);
}
fail_count = 0;
let mut status = health_status_clone.lock().await;
*status = HealthStatus::Healthy;
}
_ => {
fail_count += 1;
if fail_count >= 3 {
let mut status = health_status_clone.lock().await;
*status = HealthStatus::Unhealthy;
eprintln!(
"Ping to {} failed {} times, marking as Unhealthy",
first_ip, fail_count
);
}
}
}
sleep(Duration::from_secs(10)).await;
}
});
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(health_status.clone()))
.service(health)
})
.bind(("0.0.0.0", 8000))?
.run()
.await
}
I expected to see this happen:
cargo clippy fixes the warnings and doesn't error out.
Instead, this happened:
cargo clippy failed out with above error.
Version
rustc 1.87.0 (17067e9ac 2025-05-09)
binary: rustc
commit-hash: 17067e9ac6d7ecb70e50f92c1944e545188d2359
commit-date: 2025-05-09
host: x86_64-unknown-linux-gnu
release: 1.87.0
LLVM version: 20.1.1
Additional Labels
No response