A std::cell::Ref
estrutura em Rust é definida da seguinte forma:
pub struct Ref<'b, T: ?Sized + 'b> {
// NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a
// `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
// `NonNull` is also covariant over `T`, just like we would have with `&T`.
value: NonNull<T>,
borrow: BorrowRef<'b>,
}
O // NB
comentário (presumo Nota bene / Nasty Bug ou algo assim?) implica que a seguinte definição não funcionaria, porque seria uma noalias
violação (eles se referem aos atributos LLVM no backend?):
pub struct Ref2<'b, T: ?Sized + 'b> {
value: &'b T,
borrow: BorrowRef<'b>,
}
Não entendo esse ponto, pois tive a impressão de que a semântica não lexical do tempo de vida foi devidamente preservada na geração de código. Caso contrário, o seguinte exemplo simples (que obviamente compila) também seria ilegal, certo?:
struct Foo<'a> {
v: &'a i32,
}
fn foo(x: &mut i32) {
let f = Foo { v: x };
*x = 5; // value modified while the `noalias` f.v pointer is still in scope
}
Alguém com mais conhecimento sobre os internos poderia me esclarecer sobre isso? Temo estar entendendo mal algo crítico aqui, levando a possíveis problemas em meu próprio código inseguro.
Os tempos de vida não lexicais nunca foram uma propriedade da geração de código. Eles eram puramente uma propriedade de verificação de empréstimo, e a verificação de empréstimo nunca afeta a geração de código.
O exemplo que você forneceu não é ilegal de acordo com o LLVM. O problema é que
noalias
só se manifesta com parâmetro de função, pois só eles recebem umnoalias
atributo (pelo menos atualmente). Portanto, a única maneira de escrever um código problemático sem código inseguro será:Exceto que não compila, porque você está movendo um arquivo
v
. Portanto, não havia como desencadear uma compilação incorreta.No
RefCell
entanto, podemos fazer isso:Qual seria LLVM UB se
Ref
usasse uma referência.