Можно ли отобразить Rc < T > чтобы получить Rc < Subpart-of-T & gt ;?

Вот что мы придумали для копирования одного поля в другое для ~ 150_000 записей. Это заняло около 6 минут, но все еще значительно менее ресурсоемким, чем это было бы для создания экземпляра и повторения одного и того же количества объектов ruby.

js_query = %({
  $or : [
    {
      'settings.mobile_notifications' : { $exists : false },
      'settings.mobile_admin_notifications' : { $exists : false }
    }
  ]
})

js_for_each = %(function(user) {
  if (!user.settings.hasOwnProperty('mobile_notifications')) {
    user.settings.mobile_notifications = user.settings.email_notifications;
  }
  if (!user.settings.hasOwnProperty('mobile_admin_notifications')) {
    user.settings.mobile_admin_notifications = user.settings.email_admin_notifications;
  }
  db.users.save(user);
})

js = "db.users.find(#{js_query}).forEach(#{js_for_each});"
Mongoid::Sessions.default.command('$eval' => js)
3
задан Shepmaster 16 January 2019 в 01:33
поделиться

3 ответа

Как сказал ThatOneDeveloper в их ответ , он не поддерживается стандартной библиотекой. Вы можете реализовать такую ​​функцию самостоятельно:

use std::ops::Deref;

#[derive(Clone)]
struct RcSome<T>(Rc<Option<T>>);

impl<T> RcSome<T> {
    fn from(rc: &Rc<Option<T>>) -> RcSome<T> {
        RcSome(rc.clone())
    }
}

impl<T> Deref for RcSome<T> {
    type Target = T;
    fn deref(&self) ->  &T {
        self.0.as_ref().as_ref().unwrap()
    }
}

Тогда вы можете сделать это

let rc_option: Rc<Option<T>> = Rc::new(Some(value));
let ok_value: RcSome<T> = RcSome::from(&rc_option);

Обратите внимание, что это вызовет панику, если rc_option содержит None. Но ok_value теперь будет вести себя как Rc<T> - то есть вы можете clone() это сделать и сделать ok_value.some_method_of_T(). ok_value также не разделяет время жизни с rc_option, поэтому может пережить его.

0
ответ дан Shepmaster 16 January 2019 в 01:33
поделиться

Не с Rc.

Это невозможно с Rc просто из-за его структуры памяти:

//  Equivalence:
struct RcBox<T> {
    strong: AtomicUsize,
    weak: AtomicUsize,
    data: T,
};

struct Rc<T> {
    ptr: *const RcBox<T>,
};

Следовательно, ожидается, что счетчики будут находиться рядом с T, и поэтому вы не можете разделить счетчики между двумя отдельные элементы.


Возможны альтернативы.

С точки зрения структуры памяти, вполне приемлемо создать альтернативу FlexRc:

struct Counters {
    strong: AtomicUsize,
    weak: AtomicUsize, // if support for FlexWeak is desired.
    ptr: *mut (),
    drop: fn(*mut ()),
}

struct FlexRc<T> {
    counters: *mut Counters,
    ptr: *const T,
}

И это теоретически может позволить отображение ... однако создание безопасного интерфейса поверх это может быть нелегко.

Как запретить пользователю возвращать несвязанный срок жизни в map? Достаточно ли того, чтобы срок службы обратной ссылки превышал срок службы flex, чтобы быть безопасным?

fn fool(flex: FlexRc<Option<i32>>) -> FlexRc<i32> {
    let i = 3;
    flex.map(|_| &i)
}
0
ответ дан Matthieu M. 16 January 2019 в 01:33
поделиться

Короткий ответ

Нет, невозможно создать Rc<T> из Rc<Option<T>>, который оставляет последний все еще существующим. Однако можно создать Rc<&T> из Rc<Option<T>>, оставив при этом последнюю переменную существующей.

Длинный ответ

Если вы пытаетесь создать новый Rc<T>, которому принадлежит T внутри Rc<Option<T>>, вам придется использовать оригинал Rc<Option<T>>. Вы также не можете иметь несколько экземпляров Rc<Option<T>>, потому что тогда вы перемещаете общее значение, пока еще существуют указатели, что очень небезопасно.

Но есть способ сделать это безопасно! Используя Rc::try_unwrap, вы можете попытаться переместить значение наружу, но это вернет ошибку, если существует несколько экземпляров оригинала Rc. Имейте в виду, что вы также должны разобраться со сценарием, в котором Option<T> заканчивается None.

Вот пример этого:

let rc_option: Rc<Option<T>> = Rc::new(Some(value));

match Rc::try_unwrap(rc_option) {
    Ok(option) => {
        match option {
            Some(t) => {
                let ok_value: Rc<T> = Rc::new(t);
                // Do something with ok_value
            }
            None => {
                // Do something else here
            }
        }
    }
    Err(rc_option) => {
        // There are multiple owners, do something else here
    }
}

Если вы хотите сохранить оригинал, вы можете сделать это:

match &*rc_option {
    Some(ref t) => {
        let ok_ref: Rc<&T> = Rc::new(t);
    }
    None => { /* Do something else, there's no internal value */ }
}

РЕДАКТИРОВАТЬ: Как хронический упомянуто, обратите внимание, что ok_ref не может пережить rc_option (потому что это ссылка на rc_option), что, возможно, не то, что вы хотите, чтобы.

0
ответ дан ThatOneDeveloper 16 January 2019 в 01:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: