Вот что мы придумали для копирования одного поля в другое для ~ 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)
Как сказал 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
, поэтому может пережить его.
Это невозможно с 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)
}
Нет, невозможно создать 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
), что, возможно, не то, что вы хотите, чтобы.