Sie haben Recht vor ut dass:
There is no reason for the reference to live as long as the factory, it only needs to live as long as the object the factory is creating (the factory itself doesn't store a reference to the string).
Aber die gebunden auf call_phone
sagt etwas anderes
fn call_phone<'a, P: Phone, F: Factory<'a, P>>(f: F) { ... }
Dieser Code sagt, dass es ein einziges Leben für die ganze Fabrik, die für jedes Telefon verwendet wird. Sie wollen etwas anderes, möchte man sagen, dass f
eine gute Fabrik für ist jede Lebensdauer:
fn call_phone<..., F: for<'a> Factory<'a, ...>>(f: F) { ... }
Das andere Problem ist, dass in der Factory
Charakterzug Definition:
trait Factory<'a, P: Phone> {
fn new_phone(&self, ms: &'a str) -> P;
}
Es gibt nichts Binde Lebensdauer ist von P
bis ms
. Die Eigenschaftsdefinition ermöglicht es dem zurückgegebenen Telefon, den String zu überleben, was für die Implementierung IPhone
definitiv verboten sein sollte! Also, um es zu beheben, fügen wir ein Leben lang Parameter an das Phone
Merkmal:
trait Phone<'a> {
fn call(&self);
}
Aber es gibt noch ein Problem. Wir können nicht wirklich, dass die Unterzeichnung schreiben:
fn call_phone<P: ???, F: for<'a> Factory<'a, P<'a>>(f: F) { ... }
Da wir P
sein, nicht eine Art wollen, sondern eine Familie von Typen (genauer gesagt, ein lifetime → type
Konstruktor). Denken Sie daran, dass das Telefon in jeder Iteration der Schleife einen anderen Typ hat (da die Lebensdauer ein Teil eines Typs ist und die Lebensdauern in verschiedenen Iterationen von Schleifen unterschiedlich sind).
Fähigkeit, eine solche Unterschrift auszudrücken ist für die Zukunft Rust geplant, aber jetzt haben wir eine Abhilfe machen und das Telefon zugeordneten Art von Factory
Eigenschaft zu machen:
trait Phone<'a> {
fn call(&self);
}
struct IPhone<'a> {
my_str: &'a str
}
impl<'a> Phone<'a> for IPhone<'a> {
fn call(&self) {
println!("{}", self.my_str);
}
}
trait Factory<'a> {
type Output: Phone<'a>;
fn new_phone(&self, ms: &'a str) -> Self::Output;
}
struct IPhoneFactory;
impl<'a> Factory<'a> for IPhoneFactory {
type Output = IPhone<'a>;
fn new_phone(&self, ms: &'a str) -> IPhone<'a> {
IPhone {
my_str: ms
}
}
}
fn call_phone<F: for<'a> Factory<'a>>(f: F) {
for i in 0..10 {
let s = i.to_string();
let p = f.new_phone(&s);
p.call();
}
}
fn main() {
call_phone(IPhoneFactory);
}
Assoziierte Typ der Fabrik erlaubt produzieren Sie nur eine Art von Produkt, das ist vielleicht das, was Sie wollten.Wenn Sie verschiedene Implementierungen von Factory
wollen verschiedene Output
s haben, können Sie dies zu erreichen, indem Phantomtypen mit:
trait Phone<'a> {
type Phantom;
fn call(&self);
}
enum IPhonePhantom {}
struct IPhone<'a> {
my_str: &'a str
}
impl<'a> Phone<'a> for IPhone<'a> {
type Phantom = IPhonePhantom;
fn call(&self) {
println!("{}", self.my_str);
}
}
trait Factory<'a, Selector> {
type Output: Phone<'a, Phantom=Selector>;
fn new_phone(&self, ms: &'a str) -> Self::Output;
}
struct MyFactory;
impl<'a> Factory<'a, IPhonePhantom> for MyFactory {
type Output = IPhone<'a>;
fn new_phone(&self, ms: &'a str) -> IPhone<'a> {
IPhone {
my_str: ms
}
}
}
fn call_phone<Selector, F: for<'a> Factory<'a, Selector>>(f: F) {
for i in 0..10 {
let s = i.to_string();
let p = f.new_phone(&s);
p.call();
}
}
fn main() {
call_phone::<IPhonePhantom, _>(MyFactory);
}
Der Phantom
zugehörige Typ auf das Phone
Merkmale nicht unbedingt erforderlich sind, ist es nur notwendig, um den Telefontyp zu binden zu seinem Phantom-Typ und um sicherzustellen, dass Factory
Implementierer nicht lügen.
Das war sehr hilfreich und aufschlussreich, danke. –