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

use std::convert::TryInto;

fn as_byte<T>(source: T) -> u8
where
    T: TryInto<u8>,
    T: PartialOrd<u8>,
{
    if source < 0 {
        return 0;
    }
    if source > 255 {
        return 255;
    }

    source.try_into().unwrap_or(0)
}

ฉันคิดว่ามันสมเหตุสมผลที่จะ จำกัด ข้อมูลทั่วไปของฉันกับสิ่งที่ฉันต้องการจะทำจริง ๆsourceดังนั้นฉันจึงประกาศว่าฉันต้องการเปรียบเทียบกับ a u8และสามารถแปลงเป็นได้u8(ซึ่งไม่ควรล้มเหลวถ้าฉันจับ ค่านอกช่วงก่อน)

ถ้าผมเรียกas_byte(1234i32)มันจะล้มเหลวเนื่องจากมีการดำเนินการใด ๆสำหรับPartialOrd<u8> i32แม้ว่าจะมีการนำไปใช้ แต่สิ่งนี้จะล้มเหลวเนื่องจากsourceถูกย้ายในการเปรียบเทียบครั้งแรกและถูกนำมาใช้หลังจากการย้ายในการตรวจสอบครั้งที่สอง

ถ้าฉันผ่านsourceการอ้างอิงเหมือนas_byte(&1234i32)มันจะยิ่งแย่ลง ตอนนี้TryInto<u8>trait ไม่ได้ถูกนำมาใช้เช่นกัน เนื่องจากไม่มีFrom<&i32>สำหรับu8.

ฉันเข้าใจว่าทำไมปัญหาเหล่านี้ถึงเกิดขึ้น แต่ฉันไม่สามารถหาวิธีแก้ไขได้ ฉันพยายามเปลี่ยนข้อ จำกัด เป็นwhere &'a Tหรือเปลี่ยนพารามิเตอร์เป็นsource: &Tหรือทั้งสองอย่าง นั่นทำให้ฉันมีปัญหาที่คล้ายกันซึ่งไม่ได้ใช้งานคุณลักษณะสำหรับประเภทอ้างอิง สิ่งนี้ทำให้ฉันรู้สึกเหมือนฉันไม่เข้าใจแนวคิดของการยืม/อ้างอิงอย่างถูกต้องตั้งแต่แรก

ข้อผิดพลาดในกระบวนการคิดของฉันอยู่ที่ไหน

  1. หากPartialOrd<u8> for i32มีการประกาศวิธีนี้ควรมีลักษณะอย่างไร

  2. เมื่อรู้ว่าPartialOrd<u8> for i32ไม่มีอยู่ ฉันควรจำกัดพารามิเตอร์ type อย่างไร เหตุใดฉันจึงได้รับข้อผิดพลาด "ลักษณะ `Foo` ไม่ถูกนำไปใช้กับ `&mut T`" แม้ว่า T จะใช้คุณสมบัตินี้ เกี่ยวข้องและค่อนข้างจะตอบคำถามของฉัน - แต่ฉันสามารถทำได้ (และอาจไม่ควร) ใช้คุณลักษณะสำหรับประเภทดั้งเดิม / std ได้ไหม

ตอบ

คุณสามารถเขียนas_byte()แบบนี้:

fn as_byte<T>(source: T) -> u8
where
    T: TryInto<u8> + From<u8> + PartialOrd,
{
    if source < 0.into() {
        return 0;
    }
    if source > 255.into() {
        return 255;
    }

    source.try_into().unwrap_or_else(|_| unreachable!())
}

สนามเด็กเล่น

เนื่องจากu8มีขนาดเล็กมาก จึงควรแปลง 0 และ 255 เป็นTแล้วเปรียบเทียบมากกว่าวิธีอื่น ความต้องการPartialOrd<u8>ทำให้as_bytesคอมไพล์แยกกัน แต่ไม่ใช่เมื่อเรียกใช้ด้วยประเภทจำนวนเต็มคอนกรีต เนื่องจากปัจจุบันประเภทจำนวนเต็มของ Rust ไม่ได้ทำการเปรียบเทียบกับประเภทที่มีความกว้างต่างกัน คุณจึงต้องแปลงตัวถูกดำเนินการทั้งสองเป็นประเภทเดียวกัน

ฉันไม่แน่ใจว่าทำไมคุณถึงมีปัญหากับsourceการย้าย - PartialOrdกำหนดการเปรียบเทียบบน&selfและ&rhsดังนั้นจึงไม่ควรทำให้เกิดการย้าย