避免编写同步性状的重复代码及其异步方的最佳实践
我在编写一个小型自定义协议时偶然发现了这个问题,该协议同时扩展了 std :: io :: write
和期货:: io :: io :: asyncwrite
(以及读取特征)。我注意到编写了很多重复的代码,因为该协议的行为是否完全相同,无论它是否是异步。这在详尽的测试中尤其不错,在详尽的测试中,我使用两个版本的光标
,并且需要测试这两个变体一起工作。
有没有办法弥合两种特征?也许是一个宏,它仅通过遗漏了.await和异步部件(如果适用)来生成两个变体。
参考代码实现
这有点麻烦。
impl<W> ProtoWriteExt for W
where
W: Write,
{
fn proto_write<T>(&mut self, value: &T) -> Result<(), ProtocolError>
where
T: Serialize,
{
// lots of code...
// Only these calls change
self.write_all(&bytes)?;
// ...
}
}
#[async_trait]
impl<W> ProtoAsyncWriteExt for W
where
W: AsyncWrite + Unpin + Send + Sync,
{
async fn proto_write<T>(&mut self, value: &T) -> Result<(), ProtocolError>
where
T: Serialize + Sync,
{
// Same code as above...
// Only these calls change
self.write_all(&bytes).await?;
// ...
}
}
参考测试
将会有更多类似的测试,我也必须针对阻止版本测试非阻止版本。
/// Writing a primitive value and then reading it results in an unchanged value.
#[test]
fn transfers_primitive_correctly() -> Result<(), ProtocolError> {
let expected = 42;
let mut cursor = std::io::Cursor::new(Vec::<u8>::new());
cursor.proto_write(&expected)?;
cursor.set_position(0);
let result: i32 = cursor.proto_read()?;
assert_eq!(expected, result);
Ok(())
}
/// Writing a primitive value and then reading it results in an unchanged value.
#[tokio::test]
async fn async_transfers_primitive_correctly() -> Result<(), ProtocolError> {
let expected = 42;
let mut cursor = futures::io::Cursor::new(Vec::<u8>::new());
cursor.proto_write(&expected).await?;
cursor.set_position(0);
let result: i32 = cursor.proto_read().await?;
assert_eq!(expected, result);
Ok(())
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
reqwest
板条板在异步代码中进行所有繁重的举重,并且在阻止方法仅在异步期货上阻止。所以 and /code> (基于tokio-postgres
板条箱)。
有一个 keyword generic instirive ,这将使您可以通过
async ness,但仅是早期阶段(另请参见 lang Team会议文档)。
您可能可以通过宏来做到这一点,但这很难:同步版本必须调用同步方法,而异步版本将需要以某种方式调用异步方法。我相信这是可能的,但是我不知道提供的板条箱。
The
reqwest
crate does all heavy lifting in async code, and within the blocking methods just blocks on the async futures. So domongodb
andpostgres
(based on thetokio-postgres
crate).There is a keyword generics initiative that will allow you to be generic over
async
ness, but it is only early stages (see also the lang team meeting document).You probably can do that by a macro, but it is pretty hard: the sync version will have to call sync methods while the async version will need to call async methods somehow. I believe this is possible, however I don't know a crate that provides that.
重复
板条箱可以用目标替代品创建代码的重复项。这在您的测试中可能特别有用,这可能是这样的:
这应该扩展到您拥有的测试代码。
我不愿意建议您使用
重复
为您的Inpland
的s,因为在我看来它们太不同了,无法为重复< /代码>。
我首先要探索重构代码的可能性,以便它们只是调用相同的功能。
但是,如果不可能,仍然可以使用
重复
。The
duplicate
crate can create duplicates of code with targeted substitutions in it.This could be especially useful in your tests, which could look like this:
This should expand to the test code you have.
I'm hesitant to advise you to use
duplicate
for yourimpl
's as it seems to me that they are too different to be a good case forduplicate
.I would first explore the possibility of refactoring the code, such that they simply call the same function.
However, if that is not possible,
duplicate
could still be used.