根据编译目标 OS,Rust 将不同类型的值分配给变量的惯用方式是什么?
What is the idiomatic way in Rust of assigning values of different types to a variable, depending on the compilation target OS?
我正在研究绑定到 Tokio 套接字并管理 TCP 连接的代码库。在生产中,它使用 tokio-vsock
箱绑定到 AF_VSOCK
。
在 Mac 上进行本地开发时,AF_VSOCK
API 不可用,因为没有 hypervisor -> VM
连接 — 它只是 运行 本机使用 cargo run
.
在本地 运行ning 时,我一直在创建标准 tokio::net::TcpListener
struct and in production I have been creating a tokio_vsock::VsockListener
。这两个结构 大部分 可以互换,并公开相同的方法。无论使用哪个结构,其余代码都能完美运行。
到目前为止,我只是保留了两个结构并简单地注释掉了本地不需要的结构——这显然不是“好的做法”。我的代码如下:
#[tokio::main]
async fn main() -> Result<(), ()> {
// Production AF_VSOCK listener (comment out locally)
let mut listener = tokio_vsock::VsockListener::bind(
&SockAddr::Vsock(
VsockAddr::new(
VMADDR_CID_ANY,
LISTEN_PORT,
)
)
)
.expect("Unable to bind AF_VSOCK listener");
// Local TCP listener (comment out in production)
let mut listener = tokio::net::TcpListener::bind(
std::net::SocketAddr::new(
std::net::IpAddr::V4(
std::net::Ipv4Addr::new(0, 0, 0, 0)
),
LISTEN_PORT as u16,
)
)
.await
.expect("Unable to bind TCP listener");
// This works regardless of which listener is used
let mut incoming = listener.incoming();
while let Some(socket) = incoming.next().await {
match socket {
Ok(mut stream) => {
// Do something
}
}
}
Ok(())
}
我尝试使用 cfg!()
宏并将 target_os
设置为条件,但编译器抱怨两个 bind()
方法返回的类型不匹配。
我的问题是:在 Rust 中用不同类型[=]分配不同值的惯用方式是什么 到变量,取决于编译目标 OS?
有多种选择。关于 stdlib 本身的用法,最简单和最常见的是使用 #[cfg]
宏(而不是 cfg!()
。以下代码片段阐明了它的用法:
struct Linux;
impl Linux {
fn x(&self) -> Linux {
println!("Linux");
Linux
}
}
struct Windows;
impl Windows {
fn x(&self) -> Windows {
println!("Windows");
Windows
}
}
fn main() {
#[cfg(not(target_os = "linux"))]
let obj = {
let obj = Linux;
obj
};
#[cfg(not(target_os = "windows"))]
let obj = {
let obj = Windows;
obj
};
let _ = obj.x();
}
在你的情况下,这将是(未经测试):
#[tokio::main]
async fn main() -> Result<(), ()> {
#[cfg(target_os = "linux")]
let mut listener = tokio_vsock::VsockListener::bind(
&SockAddr::Vsock(
VsockAddr::new(
VMADDR_CID_ANY,
LISTEN_PORT,
)
)
)
.expect("Unable to bind AF_VSOCK listener");
#[cfg(target_os = "Mac")]
let mut listener = tokio::net::TcpListener::bind(
std::net::SocketAddr::new(
std::net::IpAddr::V4(
std::net::Ipv4Addr::new(0, 0, 0, 0)
),
LISTEN_PORT as u16,
)
)
.await
.expect("Unable to bind TCP listener");
...
}
检查 https://doc.rust-lang.org/reference/conditional-compilation.html 可用条件,包括功能标志,以防 target_os 不够适用。
#[cfg]
和 cfg!()
的主要区别是 cfg!
不删除代码。根据它的文档:“cfg! 与#[cfg] 不同,它不会删除任何代码,只会评估为 true 或 false”。因此,在使用 #[cfg]
时出现编译错误更类似于 C/C++ 中的 if-defs 并删除未使用的代码,因此编译器永远不会看到类型不匹配。
我正在研究绑定到 Tokio 套接字并管理 TCP 连接的代码库。在生产中,它使用 tokio-vsock
箱绑定到 AF_VSOCK
。
在 Mac 上进行本地开发时,AF_VSOCK
API 不可用,因为没有 hypervisor -> VM
连接 — 它只是 运行 本机使用 cargo run
.
在本地 运行ning 时,我一直在创建标准 tokio::net::TcpListener
struct and in production I have been creating a tokio_vsock::VsockListener
。这两个结构 大部分 可以互换,并公开相同的方法。无论使用哪个结构,其余代码都能完美运行。
到目前为止,我只是保留了两个结构并简单地注释掉了本地不需要的结构——这显然不是“好的做法”。我的代码如下:
#[tokio::main]
async fn main() -> Result<(), ()> {
// Production AF_VSOCK listener (comment out locally)
let mut listener = tokio_vsock::VsockListener::bind(
&SockAddr::Vsock(
VsockAddr::new(
VMADDR_CID_ANY,
LISTEN_PORT,
)
)
)
.expect("Unable to bind AF_VSOCK listener");
// Local TCP listener (comment out in production)
let mut listener = tokio::net::TcpListener::bind(
std::net::SocketAddr::new(
std::net::IpAddr::V4(
std::net::Ipv4Addr::new(0, 0, 0, 0)
),
LISTEN_PORT as u16,
)
)
.await
.expect("Unable to bind TCP listener");
// This works regardless of which listener is used
let mut incoming = listener.incoming();
while let Some(socket) = incoming.next().await {
match socket {
Ok(mut stream) => {
// Do something
}
}
}
Ok(())
}
我尝试使用 cfg!()
宏并将 target_os
设置为条件,但编译器抱怨两个 bind()
方法返回的类型不匹配。
我的问题是:在 Rust 中用不同类型[=]分配不同值的惯用方式是什么 到变量,取决于编译目标 OS?
有多种选择。关于 stdlib 本身的用法,最简单和最常见的是使用 #[cfg]
宏(而不是 cfg!()
。以下代码片段阐明了它的用法:
struct Linux;
impl Linux {
fn x(&self) -> Linux {
println!("Linux");
Linux
}
}
struct Windows;
impl Windows {
fn x(&self) -> Windows {
println!("Windows");
Windows
}
}
fn main() {
#[cfg(not(target_os = "linux"))]
let obj = {
let obj = Linux;
obj
};
#[cfg(not(target_os = "windows"))]
let obj = {
let obj = Windows;
obj
};
let _ = obj.x();
}
在你的情况下,这将是(未经测试):
#[tokio::main]
async fn main() -> Result<(), ()> {
#[cfg(target_os = "linux")]
let mut listener = tokio_vsock::VsockListener::bind(
&SockAddr::Vsock(
VsockAddr::new(
VMADDR_CID_ANY,
LISTEN_PORT,
)
)
)
.expect("Unable to bind AF_VSOCK listener");
#[cfg(target_os = "Mac")]
let mut listener = tokio::net::TcpListener::bind(
std::net::SocketAddr::new(
std::net::IpAddr::V4(
std::net::Ipv4Addr::new(0, 0, 0, 0)
),
LISTEN_PORT as u16,
)
)
.await
.expect("Unable to bind TCP listener");
...
}
检查 https://doc.rust-lang.org/reference/conditional-compilation.html 可用条件,包括功能标志,以防 target_os 不够适用。
#[cfg]
和 cfg!()
的主要区别是 cfg!
不删除代码。根据它的文档:“cfg! 与#[cfg] 不同,它不会删除任何代码,只会评估为 true 或 false”。因此,在使用 #[cfg]
时出现编译错误更类似于 C/C++ 中的 if-defs 并删除未使用的代码,因此编译器永远不会看到类型不匹配。