Основная проблема заключается в том, что вы пытаетесь импортировать класс, но используете синтаксис, который работает только для импорта модуля. В частности, import lib.A
никогда не будет работать, если A
- это класс, определенный в модуле lib.a
(и импортированный в пространство имен верхнего уровня lib
).
Я предлагаю, чтобы вы избегаете использования синтаксиса from _ import _
, если вам это действительно не нужно. Это облегчает решение зависимостей:
lib/a.py
:
import lib.b # note, we're not importing the class B, just the module b!
class A():
def foo(self):
return lib.b.B() # use the class later, with a qualified name
lib/b.py
:
import lib.a # again, just import the module, not the class
class B():
def foo(self):
return lib.a.A() # use another qualified name reference
lib/__init__.py
:
from a import A # these imports are fine, since the sub-modules don't rely on them
from b import B # they can be the public API for the A and B classes
Вы также можете использовать относительный импорт модулей, если вы не хотите, чтобы a
и b
зависели от имени своего пакета lib
.
Это обязательно работать, потому что ни один из классов A или B на самом деле не требует, чтобы другой существовал еще, чтобы быть определенным. Только после того, как они импортируются, экземпляры A должны знать о классе B (и наоборот).
Если один из классов, унаследованный от другого или иным образом используемый экземпляр другого в верхний уровень, вам нужно быть более осторожным, какой модуль был загружен первым, или он может все еще сломаться.
Foo::new(words).split_first()
будет интерпретироваться примерно как
let tmp = Foo::new(words);
let ret = tmp.split_first();
drop(tmp);
ret
Если бы Rust позволил вам сделать это, ссылки в ret
указали бы s> [править: разрешено типом split_first
указывать *] на теперь упавшее значение tmp
. Так что хорошо, что Руст запрещает это. Если бы вы написали эквивалентную однострочную версию в C ++, вы бы молча получили неопределенное поведение.
Написав let
привязку самостоятельно, вы задерживаете отбрасывание до конца области действия, расширяя тем самым область, где эти ссылки безопасны.
Подробнее см. временные времена жизни в справочнике по ржавчине.
* Редактировать: Как указал Jmb , реальная проблема в этом конкретном примере заключается в том, что тип
fn split_first(&'a self) -> &'a str
недостаточно конкретен, и лучшим решением является уточните тип до:
fn split_first<'b>(&'b self) -> &'a str
, что может быть сокращено:
fn split_first(&self) -> &'a str
Это передает предполагаемую гарантию того, что возвращаемые ссылки не указывают на Foo<'a>
(только на строку сам по себе).
Краткий ответ: удалить время жизни 'a
для параметра self
параметра split_first
: fn split_first(&self) -> &'a str
( детская площадка ).
Длинный ответ:
Когда вы пишете этот код:
struct Foo<'a> {
part: &'a str,
}
impl<'a> Foo<'a> {
fn new(s: &'a str) -> Self {
Foo { part: s }
}
}
Вы говорите компилятору, что все Foo
экземпляры связаны с некоторым временем жизни [ 118], которое должно быть равно или короче времени жизни строки, передаваемой в качестве параметра в Foo::new
. Это время жизни 'a
может отличаться от времени жизни каждого экземпляра Foo
. Затем вы пишете:
let words = "Sometimes think, the greatest sorrow than older";
Foo::new(words)
Компилятор делает вывод, что время жизни 'a
должно быть равно или короче времени жизни words
. Запретив любые другие ограничения, компилятор будет использовать время жизни words
, которое равно 'static
, поэтому оно действует в течение всего срока службы программы.
Когда вы добавляете определение split_first
:
fn split_first(&'a self) -> &'a str
Вы добавляете дополнительное ограничение: вы говорите, что 'a
также должно быть равно или короче времени жизни self
, Поэтому компилятор будет принимать меньшее время жизни words
и время жизни временного экземпляра Foo
, то есть время жизни временного экземпляра. @ Ответ AndersKaseorg объясняет, почему это не работает.
Удаляя время жизни 'a
для параметра self
, я декоррелирую 'a
из времени жизни временного, поэтому компилятор может снова сделать вывод, что 'a
- это время жизни words
, которое достаточно долго для работы программы.