Пришел к следующему выводу, во всех случаях, когда все под вашем контролем, и вы можете уверено гарантировать правильность использования указателей, (например когда это часть внутренней кухни вашей библиотеки)
использование сырых *const T and *mut T не только вредно, но и полезно. 
особенно это касается static переменных.
а вот внешние интерфейсы, где возможны не подконтрольные вариации, наоборот использование *const T and *mut T лучше избегать
вот такое “открытие” нуба.
было бы интересно услышать возражения,
если у кого нибудь есть какие-то подобные, очевидно-не очевидные, наблюдения… делитесь
Весь мой опыт толсто намекает мне, что чего-то нетривиальное уверенно гарантировать, если компилятор (или еще что автоматическое) меня не подстраховывает, я не особо могу. Даже если речь идет чисто о внутренностях какой-то моей библиотеки. Использование сырых указателей лишает львиной доли преимуществ ржавчины, так что если без них реально решить поставленную задачу, то лучше их и не расчехлять лишний раз.
использование сырых *const T and *mut T … полезно … особенно это касается static переменных.
Тут хочется подробнее про решаемую задачу и сравнение с безопасным вариантом.
дополню:
static - поскольку они существуют все время работы программы И это
либо однопоточное приложение (что бывает часто)
либо этот статик упрятан в локальное хранилище потока, если приложение многопоточное.
просто я написал код и наличие проверок вынуждало делать кучу оберток, которые просто не нужны… лишком все тривиально.
переписал на указателях - код существенно упростился.
А примеры кода до и после можешь показать? Обычно меня всякие вспомогательные функции и ? выручали, но может и правда в этой ситуации оправдано.
всё. тема инициализации static стала не актуальной.
решение, c минимальными издержками, найдено…
# ![feature(untagged_unions)]
union Static<T> {
uninited: (),
inited: T,
}
// Resources:
static mut FOO: Static<FOO_TYPE> = Static{ uninited: () };
fn main() {
let foo = get_FOO_TYPE(...);
unsafe { FOO = Static{ inited: foo }; }
}
У меня есть такой код получение/установки значений полей Toml, который я не знаю как реализовать без сырых указателей:
fn set_toml(toml: &mut Value, path: impl AsRef<str>, value: Value) -> Result<(), Error> {
let mut cursor = toml as *mut Value;
let path_vec = path.as_ref()
.split(unsafe { SEPARATOR })
.collect::<Vec<&str>>();
let mut next_iter = path_vec.iter().skip(1);
// проверяем структуру
for (current, next) in path_vec.iter().map(|current| (current, next_iter.next())) {
// Если есть следующий элемент то создаем "папку"
if let Some(next) = next {
if let Ok(index) = current.parse::<usize>() {
if let Value::Array(ref mut array) = unsafe { &mut *cursor } {
let empty = if let Ok(_) = next.parse::<usize>() {
Value::Array(Vec::new())
} else {
Value::Table(BTreeMap::new())
};
for _ in array.len()..index + 1 {
array.push(empty.clone());
}
cursor = &mut array[index] as *mut Value;
} else {
return Err(ErrorKind::RedefineTypeError(current.to_string()).into());
}
} else {
if let Value::Table(ref mut table) = unsafe { &mut *cursor } {
if !table.contains_key(¤t.to_string()) {
if let Ok(_) = next.parse::<usize>() {
table.insert(current.to_string(), Value::Array(Vec::new()));
} else {
table.insert(current.to_string(), Value::Table(BTreeMap::new()));
}
}
cursor = table.get_mut(¤t.to_string()).unwrap() as *mut Value;
} else {
return Err(ErrorKind::RedefineTypeError(current.to_string()).into());
}
}
// Если следующего элемента нет создаем "файл"
} else {
match unsafe { &mut *cursor } {
Value::Array(ref mut array) => match current.parse::<usize>() {
Ok(index) => {
for _ in array.len()..index + 1 {
array.push(value.clone());
}
if !array[index].same_type(&value) {
return Err(ErrorKind::RedefineTypeError(current.to_string()).into());
}
array[index] = value.clone();
}
Err(err) => {
return Err(ErrorKind::IndexParseError(current.to_string(), err).into());
}
},
Value::Table(ref mut table) => {
if let Some(old) = table.get(¤t.to_string()) {
if !old.same_type(&value) {
return Err(ErrorKind::RedefineTypeError(current.to_string()).into());
}
}
table.insert(current.to_string(), value.clone());
}
_ => {
return Err(ErrorKind::RedefineTypeError(current.to_string()).into());
}
}
}
}
Ok(())
}
fn get_toml<'a>(toml: &'a Value, path: impl AsRef<str>) -> Result<Option<&'a Value>, Error> {
let mut cursor = toml;
let path_vec = path.as_ref()
.split(unsafe { SEPARATOR })
.collect::<Vec<&str>>();
let mut next_iter = path_vec.iter().skip(1);
for (current, next) in path_vec.iter().map(|current| (current, next_iter.next())) {
if let Some(_) = next {
match cursor {
Value::Array(array) => {
if let Ok(index) = current.parse::<usize>() {
if index < array.len() {
cursor = &array[index];
} else {
return Ok(None);
}
} else {
return Ok(None);
}
}
Value::Table(table) => {
if let Some(index) = table.get(¤t.to_string()) {
cursor = index;
} else {
return Ok(None);
}
}
_ => {
return Ok(None);
}
}
} else {
match cursor {
Value::Array(array) => {
if let Ok(index) = current.parse::<usize>() {
if index < array.len() {
return Ok(Some(&array[index]));
} else {
return Ok(None);
}
} else {
return Ok(None);
}
}
Value::Table(table) => {
if let Some(index) = table.get(¤t.to_string()) {
return Ok(Some(index));
} else {
return Ok(None);
}
}
_ => {
return Ok(None);
}
}
}
}
Ok(None)
}
Спасибо. А могло быть такое, что старая версия компилятора ругалась borrow-checker’ом?
А нет!
Не понимаю почему в плейграунде этот код компилируется, но у меня такая ошибка:
error[E0499]: cannot borrow `cursor.0` as mutable more than once at a time
--> config/src/lib.rs:142:37
|
142 | if let Value::Array(ref mut array) = cursor {
| ^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop
...
201 | }
| - mutable borrow ends here
error[E0506]: cannot assign to `cursor` because it is borrowed
--> config/src/lib.rs:151:21
|
142 | if let Value::Array(ref mut array) = cursor {
| ------------- borrow of `cursor` occurs here
а что это за магия:
if let Value::Array(ref mut array) = { cursor }
vs
if let Value::Array(ref mut array) = cursor
Это перемещение содержимого cursor в неявную временную переменную.
В принципе можно написать так:
let t = cursor;
if let Value::Array(ref mut array) = t {
//...
cursor = &mut array[index];
}
или немного короче:
if let Value::Array(ref mut array) = {let t = cursor; t} {
//...
cursor = &mut array[index];
}
Теперь array заимствует не cursor а временную переменную t. И мы можем записать новое значение в cursor.
Вот небольшая статья про эту магию:
https://bluss.github.io/rust/fun/2015/10/11/stuff-the-identity-function-does/
Спасибо.
Я посмотрел 2018-edition это исправляет.