Как в структуру в хипе засунуть str без копирования?

Имеется следующий код

extern crate xmltree;

use xmltree::Element;
use std::collections::HashMap;

struct S1<'a> {
    name: &'a str,
}

trait HasName{
    fn print_name(&self);
}

impl<'a> HasName for S1<'a>{
    fn print_name(&self){
        println!("{}", self.name);
    }
}

fn create_obj<'a>(e:&'a Element, map: &'a mut HashMap<i32, Box<HasName>>){
    let n = e.attributes.get("name").unwrap();
    map.insert(1, Box::new(S1{name:n}));
}

fn main() {
    let mut map:HashMap<i32, Box<HasName>> = HashMap::new();
//тут чтение внешнего xml и вызов create_obj
}

Допустим, копию строки создавать не хочется.
Я предполагаю, что внутри main область видимости моей хэшмапы и распарсенного xml. И, как я это понимаю, я указал это дав область видимости: &'a mut HashMap<i32, Box>
То есть, я это понимаю так: Box, которым владеет моя хэшмапа не будет жить дольше хэшмапы. Значит, и структура которую бокс хранит не будет жить дольше. А значит, и str который в структуре не будет жить дольше.
Следовательно, должно же прокатить.
Но компилятор ругается, что не может вывести область видимости имени, которое достается из Element. Хотя, для элемента указана эта область - и она тоже 'a. То есть, должно же компилиться.
Но выдает такое:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:21:26
   |
21 |     let n = e.attributes.get("name").unwrap();
   |                          ^^^
   |
note: first, the lifetime cannot outlive the lifetime 'a as defined on the body at 20:73...
  --> src/main.rs:20:74
   |
20 |   fn create_obj<'a>(e:&'a Element, map: &'a mut HashMap<i32, Box<HasName>>){
   |  __________________________________________________________________________^ starting here...
21 | |     let n = e.attributes.get("name").unwrap();
22 | |     map.insert(1, Box::new(S1{name:n}));
23 | | }
   | |_^ ...ending here
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:21:13
   |
21 |     let n = e.attributes.get("name").unwrap();
   |             ^^^^^^^^^^^^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected std::boxed::Box<HasName + 'static>, found std::boxed::Box<HasName>)
  --> src/main.rs:22:19
   |
22 |     map.insert(1, Box::new(S1{name:n}));
   |                   ^^^^^^^^^^^^^^^^^^^^

Попробуйте вот так:

fn create_obj<'a,'b>(e:&'a Element, map: &'b mut HashMap<i32, Box<HasName+'a>>)

Конструкция Trait+'t позволяет задать время жизни объекта для которого реализован типаж Trait. Второе вж 'b нужно чтобы Раст не думал что данные, передаваемые в функцию, заимствуют друг друга.

Приятного знакомства с borrowchecker-ом :slight_smile:

1 лайк

Откомпилилось!!!

Где вы прочитали, что можно Конструкция Trait+'t ? Я вроде всю книгу вдоль и поперек исчитал уже до дыр - и там такого не было. Видимо, пропустил, все же.

Второе вж 'b нужно чтобы Раст не думал что данные, передаваемые в функцию, заимствуют друг друга.

Вроде, компилится и без 'b, хотя вызвать еще не пытался.

Приятного знакомства с borrowchecker-ом :slight_smile:

Да, спасибо. Чувствую, не последний раз спрашивать приходится.

Сам не помню уже. Тоже в книге этого не нашел.
Вот пара вопросов на stackoverflow на эту тему:


1 лайк

Нашел упоминание о времени жизни трейт-объектов во второй редакции Rust Book:
http://rust-lang.github.io/book/second-edition/ch19-02-advanced-lifetimes.html#lifetimes-in-trait-objects

Да и вся глава рекомендуется к ознакомлению, там довольно интересный случай с временами жизни разбирается.

1 лайк

спасибо, пошел читать.
Этот долбанный borrow checker…никогда еще технология мне не давалась так тяжело.