首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免MutexGuard接口中可能未初始化的变量移动?

如何避免MutexGuard接口中可能未初始化的变量移动?
EN

Stack Overflow用户
提问于 2021-10-07 07:38:01
回答 2查看 220关注 0票数 1

以下代码:

代码语言:javascript
复制
  let conversation_model =
    if lsm { CONVMODEL.lock().await } else {
      conv_model_loader()
    };

CONVMODEL.lock().awaitMutexGuard<T>conv_model_loader()T

我需要这两个的通用接口,所以我不能复制粘贴我的代码在两种情况下,因为它将只有不同的类型,任何其他都是一样的。

编辑:

有密码..。(至少我想做的事)

代码语言:javascript
复制
  let (locked, loaded);  // pun not intended
  if lsm {
    locked = CONVMODEL.lock().await;
  } else {
    loaded = conv_model_loader();
  };
  let mut chat_context = CHAT_CONTEXT.lock().await;
  task::spawn_blocking(move || {
    let conversation_model = if lsm { &*locked } else { &loaded };

但我很生气是因为

代码语言:javascript
复制
use of possibly-uninitialized variable: `locked`\nuse of possibly-uninitialized `locked`

因此,问题是如何使用MutexGuard与接口&T,但如何在spawn_blocking内部使用它,以及如何使用#[async_recursion]

编辑:

代码语言:javascript
复制
  let (mut locked, mut loaded) = (None, None);
  if lsm {
    locked = Some( CONVMODEL.lock().await );
  } else {
    loaded = Some( conv_model_loader() );
  };
  let mut chat_context = CHAT_CONTEXT.lock().await;
  task::spawn_blocking(move || {
    let (lock, load);
    let conversation_model =
      if lsm {
        lock = locked.unwrap();
        &*lock
      } else {
        load = loaded.unwrap();
        &load
      };

下面的代码正在工作,但实际上非常难看的XD (我想知道是否有可能简化这段代码)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-10-07 09:17:00

每当您对某个值有一些选择时,您都希望访问enum。例如,在铁锈中,我们不做像let value: T; let is_initialized: bool;这样的事情,我们做Option<T>

您可以选择两个值,要么是已获得的互斥量,要么是直接值。这通常被称为“任一个”,并且有一个流行的锈菌箱包含这种类型:Either。对你来说,这看起来可能是:

代码语言:javascript
复制
    use either::Either;

    let conv_model = if lsm {
        Either::Left(CONVMODEL.lock().await)
    } else {
        Either::Right(conv_model_loader())
    };

    tokio::task::spawn_blocking(move || {
        let conversation_model = match &conv_model {
            Either::Left(locked) => locked.deref(),
            Either::Right(loaded) => loaded,
        };

        conversation_model.infer();
    });

这个类型以前存在于标准库中,但由于它不经常被使用,所以它被删除了,因为创建一个更具有描述性的特定域类型是相当微不足道的。我同意这一点,你可以这样做:

代码语言:javascript
复制
pub enum ConvModelSource {
    Locked(MutexGuard<'static, ConvModel>),
    Loaded(ConvModel),
}

impl Deref for ConvModelSource {
    type Target = ConvModel;
    
    fn deref(&self) -> &Self::Target {
        match self {
            Self::Locked(guard) => guard.deref(),
            Self::Loaded(model) => model,
        }
    }
}

// ...

let conv_model = if lsm {
    ConvModelSource::Locked(CONVMODEL.lock().await)
} else {
    ConvModelSource::Loaded(conv_model_loader())
};

tokio::task::spawn_blocking(move || {
    conv_model.infer();
});

这是更有表现力的,并将“如何填充这个”从它使用的地方移开。

在常见情况下,您确实希望使用user4815162342展示的更简单的方法。您将存储其中一个临时成员,形成对它的引用(知道您刚刚初始化它),并将其交回。

然而,这并不适用于spawn_blocking。引用的生命周期是暂时的--将这样的引用传递给生成的任务是一个悬空的引用。

这就是为什么错误消息(“借来的值活得不够长”和“参数要求locked是为'static借来的”)引导您沿着试图将lockedloaded移动到闭包以位于其最后休息位置的路径,然后形成一个引用。那推荐信就不会悬空了。

但这意味着您将一个可能未初始化的值移到闭包中。铁锈不明白您正在使用相同的检查来查看填充了哪个临时值。(您可以想象第二次检查!lsm时会出现错误,现在切换到了。)

最终,您必须将值的来源移到生成的任务(闭包)中,以便形成具有可用生存期的引用。enum的使用基本上是对您的布尔案例进行编码,检查一些Rust理解的内容,并将其自然展开。

票数 3
EN

Stack Overflow用户

发布于 2021-10-07 07:48:20

您可以从两者中提取&mut T并使用它。下面这样的东西应该能起作用:

代码语言:javascript
复制
let (locked, loaded);  // pun not intended
let conversation_model = if lsm {
    locked = CONVMODEL.lock().await;
    &mut *locked
} else {
    loaded = conv_model_loader();
    &mut loaded
};
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69477156

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档