วิธีการแปลงสตริงเป็น & 'str แบบคงที่


92

ฉันจะแปลง a Stringเป็น a &str? โดยเฉพาะอย่างยิ่งฉันต้องการแปลงเป็นอายุstrการstaticใช้งาน ( &'static str)


ดูเหมือนจะเป็นไปไม่ได้หรือเป็นที่ต้องการ 'staticอายุการใช้งานหมายความว่าสตริงไม่เคยถูกจัดสรรนั่นคือหน่วยความจำรั่วไหล ทำไมคุณถึงต้องการ&'static strแทนที่จะ&'a strเป็นสิ่งที่เหมาะสม'a?

3
มันจะดูเป็นยัง&'a str ไง?
Christoph

ผ่านas_slice. จะช่วยได้ง่ายกว่าถ้าคุณอธิบายปัญหาที่เป็นรูปธรรมที่คุณกำลังพยายามแก้ไขและปัญหาที่คุณพบขณะทำเช่นนั้น

นอกจากนี้โปรดทราบSendStrว่าประเภทที่เป็นสตริงที่เป็นเจ้าของหรือสตริงแบบคงที่
Chris Morgan

คำตอบ:


135

อัปเดตสำหรับ Rust 1.0

คุณไม่สามารถได้รับ&'static strจาก a StringเพราะStrings อาจไม่อยู่ตลอดชีวิตของโปรแกรมของคุณและนั่นคือสิ่งที่&'staticหมายถึงอายุการใช้งาน คุณสามารถรับพารามิเตอร์ slice ตามStringอายุการใช้งานของตัวเองได้จากมันเท่านั้น

ในการเปลี่ยนจาก a Stringเป็นชิ้น&'a strคุณสามารถใช้ไวยากรณ์การแบ่งส่วน:

let s: String = "abcdefg".to_owned();
let s_slice: &str = &s[..];  // take a full slice of the string

หรืออีกวิธีหนึ่งคุณสามารถใช้ข้อเท็จจริงที่StringดำเนินการDeref<Target=str>และดำเนินการใหม่อย่างชัดเจน:

let s_slice: &str = &*s;  // s  : String 
                          // *s : str (via Deref<Target=str>)
                          // &*s: &str

มีวิธีอื่นที่ช่วยให้สามารถใช้ไวยากรณ์ที่กระชับยิ่งขึ้นได้ แต่จะใช้ได้ก็ต่อเมื่อคอมไพเลอร์สามารถกำหนดประเภทเป้าหมายที่ต้องการได้ (เช่นในอาร์กิวเมนต์ของฟังก์ชันหรือการผูกตัวแปรที่พิมพ์อย่างชัดเจน) เรียกว่าderef coercionและอนุญาตให้ใช้&ตัวดำเนินการเพียงอย่างเดียวและคอมไพเลอร์จะแทรกจำนวน*s ที่เหมาะสมโดยอัตโนมัติตามบริบท:

let s_slice: &str = &s;  // okay

fn take_name(name: &str) { ... }
take_name(&s);           // okay as well

let not_correct = &s;    // this will give &String, not &str,
                         // because the compiler does not know
                         // that you want a &str

โปรดทราบว่ารูปแบบนี้ไม่ซ้ำกันสำหรับString/ &str- คุณสามารถใช้ได้กับทุกคู่ประเภทที่เชื่อมต่อผ่านDerefเช่นกับCString/ CStrและOsString/ OsStrจากstd::ffiโมดูลหรือPathBuf/ Pathจากstd::pathโมดูล


29
ใน Rust 1.10 แทนที่จะlet s_slice: &str = &s[..];ทำสิ่งนี้:let s_slice: &str = s.as_str();
Shnatsel

3
ในบางครั้งสตริงเดิมไม่เพียงพอเช่นในบล็อกที่ตรงกัน {... } ที่จะนำไปสู่'s' does not live long enough error.
Dereckson

42

คุณสามารถทำได้ แต่เกี่ยวข้องกับการรั่วไหลหน่วยความจำของไฟล์String . นี่ไม่ใช่สิ่งที่คุณควรทำเบา ๆ ด้วยการรั่วไหลของหน่วยความจำStringเรารับประกันได้ว่าหน่วยความจำจะไม่ถูกปลดปล่อย (ดังนั้นการรั่วไหล) ดังนั้นการอ้างอิงถึงวัตถุภายในสามารถตีความได้ว่า'staticมีอายุการใช้งาน

fn string_to_static_str(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

fn main() {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    let s: &'static str = string_to_static_str(s);
}

8
Stringให้การรับประกันว่าตราบใดที่วัตถุยังไม่ถูกทิ้งหน่วยความจำยังคงมีชีวิตอยู่ เนื่องจากmem::forgetรับประกันว่าวัตถุจะไม่ตกหล่นเราจึงรับประกันได้ว่าการอ้างอิงถึงวัตถุที่บรรจุอยู่strจะไม่ถูกต้อง ดังนั้นเราจึงสามารถยืนยันได้ว่าเป็นการ'staticอ้างอิง
oli_obk

1
สิ่งนี้มีประโยชน์อย่างไม่น่าเชื่อสำหรับแอปพลิเคชัน Rust ของฉันซึ่งจำเป็นต้องบังคับให้Stringเข้าร่วม&'static strเพื่อให้โทเค็นที่สร้างจากต้นฉบับStringพร้อมใช้งานในทุกเธรด หากไม่มีสิ่งนี้คอมไพเลอร์ Rust จะบ่นว่าStringอายุการใช้งานของฉันสิ้นสุดลงเมื่อสิ้นสุดฟังก์ชันหลักซึ่งยังไม่ดีพอเพราะไม่มีการ'staticรับประกัน
mmstick

1
@mmstick: ทางออกที่ดีกว่าในกรณีนี้คือการใช้crossbeamและกำหนดขอบเขตเธรด
oli_obk

3
@mmstick: ถ้าคุณใส่แอปพลิเคชันทั้งหมดของคุณลงในขอบเขต crossbeam และสร้างสตริงนอกขอบเขตคุณจะได้รับสิ่งนั้น
oli_obk

1
คำตอบนี้ดีมาก! ทั้งคู่บอกวิธีสร้างสตริสสตริงแบบคงที่และทำให้ฉันมั่นใจว่าจะไม่ทำ! ฉันเลือกที่จะ refactor แอปของฉันเพื่อไม่ให้ใช้ส่วนสตริงแบบคงที่ในหลาย ๆ ที่
Paul Chernoch

24

ในฐานะที่เป็นสนิมรุ่น 1.26 มันเป็นไปได้ที่จะแปลงStringไป&'static strโดยไม่ต้องใช้unsafeรหัส:

fn string_to_static_str(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

สิ่งนี้จะแปลงStringอินสแตนซ์เป็นแบบบรรจุกล่องstrและรั่วไหลทันที ซึ่งจะช่วยปลดปล่อยความจุส่วนเกินทั้งหมดที่สตริงอาจครอบครองอยู่ในขณะนี้

โปรดทราบว่ามีวิธีแก้ปัญหาเกือบตลอดเวลาที่ดีกว่าสำหรับวัตถุที่รั่วไหลเช่นการใช้crossbeamลังถ้าคุณต้องการแบ่งปันสถานะระหว่างเธรด


2

TL; DR: คุณจะได้รับ&'static strจากสิ่งStringที่ตัวมันเองมีอายุการ'staticใช้งาน

แม้ว่าคำตอบอื่น ๆ จะถูกต้องและมีประโยชน์ที่สุด แต่ก็มีขอบกรณี (ไม่เป็นประโยชน์) ซึ่งคุณสามารถแปลง a Stringเป็น a &'static str:

อายุการใช้งานของการอ้างอิงต้องสั้นกว่าหรือเท่ากับอายุการใช้งานของวัตถุที่อ้างอิงเสมอ กล่าวคืออ็อบเจ็กต์ที่อ้างอิงจะต้องมีอายุยืนยาวกว่า (หรือยาวเท่ากัน) มากกว่าการอ้างอิง เนื่องจาก'staticหมายถึงอายุการใช้งานทั้งหมดของโปรแกรมจึงไม่มีอายุการใช้งานที่ยาวนานขึ้น แต่อายุการใช้งานที่เท่าเทียมกันจะเพียงพอ ดังนั้นหากStringอายุการใช้งาน'staticคุณสามารถขอ&'static strข้อมูลอ้างอิงได้

การสร้างstaticประเภทStringเป็นไปได้ในทางทฤษฎีด้วย Rust 1.31 เมื่อconst fnคุณสมบัติดังกล่าวถูกปล่อยออกมา น่าเสียดายที่ฟังก์ชัน const เพียงตัวเดียวที่ส่งคืน a Stringอยู่String::new()ในขณะนี้และยังคงอยู่หลังประตูคุณลักษณะ (ดังนั้นตอนนี้ต้องใช้ Rust ทุกคืน)

ดังนั้นรหัสต่อไปนี้ทำการแปลงที่ต้องการ (โดยใช้ทุกคืน) ... และไม่มีการใช้งานจริงยกเว้นเพื่อความสมบูรณ์ของการแสดงว่าเป็นไปได้ในกรณีนี้

#![feature(const_string_new)]

static MY_STRING: String = String::new();

fn do_something(_: &'static str) {
    // ...
}

fn main() {
    do_something(&MY_STRING);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.