< 返回版块

Orca-bit 发表于 2021-05-04 11:39

Tags:LeetCode

本人刚接触Rust两三天,想通过做题来快速熟悉Rust,在做LeetCode2.两数相加时,参考Rustgym写出了可以通过的代码,但还有一些疑问,先看全部的代码,其中的注释为个人理解,有错误还望指教:

/*
 * @lc app=leetcode.cn id=2 lang=rust
 *
 * [2] 两数相加
 */

// @lc code=start
// Definition for singly-linked list.
// #[derive(PartialEq, Eq, Clone, Debug)]
// pub struct ListNode {
//   pub val: i32,
//   pub next: Option<Box<ListNode>>
//}
// impl ListNode {
//   #[inline]
//   fn new(val: i32) -> Self {
//     ListNode {
//       next: None,
//       val
//     }
//   }
// }

//定义一个别名
pub type ListLink = Option<Box<ListNode>>;

impl Solution {
    pub fn add_two_numbers(l1: Option<Box<ListNode>>, l2: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
        //sum为最后返回的答案
        let mut sum: ListLink = None;
        //p1为l1的借用
        let mut p1: &ListLink = &l1;
        //p2为l2的借用
        let mut p2: &ListLink = &l2;
        //p3为sum的可变借用
        let mut p3: &mut ListLink = &mut sum;
        //carry为进位
        let mut carry = 0;
        while p1.is_some() || p2.is_some() || carry != 0 {
            //val定义为当前位置的结果,初始化为carry
            let mut val = carry;
            //as_ref()将&Option<T>变为Option<&T>
            //n1和n2的类型为&T,这里有些不理解
            if let Some(n1) = p1.as_ref() {
                val += n1.val;
                p1 = &n1.next;
            }
            if let Some(n2) = p2.as_ref() {
                val += n2.val;
                p2 = &n2.next;
            }
            //更新carry的值
            carry = val / 10;
            //解引用p3
            *p3 = Some(Box::new(ListNode::new(val % 10)));
            //更新p3,更新后实际上为None
            p3 = &mut p3.as_mut().unwrap().next;
        }
        sum
    }
}
// @lc code=end

其中有一部分不是很理解:

            //as_ref()将&Option<T>变为Option<&T>
            //n1和n2的类型为&T,这里有些不理解
            if let Some(n1) = p1.as_ref() {
                val += n1.val;
                p1 = &n1.next;
            }
            if let Some(n2) = p2.as_ref() {
                val += n2.val;
                p2 = &n2.next;
            }

这里的模式匹配是否推断n1n2&TT在这里为Box<ListNode>,为何之后的代码的行为可以像ListNode一样,这里有些不懂,希望大神帮小白解答一下。

评论区

写评论
Aya0wind 2021-05-04 14:35

这样吗,还真没注意到,竟然不是实现的Asref trait。是因为其作用跟Asref trait取引用的原意不一样才选择作为成员函数的?

--
👇
Bai-Jinlin: 不过有一说一Option的as_ref不是Option实现了AsRef trait,而是那个就是一个Option的普通方法,Pin里的as_ref也是一个意思。

--
👇
Orca-bit: 十分感谢

👇
Aya0wind: 如果你是问as_ref为什么是把&Option变成Option<&T>,这个其实就是Option实现的的Asref trait干的事,没啥原因,他就是这么做的,就是一个转换,让你能方便的取到Option内部T的引用。 然后就是你问的Box为什么可以像T那样使用,因为Box实现了一个trait叫Deref,Rust有一个特性叫auto deref,就是可以把实现了Deref的类型当成deref之后返回的类型来用,而Box的Deref是这么实现的:

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self
    }
}

即返回一个&T,而T就是Box指向的那个类型,这样就可以像使用对象本身一样来使用Box包装之后的对象了。

例如后面的

val += n1.val;

其实应该是

val += n1.deref().val;

只不过编译器自动做了这件事。
类似的还有个trait叫DerefMut,看名字你应该能猜到它是干啥用的。

Bai-Jinlin 2021-05-04 13:51

不过有一说一Option的as_ref不是Option实现了AsRef trait,而是那个就是一个Option的普通方法,Pin里的as_ref也是一个意思。

--
👇
Orca-bit: 十分感谢

👇
Aya0wind: 如果你是问as_ref为什么是把&Option变成Option<&T>,这个其实就是Option实现的的Asref trait干的事,没啥原因,他就是这么做的,就是一个转换,让你能方便的取到Option内部T的引用。 然后就是你问的Box为什么可以像T那样使用,因为Box实现了一个trait叫Deref,Rust有一个特性叫auto deref,就是可以把实现了Deref的类型当成deref之后返回的类型来用,而Box的Deref是这么实现的:

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self
    }
}

即返回一个&T,而T就是Box指向的那个类型,这样就可以像使用对象本身一样来使用Box包装之后的对象了。

例如后面的

val += n1.val;

其实应该是

val += n1.deref().val;

只不过编译器自动做了这件事。
类似的还有个trait叫DerefMut,看名字你应该能猜到它是干啥用的。

作者 Orca-bit 2021-05-04 13:28

十分感谢

👇
Aya0wind: 如果你是问as_ref为什么是把&Option变成Option<&T>,这个其实就是Option实现的的Asref trait干的事,没啥原因,他就是这么做的,就是一个转换,让你能方便的取到Option内部T的引用。 然后就是你问的Box为什么可以像T那样使用,因为Box实现了一个trait叫Deref,Rust有一个特性叫auto deref,就是可以把实现了Deref的类型当成deref之后返回的类型来用,而Box的Deref是这么实现的:

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self
    }
}

即返回一个&T,而T就是Box指向的那个类型,这样就可以像使用对象本身一样来使用Box包装之后的对象了。

例如后面的

val += n1.val;

其实应该是

val += n1.deref().val;

只不过编译器自动做了这件事。
类似的还有个trait叫DerefMut,看名字你应该能猜到它是干啥用的。

Aya0wind 2021-05-04 12:08

如果你是问as_ref为什么是把&Option变成Option<&T>,这个其实就是Option实现的的Asref trait干的事,没啥原因,他就是这么做的,就是一个转换,让你能方便的取到Option内部T的引用。 然后就是你问的Box为什么可以像T那样使用,因为Box实现了一个trait叫Deref,Rust有一个特性叫auto deref,就是可以把实现了Deref的类型当成deref之后返回的类型来用,而Box的Deref是这么实现的:

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, A: Allocator> Deref for Box<T, A> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self
    }
}

即返回一个&T,而T就是Box指向的那个类型,这样就可以像使用对象本身一样来使用Box包装之后的对象了。

例如后面的

val += n1.val;

其实应该是

val += n1.deref().val;

只不过编译器自动做了这件事。
类似的还有个trait叫DerefMut,看名字你应该能猜到它是干啥用的。

1 共 4 条评论, 1 页